OSDN Git Service

* include/posix.h (_POSIX_PII_SOCKET): Define.
[pf3gnuchains/gcc-fork.git] / libjava / java / net / natPlainDatagramSocketImpl.cc
1 /* Copyright (C) 1999, 2000  Free Software Foundation
2
3    This file is part of libgcj.
4
5 This software is copyrighted work licensed under the terms of the
6 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
7 details.  */
8
9 #include <config.h>
10
11 #ifdef USE_WINSOCK
12 #include <windows.h>
13 #include <winsock.h>
14 #include <errno.h>
15 #include <string.h>
16 #ifndef ENOPROTOOPT
17 #define ENOPROTOOPT 109
18 #endif
19 #else /* USE_WINSOCK */
20 #include "posix.h"
21 #ifdef HAVE_SYS_SOCKET_H
22 #include <sys/socket.h>
23 #endif
24 #ifdef HAVE_NETINET_IN_H
25 #include <netinet/in.h>
26 #endif
27 #ifdef HAVE_ARPA_INET_H
28 #include <arpa/inet.h>
29 #endif
30 #include <errno.h>
31 #include <string.h>
32 #endif /* USE_WINSOCK */
33
34 #if HAVE_BSTRING_H
35 // Needed for bzero, implicitly used by FD_ZERO on IRIX 5.2 
36 #include <bstring.h>
37 #endif
38
39 // Avoid macro definitions of bind from system headers, e.g. on
40 // Solaris 7 with _XOPEN_SOURCE.  FIXME
41 static inline int
42 _Jv_bind (int fd, struct sockaddr *addr, int addrlen)
43 {
44   return ::bind (fd, addr, addrlen);
45 }
46
47 #ifdef bind
48 #undef bind
49 #endif
50
51 #include <gcj/cni.h>
52 #include <java/io/IOException.h>
53 #include <java/io/FileDescriptor.h>
54 #include <java/io/InterruptedIOException.h>
55 #include <java/net/BindException.h>
56 #include <java/net/SocketException.h>
57 #include <java/net/PlainDatagramSocketImpl.h>
58 #include <java/net/InetAddress.h>
59 #include <java/net/DatagramPacket.h>
60 #include <java/lang/InternalError.h>
61 #include <java/lang/Object.h>
62 #include <java/lang/Boolean.h>
63 #include <java/lang/Integer.h>
64
65 // FIXME: remove these
66 #define BooleanClass java::lang::Boolean::class$
67 #define IntegerClass java::lang::Integer::class$
68
69 #ifdef DISABLE_JAVA_NET
70
71 void
72 java::net::PlainDatagramSocketImpl::create ()
73 {
74   throw new SocketException (
75     JvNewStringLatin1 ("DatagramSocketImpl.create: unimplemented"));
76 }
77
78 void
79 java::net::PlainDatagramSocketImpl::bind (jint, java::net::InetAddress *)
80 {
81   throw new BindException (
82     JvNewStringLatin1 ("DatagramSocketImpl.bind: unimplemented"));
83 }
84
85 jint
86 java::net::PlainDatagramSocketImpl::peek (java::net::InetAddress *)
87 {
88   throw new java::io::IOException (
89     JvNewStringLatin1 ("DatagramSocketImpl.peek: unimplemented"));
90 }
91
92 void
93 java::net::PlainDatagramSocketImpl::send (java::net::DatagramPacket *)
94 {
95   throw new java::io::IOException (
96     JvNewStringLatin1 ("DatagramSocketImpl.send: unimplemented"));
97 }
98
99 void
100 java::net::PlainDatagramSocketImpl::receive (java::net::DatagramPacket *)
101 {
102   throw new java::io::IOException (
103     JvNewStringLatin1 ("DatagramSocketImpl.receive: unimplemented"));
104 }
105
106 void
107 java::net::PlainDatagramSocketImpl::setTimeToLive (jint)
108 {
109   throw new java::io::IOException (
110     JvNewStringLatin1 ("DatagramSocketImpl.setTimeToLive: unimplemented"));
111 }
112
113 jint
114 java::net::PlainDatagramSocketImpl::getTimeToLive ()
115 {
116   throw new java::io::IOException (
117     JvNewStringLatin1 ("DatagramSocketImpl.getTimeToLive: unimplemented"));
118 }
119
120 void
121 java::net::PlainDatagramSocketImpl::mcastGrp (java::net::InetAddress *,
122                                               jboolean)
123 {
124   throw new java::io::IOException (
125     JvNewStringLatin1 ("DatagramSocketImpl.mcastGrp: unimplemented"));
126 }
127
128 void
129 java::net::PlainDatagramSocketImpl::setOption (jint, java::lang::Object *)
130 {
131   throw new SocketException (
132     JvNewStringLatin1 ("DatagramSocketImpl.setOption: unimplemented"));
133 }
134
135 java::lang::Object *
136 java::net::PlainDatagramSocketImpl::getOption (jint)
137 {
138   throw new SocketException (
139     JvNewStringLatin1 ("DatagramSocketImpl.getOption: unimplemented"));
140 }
141
142 #else /* DISABLE_JAVA_NET */
143
144 #ifndef HAVE_SOCKLEN_T
145 typedef int socklen_t;
146 #endif
147
148 union SockAddr
149 {
150   struct sockaddr_in address;
151 #ifdef HAVE_INET6
152   struct sockaddr_in6 address6;
153 #endif
154 };
155
156 union McastReq
157 {
158 #if HAVE_STRUCT_IP_MREQ
159   struct ip_mreq mreq;
160 #endif
161 #if HAVE_STRUCT_IPV6_MREQ
162   struct ipv6_mreq mreq6;
163 #endif
164 };
165
166 union InAddr
167 {
168   struct in_addr addr;
169 #ifdef HAVE_INET6
170   struct in6_addr addr6;
171 #endif
172 };
173
174
175 // FIXME: routines here and/or in natPlainSocketImpl.cc could throw
176 // NoRouteToHostException; also consider UnknownHostException, ConnectException.
177
178 void
179 java::net::PlainDatagramSocketImpl::create ()
180 {
181   int sock = ::socket (AF_INET, SOCK_DGRAM, 0);
182   if (sock < 0)
183     {
184       char* strerr = strerror (errno);
185       throw new java::net::SocketException (JvNewStringUTF (strerr));
186     }
187   fnum = sock;
188   fd = new java::io::FileDescriptor (sock);
189 }
190
191 void
192 java::net::PlainDatagramSocketImpl::bind (jint lport,
193                                           java::net::InetAddress *host)
194 {
195   // FIXME: prob. need to do a setsockopt with SO_BROADCAST to allow multicast.
196   union SockAddr u;
197   struct sockaddr *ptr = (struct sockaddr *) &u.address;
198   // FIXME: Use getaddrinfo() to get actual protocol instead of assuming ipv4.
199   jbyteArray haddress = host->addr;
200   jbyte *bytes = elements (haddress);
201   int len = haddress->length;
202
203   if (len == 4)
204     {
205       u.address.sin_family = AF_INET;
206       if (host != NULL)
207         memcpy (&u.address.sin_addr, bytes, len);
208       else
209         u.address.sin_addr.s_addr = htonl (INADDR_ANY);
210       len = sizeof (struct sockaddr_in);
211       u.address.sin_port = htons (lport);
212     }
213 #ifdef HAVE_INET6
214   else if (len == 16)
215     {
216       u.address6.sin6_family = AF_INET6;
217       memcpy (&u.address6.sin6_addr, bytes, len);
218       len = sizeof (struct sockaddr_in6);
219       u.address6.sin6_port = htons (lport);
220     }
221 #endif
222   else
223     throw new java::net::SocketException (JvNewStringUTF ("invalid length"));
224
225   if (_Jv_bind (fnum, ptr, len) == 0)
226     {
227       socklen_t addrlen = sizeof(u);
228       if (lport != 0)
229         localPort = lport;
230       else if (::getsockname (fnum, (sockaddr*) &u, &addrlen) == 0)
231         localPort = ntohs (u.address.sin_port);
232       else
233         goto error;
234       return;
235     }
236  error:
237   char* strerr = strerror (errno);
238   throw new java::net::BindException (JvNewStringUTF (strerr));
239 }
240
241 jint
242 java::net::PlainDatagramSocketImpl::peek (java::net::InetAddress *i)
243 {
244   // FIXME: Deal with Multicast and if the socket is connected.
245   union SockAddr u;
246   socklen_t addrlen = sizeof(u);
247   ssize_t retlen =
248     ::recvfrom (fnum, (char *) NULL, 0, MSG_PEEK, (sockaddr*) &u,
249       &addrlen);
250   if (retlen < 0)
251     goto error;
252   // FIXME: Deal with Multicast addressing and if the socket is connected.
253   jbyteArray raddr;
254   jint rport;
255   if (u.address.sin_family == AF_INET)
256     {
257       raddr = JvNewByteArray (4);
258       memcpy (elements (raddr), &u.address.sin_addr, 4);
259       rport = ntohs (u.address.sin_port);
260     }
261 #ifdef HAVE_INET6
262   else if (u.address.sin_family == AF_INET6)
263     {
264       raddr = JvNewByteArray (16);
265       memcpy (elements (raddr), &u.address6.sin6_addr, 16);
266       rport = ntohs (u.address6.sin6_port);
267     }
268 #endif
269   else
270     throw new java::net::SocketException (JvNewStringUTF ("invalid family"));
271
272   i->addr = raddr;
273   return rport;
274  error:
275   char* strerr = strerror (errno);
276   throw new java::io::IOException (JvNewStringUTF (strerr));
277 }
278
279 void
280 java::net::PlainDatagramSocketImpl::send (java::net::DatagramPacket *p)
281 {
282   // FIXME: Deal with Multicast and if the socket is connected.
283   jint rport = p->getPort();
284   union SockAddr u;
285   jbyteArray haddress = p->getAddress()->addr;
286   jbyte *bytes = elements (haddress);
287   int len = haddress->length;
288   struct sockaddr *ptr = (struct sockaddr *) &u.address;
289   jbyte *dbytes = elements (p->getData());
290   if (len == 4)
291     {
292       u.address.sin_family = AF_INET;
293       memcpy (&u.address.sin_addr, bytes, len);
294       len = sizeof (struct sockaddr_in);
295       u.address.sin_port = htons (rport);
296     }
297 #ifdef HAVE_INET6
298   else if (len == 16)
299     {
300       u.address6.sin6_family = AF_INET6;
301       memcpy (&u.address6.sin6_addr, bytes, len);
302       len = sizeof (struct sockaddr_in6);
303       u.address6.sin6_port = htons (rport);
304     }
305 #endif
306   else
307     throw new java::net::SocketException (JvNewStringUTF ("invalid length"));
308
309   if (::sendto (fnum, (char *) dbytes, p->getLength(), 0, ptr, len) >= 0)
310     return;
311
312   char* strerr = strerror (errno);
313   throw new java::io::IOException (JvNewStringUTF (strerr));
314 }
315
316 void
317 java::net::PlainDatagramSocketImpl::receive (java::net::DatagramPacket *p)
318 {
319   // FIXME: Deal with Multicast and if the socket is connected.
320   union SockAddr u;
321   socklen_t addrlen = sizeof(u);
322   jbyte *dbytes = elements (p->getData());
323   ssize_t retlen = 0;
324
325   // Do timeouts via select since SO_RCVTIMEO is not always available.
326   if (timeout > 0)
327     {
328       fd_set rset;
329       struct timeval tv;
330       FD_ZERO(&rset);
331       FD_SET(fnum, &rset);
332       tv.tv_sec = timeout / 1000;
333       tv.tv_usec = (timeout % 1000) * 1000;
334       int retval;
335       if ((retval = _Jv_select (fnum + 1, &rset, NULL, NULL, &tv)) < 0)
336         goto error;
337       else if (retval == 0)
338         throw new java::io::InterruptedIOException ();
339     }
340
341   retlen =
342     ::recvfrom (fnum, (char *) dbytes, p->getLength(), 0, (sockaddr*) &u,
343       &addrlen);
344   if (retlen < 0)
345     goto error;
346   // FIXME: Deal with Multicast addressing and if the socket is connected.
347   jbyteArray raddr;
348   jint rport;
349   if (u.address.sin_family == AF_INET)
350     {
351       raddr = JvNewByteArray (4);
352       memcpy (elements (raddr), &u.address.sin_addr, 4);
353       rport = ntohs (u.address.sin_port);
354     }
355 #ifdef HAVE_INET6
356   else if (u.address.sin_family == AF_INET6)
357     {
358       raddr = JvNewByteArray (16);
359       memcpy (elements (raddr), &u.address6.sin6_addr, 16);
360       rport = ntohs (u.address6.sin6_port);
361     }
362 #endif
363   else
364     throw new java::net::SocketException (JvNewStringUTF ("invalid family"));
365
366   p->setAddress (new InetAddress (raddr, NULL));
367   p->setPort (rport);
368   p->setLength ((jint) retlen);
369   return;
370  error:
371   char* strerr = strerror (errno);
372   throw new java::io::IOException (JvNewStringUTF (strerr));
373 }
374
375 void
376 java::net::PlainDatagramSocketImpl::setTimeToLive (jint ttl)
377 {
378   // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4.
379   char val = (char) ttl;
380   socklen_t val_len = sizeof(val);
381   if (::setsockopt (fnum, IPPROTO_IP, IP_MULTICAST_TTL, &val, val_len) == 0)
382     return;
383
384   char* strerr = strerror (errno);
385   throw new java::io::IOException (JvNewStringUTF (strerr));
386 }
387
388 jint
389 java::net::PlainDatagramSocketImpl::getTimeToLive ()
390 {
391   // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4.
392   char val;
393   socklen_t val_len = sizeof(val);
394   if (::getsockopt (fnum, IPPROTO_IP, IP_MULTICAST_TTL, &val, &val_len) == 0)
395     return ((int) val) & 0xFF;
396
397   char* strerr = strerror (errno);
398   throw new java::io::IOException (JvNewStringUTF (strerr));
399 }
400
401 void
402 java::net::PlainDatagramSocketImpl::mcastGrp (java::net::InetAddress *inetaddr,
403                                               jboolean join)
404 {
405   union McastReq u;
406   jbyteArray haddress = inetaddr->addr;
407   jbyte *bytes = elements (haddress);
408   int len = haddress->length;
409   int level, opname;
410   const char *ptr;
411   if (0)
412     ;
413 #if HAVE_STRUCT_IP_MREQ
414   else if (len == 4)
415     {
416       level = IPPROTO_IP;
417       opname = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
418       memcpy (&u.mreq.imr_multiaddr, bytes, len);
419       // FIXME:  If a non-default interface is set, use it; see Stevens p. 501.
420       // Maybe not, see note in last paragraph at bottom of Stevens p. 497.
421       u.mreq.imr_interface.s_addr = htonl (INADDR_ANY); 
422       len = sizeof (struct ip_mreq);
423       ptr = (const char *) &u.mreq;
424     }
425 #endif
426 #if HAVE_STRUCT_IPV6_MREQ
427   else if (len == 16)
428     {
429       level = IPPROTO_IPV6;
430
431       /* Prefer new RFC 2553 names.  */
432 #ifndef IPV6_JOIN_GROUP
433 #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
434 #endif
435 #ifndef IPV6_LEAVE_GROUP
436 #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
437 #endif
438
439       opname = join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP;
440       memcpy (&u.mreq6.ipv6mr_multiaddr, bytes, len);
441       // FIXME:  If a non-default interface is set, use it; see Stevens p. 501.
442       // Maybe not, see note in last paragraph at bottom of Stevens p. 497.
443       u.mreq6.ipv6mr_interface = 0;
444       len = sizeof (struct ipv6_mreq);
445       ptr = (const char *) &u.mreq6;
446     }
447 #endif
448   else
449     throw new java::net::SocketException (JvNewStringUTF ("invalid length"));
450
451   if (::setsockopt (fnum, level, opname, ptr, len) == 0)
452     return;
453
454   char* strerr = strerror (errno);
455   throw new java::io::IOException (JvNewStringUTF (strerr));
456 }
457
458 void
459 java::net::PlainDatagramSocketImpl::setOption (jint optID,
460                                                java::lang::Object *value)
461 {
462   int val;
463   socklen_t val_len = sizeof (val);
464
465   if (_Jv_IsInstanceOf (value, &BooleanClass))
466     {
467       java::lang::Boolean *boolobj = 
468         static_cast<java::lang::Boolean *> (value);
469       val = boolobj->booleanValue() ? 1 : 0;
470     }
471   else if (_Jv_IsInstanceOf (value, &IntegerClass))
472     {
473       java::lang::Integer *intobj = 
474         static_cast<java::lang::Integer *> (value);          
475       val = (int) intobj->intValue();
476     }
477   // Else assume value to be an InetAddress for use with IP_MULTICAST_IF.
478
479   switch (optID) 
480     {
481       case _Jv_TCP_NODELAY_ :
482         throw new java::net::SocketException (
483           JvNewStringUTF ("TCP_NODELAY not valid for UDP"));
484         return;
485       case _Jv_SO_LINGER_ :
486         throw new java::net::SocketException (
487           JvNewStringUTF ("SO_LINGER not valid for UDP"));
488         return;
489       case _Jv_SO_SNDBUF_ :
490       case _Jv_SO_RCVBUF_ :
491 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
492         int opt;
493         optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
494         if (::setsockopt (fnum, SOL_SOCKET, opt, (char *) &val, val_len) != 0)
495           goto error;    
496 #else
497         throw new java::lang::InternalError (
498           JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
499 #endif 
500         return;
501       case _Jv_SO_REUSEADDR_ :
502 #if defined(SO_REUSEADDR)
503         if (::setsockopt (fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
504             val_len) != 0)
505           goto error;
506 #else
507         throw new java::lang::InternalError (
508           JvNewStringUTF ("SO_REUSEADDR not supported"));
509 #endif 
510         return;
511       case _Jv_SO_BINDADDR_ :
512         throw new java::net::SocketException (
513           JvNewStringUTF ("SO_BINDADDR: read only option"));
514         return;
515       case _Jv_IP_MULTICAST_IF_ :
516         union InAddr u;
517         jbyteArray haddress;
518         jbyte *bytes;
519         int len;
520         int level, opname;
521         const char *ptr;
522
523         haddress = ((java::net::InetAddress *) value)->addr;
524         bytes = elements (haddress);
525         len = haddress->length;
526         if (len == 4)
527           {
528             level = IPPROTO_IP;
529             opname = IP_MULTICAST_IF;
530             memcpy (&u.addr, bytes, len);
531             len = sizeof (struct in_addr);
532             ptr = (const char *) &u.addr;
533           }
534 // Tru64 UNIX V5.0 has struct sockaddr_in6, but no IPV6_MULTICAST_IF
535 #if defined (HAVE_INET6) && defined (IPV6_MULTICAST_IF)
536         else if (len == 16)
537           {
538             level = IPPROTO_IPV6;
539             opname = IPV6_MULTICAST_IF;
540             memcpy (&u.addr6, bytes, len);
541             len = sizeof (struct in6_addr);
542             ptr = (const char *) &u.addr6;
543           }
544 #endif
545         else
546           throw
547             new java::net::SocketException (JvNewStringUTF ("invalid length"));
548
549         if (::setsockopt (fnum, level, opname, ptr, len) != 0)
550           goto error;
551         return;
552       case _Jv_SO_TIMEOUT_ :
553         timeout = val;
554         return;
555       default :
556         errno = ENOPROTOOPT;
557     }
558
559  error:
560   char* strerr = strerror (errno);
561   throw new java::net::SocketException (JvNewStringUTF (strerr));
562 }
563
564 java::lang::Object *
565 java::net::PlainDatagramSocketImpl::getOption (jint optID)
566 {
567   int val;
568   socklen_t val_len = sizeof(val);
569   union SockAddr u;
570   socklen_t addrlen = sizeof(u);
571
572   switch (optID)
573     {
574       case _Jv_TCP_NODELAY_ :
575         throw new java::net::SocketException (
576           JvNewStringUTF ("TCP_NODELAY not valid for UDP"));
577         break;
578
579       case _Jv_SO_LINGER_ :
580         throw new java::net::SocketException (
581           JvNewStringUTF ("SO_LINGER not valid for UDP"));
582         break;    
583       case _Jv_SO_RCVBUF_ :
584       case _Jv_SO_SNDBUF_ :
585 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
586         int opt;
587         optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
588         if (::getsockopt (fnum, SOL_SOCKET, opt, (char *) &val, &val_len) != 0)
589           goto error;    
590         else
591           return new java::lang::Integer (val);
592 #else
593         throw new java::lang::InternalError (
594           JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
595 #endif    
596         break;
597       case _Jv_SO_BINDADDR_:
598         // cache the local address
599         if (localAddress == NULL)
600           {     
601             jbyteArray laddr;
602             if (::getsockname (fnum, (sockaddr*) &u, &addrlen) != 0)
603               goto error;
604             if (u.address.sin_family == AF_INET)
605               {
606                 laddr = JvNewByteArray (4);
607                 memcpy (elements (laddr), &u.address.sin_addr, 4);
608               }
609 #ifdef HAVE_INET6
610             else if (u.address.sin_family == AF_INET6)
611               {
612                 laddr = JvNewByteArray (16);
613                 memcpy (elements (laddr), &u.address6.sin6_addr, 16);
614               }
615 #endif
616             else
617               throw new java::net::SocketException (JvNewStringUTF ("invalid family"));
618             localAddress = new java::net::InetAddress (laddr, NULL);
619           }
620         return localAddress;  
621         break;
622       case _Jv_SO_REUSEADDR_ :
623 #if defined(SO_REUSEADDR)
624         if (::getsockopt (fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
625             &val_len) != 0)
626           goto error;
627         return new java::lang::Boolean (val != 0);
628 #else
629         throw new java::lang::InternalError (
630           JvNewStringUTF ("SO_REUSEADDR not supported"));
631 #endif 
632         break;
633       case _Jv_IP_MULTICAST_IF_ :
634 #ifdef HAVE_INET_NTOA
635         struct in_addr inaddr;
636         socklen_t inaddr_len;
637         char *bytes;
638
639         inaddr_len = sizeof(inaddr);
640         if (::getsockopt (fnum, IPPROTO_IP, IP_MULTICAST_IF, (char *) &inaddr,
641             &inaddr_len) != 0)
642           goto error;
643
644         bytes = inet_ntoa (inaddr);
645
646         return java::net::InetAddress::getByName (JvNewStringLatin1 (bytes));
647 #else
648         throw new java::net::SocketException (
649           JvNewStringUTF ("IP_MULTICAST_IF: not available - no inet_ntoa()"));
650 #endif
651         break;
652       case _Jv_SO_TIMEOUT_ :
653         return new java::lang::Integer (timeout);
654         break;
655       default :
656         errno = ENOPROTOOPT;
657     }
658
659  error:
660   char* strerr = strerror (errno);
661   throw new java::net::SocketException (JvNewStringUTF (strerr));
662 }
663
664 #endif /* DISABLE_JAVA_NET */