OSDN Git Service

PR libgcj/31228
[pf3gnuchains/gcc-fork.git] / libjava / gnu / java / net / natPlainDatagramSocketImplPosix.cc
1 /* Copyright (C) 2003, 2005, 2006  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 #include <platform.h>
11
12 #ifdef HAVE_NETINET_IN_H
13 #include <netinet/in.h>
14 #endif
15 #ifdef HAVE_ARPA_INET_H
16 #include <arpa/inet.h>
17 #endif
18 #include <errno.h>
19 #include <string.h>
20
21 #if HAVE_BSTRING_H
22 // Needed for bzero, implicitly used by FD_ZERO on IRIX 5.2 
23 #include <bstring.h>
24 #endif
25
26 #include <gcj/cni.h>
27 #include <gnu/java/net/PlainDatagramSocketImpl.h>
28 #include <java/io/IOException.h>
29 #include <java/io/InterruptedIOException.h>
30 #include <java/net/BindException.h>
31 #include <java/net/SocketException.h>
32 #include <java/net/SocketTimeoutException.h>
33 #include <java/net/InetAddress.h>
34 #include <java/net/NetworkInterface.h>
35 #include <java/net/DatagramPacket.h>
36 #include <java/net/PortUnreachableException.h>
37 #include <java/lang/InternalError.h>
38 #include <java/lang/Object.h>
39 #include <java/lang/Boolean.h>
40 #include <java/lang/Integer.h>
41 #include <java/net/UnknownHostException.h>
42 #include <java/net/ConnectException.h>
43 #include <java/lang/NullPointerException.h>
44
45 union SockAddr
46 {
47   struct sockaddr_in address;
48 #ifdef HAVE_INET6
49   struct sockaddr_in6 address6;
50 #endif
51 };
52
53 union McastReq
54 {
55 #if HAVE_STRUCT_IP_MREQ
56   struct ip_mreq mreq;
57 #endif
58 #if HAVE_STRUCT_IPV6_MREQ
59   struct ipv6_mreq mreq6;
60 #endif
61 };
62
63 union InAddr
64 {
65   struct in_addr addr;
66 #ifdef HAVE_INET6
67   struct in6_addr addr6;
68 #endif
69 };
70
71
72 // FIXME: routines here and/or in natPlainSocketImpl.cc could throw
73 // NoRouteToHostException; also consider UnknownHostException, ConnectException.
74
75 void
76 gnu::java::net::PlainDatagramSocketImpl::create ()
77 {
78   int sock = _Jv_socket (AF_INET, SOCK_DGRAM, 0);
79
80   if (sock < 0)
81     {
82       char* strerr = strerror (errno);
83       throw new ::java::net::SocketException (JvNewStringUTF (strerr));
84     }
85
86   // We use native_fd in place of fd here.  From leaving fd null we avoid
87   // the double close problem in FileDescriptor.finalize.
88   native_fd = sock;
89 }
90
91 void
92 gnu::java::net::PlainDatagramSocketImpl::bind (jint lport,
93                                                ::java::net::InetAddress *host)
94 {
95   union SockAddr u;
96   struct sockaddr *ptr = (struct sockaddr *) &u.address;
97   // FIXME: Use getaddrinfo() to get actual protocol instead of assuming ipv4.
98   jbyteArray haddress = host->addr;
99   jbyte *bytes = elements (haddress);
100   int len = haddress->length;
101
102   if (len == 4)
103     {
104       u.address.sin_family = AF_INET;
105
106       if (host != NULL)
107         memcpy (&u.address.sin_addr, bytes, len);
108       else
109         u.address.sin_addr.s_addr = htonl (INADDR_ANY);
110
111       len = sizeof (struct sockaddr_in);
112       u.address.sin_port = htons (lport);
113     }
114 #ifdef HAVE_INET6
115   else if (len == 16)
116     {
117       u.address6.sin6_family = AF_INET6;
118       memcpy (&u.address6.sin6_addr, bytes, len);
119       len = sizeof (struct sockaddr_in6);
120       u.address6.sin6_port = htons (lport);
121     }
122 #endif
123   else
124     throw new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
125
126   if (_Jv_bind (native_fd, ptr, len) == 0)
127     {
128       socklen_t addrlen = sizeof(u);
129
130       if (lport != 0)
131         localPort = lport;
132       else if (::getsockname (native_fd, (sockaddr*) &u, &addrlen) == 0)
133         localPort = ntohs (u.address.sin_port);
134       else
135         goto error;
136
137       /* Allow broadcast by default. */
138       int broadcast = 1;
139       if (::setsockopt (native_fd, SOL_SOCKET, SO_BROADCAST, (char *) &broadcast, 
140                         sizeof (broadcast)) != 0)
141         goto error;
142
143       return;
144     }
145
146  error:
147   char* strerr = strerror (errno);
148   throw new ::java::net::BindException (JvNewStringUTF (strerr));
149 }
150
151 void
152 gnu::java::net::PlainDatagramSocketImpl::connect (::java::net::InetAddress *host,
153                                                   jint rport)
154
155   if (! host)
156     throw new ::java::lang::NullPointerException;
157
158   union SockAddr u;
159   jbyteArray haddress = host->addr;
160   jbyte *bytes = elements (haddress);
161   int len = haddress->length;
162   struct sockaddr *ptr = (struct sockaddr *) &u.address;
163   if (len == 4)
164     {
165       u.address.sin_family = AF_INET;
166       memcpy (&u.address.sin_addr, bytes, len);
167       len = sizeof (struct sockaddr_in);
168       u.address.sin_port = htons (rport);
169     }
170 #ifdef HAVE_INET6
171   else if (len == 16)
172     {
173       u.address6.sin6_family = AF_INET6;
174       memcpy (&u.address6.sin6_addr, bytes, len);
175       len = sizeof (struct sockaddr_in6);
176       u.address6.sin6_port = htons (rport);
177     }
178 #endif
179   else
180     throw new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
181   
182   if (_Jv_connect (native_fd, ptr, len) == 0)
183     return;
184   char* strerr = strerror (errno);
185   throw new ::java::net::ConnectException (JvNewStringUTF (strerr));
186 }
187
188 void
189 gnu::java::net::PlainDatagramSocketImpl::disconnect ()
190 {
191   struct sockaddr addr;
192   addr.sa_family = AF_UNSPEC;
193   // Ignore errors.  This is lame but apparently required.
194   _Jv_connect (native_fd, &addr, sizeof (addr));
195 }
196
197 jint
198 gnu::java::net::PlainDatagramSocketImpl::peek (::java::net::InetAddress *i)
199 {
200   // FIXME: Deal with Multicast and if the socket is connected.
201   union SockAddr u;
202   socklen_t addrlen = sizeof(u);
203   ssize_t retlen =
204     ::recvfrom (native_fd, (char *) NULL, 0, MSG_PEEK, (sockaddr*) &u,
205       &addrlen);
206   if (retlen < 0)
207     goto error;
208   // FIXME: Deal with Multicast addressing and if the socket is connected.
209   jbyteArray raddr;
210   jint rport;
211   if (u.address.sin_family == AF_INET)
212     {
213       raddr = JvNewByteArray (4);
214       memcpy (elements (raddr), &u.address.sin_addr, 4);
215       rport = ntohs (u.address.sin_port);
216     }
217 #ifdef HAVE_INET6
218   else if (u.address.sin_family == AF_INET6)
219     {
220       raddr = JvNewByteArray (16);
221       memcpy (elements (raddr), &u.address6.sin6_addr, 16);
222       rport = ntohs (u.address6.sin6_port);
223     }
224 #endif
225   else
226     throw new ::java::net::SocketException (JvNewStringUTF ("invalid family"));
227
228   i->addr = raddr;
229   return rport;
230  error:
231   char* strerr = strerror (errno);
232
233   if (errno == ECONNREFUSED)
234     throw new ::java::net::PortUnreachableException (JvNewStringUTF (strerr));
235
236   throw new ::java::io::IOException (JvNewStringUTF (strerr));
237 }
238
239 jint
240 gnu::java::net::PlainDatagramSocketImpl::peekData (::java::net::DatagramPacket *p)
241 {
242   // FIXME: Deal with Multicast and if the socket is connected.
243   union SockAddr u;
244   socklen_t addrlen = sizeof(u);
245   jbyte *dbytes = elements (p->getData()) + p->getOffset();
246   jint maxlen = p->maxlen - p->getOffset();
247   ssize_t retlen = 0;
248
249   // Do timeouts via select since SO_RCVTIMEO is not always available.
250   if (timeout > 0 && native_fd >= 0 && native_fd < FD_SETSIZE)
251     {
252       fd_set rset;
253       struct timeval tv;
254       FD_ZERO(&rset);
255       FD_SET(native_fd, &rset);
256       tv.tv_sec = timeout / 1000;
257       tv.tv_usec = (timeout % 1000) * 1000;
258       int retval;
259       if ((retval = _Jv_select (native_fd + 1, &rset, NULL, NULL, &tv)) < 0)
260         goto error;
261       else if (retval == 0)
262         throw new ::java::net::SocketTimeoutException
263           (JvNewStringUTF ("PeekData timed out") );
264     }
265
266   retlen =
267     ::recvfrom (native_fd, (char *) dbytes, maxlen, MSG_PEEK, (sockaddr*) &u,
268       &addrlen);
269   if (retlen < 0)
270     goto error;
271   // FIXME: Deal with Multicast addressing and if the socket is connected.
272   jbyteArray raddr;
273   jint rport;
274   if (u.address.sin_family == AF_INET)
275     {
276       raddr = JvNewByteArray (4);
277       memcpy (elements (raddr), &u.address.sin_addr, 4);
278       rport = ntohs (u.address.sin_port);
279     }
280 #ifdef HAVE_INET6
281   else if (u.address.sin_family == AF_INET6)
282     {
283       raddr = JvNewByteArray (16);
284       memcpy (elements (raddr), &u.address6.sin6_addr, 16);
285       rport = ntohs (u.address6.sin6_port);
286     }
287 #endif
288   else
289     throw new ::java::net::SocketException (JvNewStringUTF ("invalid family"));
290
291   p->setAddress (::java::net::InetAddress::getByAddress (raddr));
292   p->setPort (rport);
293   p->length = (int) retlen;
294   return rport;
295
296  error:
297   char* strerr = strerror (errno);
298
299   if (errno == ECONNREFUSED)
300     throw new ::java::net::PortUnreachableException (JvNewStringUTF (strerr));
301
302   throw new ::java::io::IOException (JvNewStringUTF (strerr));
303 }
304
305 // Close(shutdown) the socket.
306 void
307 gnu::java::net::PlainDatagramSocketImpl::close ()
308 {
309   // Avoid races from asynchronous finalization.
310   JvSynchronize sync (this);
311
312   // The method isn't declared to throw anything, so we disregard
313   // the return value.
314   _Jv_close (native_fd);
315   native_fd = -1;
316   timeout = 0;
317 }
318
319 void
320 gnu::java::net::PlainDatagramSocketImpl::send (::java::net::DatagramPacket *p)
321 {
322   JvSynchronize lock (SEND_LOCK);
323   
324   // FIXME: Deal with Multicast.
325
326   ::java::net::InetAddress *host = p->getAddress();
327   if (host == NULL)
328     {
329       // If there is no host, maybe this socket was connected, in
330       // which case we try a plain send().
331       jbyte *dbytes = elements (p->getData()) + p->getOffset();
332       if (::send (native_fd, (char *) dbytes, p->getLength(), 0) >= 0)
333         return;
334     }
335   else
336     {
337       jint rport = p->getPort();
338       union SockAddr u;
339
340       jbyteArray haddress = host->addr;
341       jbyte *bytes = elements (haddress);
342       int len = haddress->length;
343       struct sockaddr *ptr = (struct sockaddr *) &u.address;
344       jbyte *dbytes = elements (p->getData()) + p->getOffset();
345       if (len == 4)
346         {
347           u.address.sin_family = AF_INET;
348           memcpy (&u.address.sin_addr, bytes, len);
349           len = sizeof (struct sockaddr_in);
350           u.address.sin_port = htons (rport);
351         }
352 #ifdef HAVE_INET6
353       else if (len == 16)
354         {
355           u.address6.sin6_family = AF_INET6;
356           memcpy (&u.address6.sin6_addr, bytes, len);
357           len = sizeof (struct sockaddr_in6);
358           u.address6.sin6_port = htons (rport);
359         }
360 #endif
361       else
362         throw new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
363
364       if (::sendto (native_fd, (char *) dbytes, p->getLength(), 0, ptr, len)
365           >= 0)
366         return;
367     }
368
369   char* strerr = strerror (errno);
370
371   if (errno == ECONNREFUSED)
372     throw new ::java::net::PortUnreachableException (JvNewStringUTF (strerr));
373
374   throw new ::java::io::IOException (JvNewStringUTF (strerr));
375 }
376
377 void
378 gnu::java::net::PlainDatagramSocketImpl::receive (::java::net::DatagramPacket *p)
379 {
380   JvSynchronize lock (RECEIVE_LOCK);
381
382   // FIXME: Deal with Multicast and if the socket is connected.
383   union SockAddr u;
384   socklen_t addrlen = sizeof(u);
385   jbyte *dbytes = elements (p->getData()) + p->getOffset();
386   jint maxlen = p->maxlen - p->getOffset();
387   ssize_t retlen = 0;
388
389   // Do timeouts via select since SO_RCVTIMEO is not always available.
390   if (timeout > 0 && native_fd >= 0 && native_fd < FD_SETSIZE)
391     {
392       fd_set rset;
393       struct timeval tv;
394       FD_ZERO(&rset);
395       FD_SET(native_fd, &rset);
396       tv.tv_sec = timeout / 1000;
397       tv.tv_usec = (timeout % 1000) * 1000;
398       int retval;
399       if ((retval = _Jv_select (native_fd + 1, &rset, NULL, NULL, &tv)) < 0)
400         goto error;
401       else if (retval == 0)
402         throw new ::java::net::SocketTimeoutException
403           (JvNewStringUTF ("Receive timed out") );
404     }
405
406   retlen =
407     ::recvfrom (native_fd, (char *) dbytes, maxlen, 0, (sockaddr*) &u,
408       &addrlen);
409   if (retlen < 0)
410     goto error;
411   // FIXME: Deal with Multicast addressing and if the socket is connected.
412   jbyteArray raddr;
413   jint rport;
414   if (u.address.sin_family == AF_INET)
415     {
416       raddr = JvNewByteArray (4);
417       memcpy (elements (raddr), &u.address.sin_addr, 4);
418       rport = ntohs (u.address.sin_port);
419     }
420 #ifdef HAVE_INET6
421   else if (u.address.sin_family == AF_INET6)
422     {
423       raddr = JvNewByteArray (16);
424       memcpy (elements (raddr), &u.address6.sin6_addr, 16);
425       rport = ntohs (u.address6.sin6_port);
426     }
427 #endif
428   else
429     throw new ::java::net::SocketException (JvNewStringUTF ("invalid family"));
430
431   p->setAddress (::java::net::InetAddress::getByAddress (raddr));
432   p->setPort (rport);
433   p->length = (jint) retlen;
434   return;
435
436  error:
437   char* strerr = strerror (errno);
438
439   if (errno == ECONNREFUSED)
440     throw new ::java::net::PortUnreachableException (JvNewStringUTF (strerr));
441
442   throw new ::java::io::IOException (JvNewStringUTF (strerr));
443 }
444
445 void
446 gnu::java::net::PlainDatagramSocketImpl::setTimeToLive (jint ttl)
447 {
448   // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4.
449   char val = (char) ttl;
450   socklen_t val_len = sizeof(val);
451
452   if (::setsockopt (native_fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, val_len) == 0)
453     return;
454
455   char* strerr = strerror (errno);
456   throw new ::java::io::IOException (JvNewStringUTF (strerr));
457 }
458
459 jint
460 gnu::java::net::PlainDatagramSocketImpl::getTimeToLive ()
461 {
462   // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4.
463   char val;
464   socklen_t val_len = sizeof(val);
465
466   if (::getsockopt (native_fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, &val_len) == 0)
467     return ((int) val) & 0xFF;
468
469   char* strerr = strerror (errno);
470   throw new ::java::io::IOException (JvNewStringUTF (strerr));
471 }
472
473 void
474 gnu::java::net::PlainDatagramSocketImpl::mcastGrp (::java::net::InetAddress *inetaddr,
475                                                    ::java::net::NetworkInterface *,
476                                                    jboolean join)
477 {
478   // FIXME: implement use of NetworkInterface
479
480   jbyteArray haddress = inetaddr->addr;
481 #if HAVE_STRUCT_IP_MREQ || HAVE_STRUCT_IPV6_MREQ
482   union McastReq u;
483   jbyte *bytes = elements (haddress);
484 #endif
485
486   int len = haddress->length;
487   int level, opname;
488   const char *ptr;
489   if (0)
490     ;
491 #if HAVE_STRUCT_IP_MREQ
492   else if (len == 4)
493     {
494       level = IPPROTO_IP;
495       opname = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
496       memcpy (&u.mreq.imr_multiaddr, bytes, len);
497       // FIXME:  If a non-default interface is set, use it; see Stevens p. 501.
498       // Maybe not, see note in last paragraph at bottom of Stevens p. 497.
499       u.mreq.imr_interface.s_addr = htonl (INADDR_ANY); 
500       len = sizeof (struct ip_mreq);
501       ptr = (const char *) &u.mreq;
502     }
503 #endif
504 #if HAVE_STRUCT_IPV6_MREQ
505   else if (len == 16)
506     {
507       level = IPPROTO_IPV6;
508
509       /* Prefer new RFC 2553 names.  */
510 #ifndef IPV6_JOIN_GROUP
511 #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
512 #endif
513 #ifndef IPV6_LEAVE_GROUP
514 #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
515 #endif
516
517       opname = join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP;
518       memcpy (&u.mreq6.ipv6mr_multiaddr, bytes, len);
519       // FIXME:  If a non-default interface is set, use it; see Stevens p. 501.
520       // Maybe not, see note in last paragraph at bottom of Stevens p. 497.
521       u.mreq6.ipv6mr_interface = 0;
522       len = sizeof (struct ipv6_mreq);
523       ptr = (const char *) &u.mreq6;
524     }
525 #endif
526   else
527     throw new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
528
529   if (::setsockopt (native_fd, level, opname, ptr, len) == 0)
530     return;
531
532   char* strerr = strerror (errno);
533   throw new ::java::io::IOException (JvNewStringUTF (strerr));
534 }
535
536 // Helper function to get the InetAddress for a given socket (file
537 // descriptor).
538 static ::java::net::InetAddress *
539 getLocalAddress (int native_fd)
540 {
541   jbyteArray laddr;
542   union SockAddr u;
543   socklen_t addrlen = sizeof(u);
544
545   if (::getsockname (native_fd, (sockaddr*) &u, &addrlen) != 0)
546     {
547       char* strerr = strerror (errno);
548       throw new ::java::net::SocketException (JvNewStringUTF (strerr));
549     }
550   if (u.address.sin_family == AF_INET)
551     {
552       laddr = JvNewByteArray (4);
553       memcpy (elements (laddr), &u.address.sin_addr, 4);
554     }
555 #ifdef HAVE_INET6
556   else if (u.address.sin_family == AF_INET6)
557     {
558       laddr = JvNewByteArray (16);
559       memcpy (elements (laddr), &u.address6.sin6_addr, 16);
560     }
561 #endif
562   else
563     throw new ::java::net::SocketException (JvNewStringUTF ("invalid family"));
564
565   return ::java::net::InetAddress::getByAddress (laddr);
566 }
567
568 void
569 gnu::java::net::PlainDatagramSocketImpl::setOption (jint optID,
570                                                     ::java::lang::Object *value)
571 {
572   int val;
573   socklen_t val_len = sizeof (val);
574
575   if (native_fd < 0)
576     throw new ::java::net::SocketException (JvNewStringUTF ("Socket closed"));
577
578   if (_Jv_IsInstanceOf (value, &::java::lang::Boolean::class$))
579     {
580       ::java::lang::Boolean *boolobj = 
581         static_cast< ::java::lang::Boolean *> (value);
582       val = boolobj->booleanValue() ? 1 : 0;
583     }
584   else if (_Jv_IsInstanceOf (value, &::java::lang::Integer::class$))
585     {
586       ::java::lang::Integer *intobj = 
587         static_cast< ::java::lang::Integer *> (value);          
588       val = (int) intobj->intValue();
589     }
590   // Else assume value to be an InetAddress for use with IP_MULTICAST_IF.
591
592   switch (optID) 
593     {
594       case _Jv_TCP_NODELAY_ :
595         throw new ::java::net::SocketException (
596           JvNewStringUTF ("TCP_NODELAY not valid for UDP"));
597         return;
598       case _Jv_SO_LINGER_ :
599         throw new ::java::net::SocketException (
600           JvNewStringUTF ("SO_LINGER not valid for UDP"));
601         return;
602       case _Jv_SO_KEEPALIVE_ :
603         throw new ::java::net::SocketException (
604           JvNewStringUTF ("SO_KEEPALIVE not valid for UDP"));
605         return;
606
607       case _Jv_SO_BROADCAST_ :
608         if (::setsockopt (native_fd, SOL_SOCKET, SO_BROADCAST, (char *) &val,
609                           val_len) != 0)
610           goto error;
611         return;
612         
613       case _Jv_SO_OOBINLINE_ :
614         throw new ::java::net::SocketException (
615           JvNewStringUTF ("SO_OOBINLINE: not valid for UDP"));
616         return;
617         
618       case _Jv_SO_SNDBUF_ :
619       case _Jv_SO_RCVBUF_ :
620 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
621         int opt;
622         optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
623         if (::setsockopt (native_fd, SOL_SOCKET, opt, (char *) &val, val_len) != 0)
624           goto error;    
625 #else
626         throw new ::java::lang::InternalError (
627           JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
628 #endif 
629         return;
630       case _Jv_SO_REUSEADDR_ :
631 #if defined(SO_REUSEADDR)
632         if (::setsockopt (native_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
633             val_len) != 0)
634           goto error;
635 #else
636         throw new ::java::lang::InternalError (
637           JvNewStringUTF ("SO_REUSEADDR not supported"));
638 #endif 
639         return;
640       case _Jv_SO_BINDADDR_ :
641         throw new ::java::net::SocketException (
642           JvNewStringUTF ("SO_BINDADDR: read only option"));
643         return;
644       case _Jv_IP_MULTICAST_IF_ :
645         union InAddr u;
646         jbyteArray haddress;
647         jbyte *bytes;
648         int len;
649         int level, opname;
650         const char *ptr;
651
652         haddress = ((::java::net::InetAddress *) value)->addr;
653         bytes = elements (haddress);
654         len = haddress->length;
655         if (len == 4)
656           {
657             level = IPPROTO_IP;
658             opname = IP_MULTICAST_IF;
659             memcpy (&u.addr, bytes, len);
660             len = sizeof (struct in_addr);
661             ptr = (const char *) &u.addr;
662           }
663 // Tru64 UNIX V5.0 has struct sockaddr_in6, but no IPV6_MULTICAST_IF
664 #if defined (HAVE_INET6) && defined (IPV6_MULTICAST_IF)
665         else if (len == 16)
666           {
667             level = IPPROTO_IPV6;
668             opname = IPV6_MULTICAST_IF;
669             memcpy (&u.addr6, bytes, len);
670             len = sizeof (struct in6_addr);
671             ptr = (const char *) &u.addr6;
672           }
673 #endif
674         else
675           throw
676             new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
677
678         if (::setsockopt (native_fd, level, opname, ptr, len) != 0)
679           goto error;
680         return;
681         
682       case _Jv_IP_MULTICAST_IF2_ :
683         throw new ::java::net::SocketException (
684           JvNewStringUTF ("IP_MULTICAST_IF2: not yet implemented"));
685         return;
686         
687       case _Jv_IP_MULTICAST_LOOP_ :
688         // cache the local address
689         if (localAddress == NULL)
690           localAddress = getLocalAddress (native_fd);
691         len = localAddress->addr->length;
692         if (len == 4)
693           {
694             level = IPPROTO_IP;
695             opname = IP_MULTICAST_LOOP;
696           }
697 #if defined (HAVE_INET6) && defined (IPV6_MULTICAST_LOOP)
698         else if (len == 16)
699           {
700             level = IPPROTO_IPV6;
701             opname = IPV6_MULTICAST_LOOP;
702           }
703 #endif
704         else
705           throw
706             new ::java::net::SocketException (JvNewStringUTF ("invalid address length"));
707         if (::setsockopt (native_fd, level, opname, (char *) &val,
708                           val_len) != 0)
709           goto error;
710         return;
711         
712       case _Jv_IP_TOS_ :
713         if (::setsockopt (native_fd, SOL_SOCKET, IP_TOS, (char *) &val,
714            val_len) != 0)
715           goto error;    
716         return;
717         
718       case _Jv_SO_TIMEOUT_ :
719         timeout = val;
720         return;
721       default :
722         errno = ENOPROTOOPT;
723     }
724
725  error:
726   char* strerr = strerror (errno);
727   throw new ::java::net::SocketException (JvNewStringUTF (strerr));
728 }
729
730 ::java::lang::Object *
731 gnu::java::net::PlainDatagramSocketImpl::getOption (jint optID)
732 {
733   int val;
734   socklen_t val_len = sizeof(val);
735   int level, opname;
736
737   switch (optID)
738     {
739       case _Jv_TCP_NODELAY_ :
740         throw new ::java::net::SocketException (
741           JvNewStringUTF ("TCP_NODELAY not valid for UDP"));
742         break;
743       case _Jv_SO_LINGER_ :
744         throw new ::java::net::SocketException (
745           JvNewStringUTF ("SO_LINGER not valid for UDP"));
746         break;    
747       case _Jv_SO_KEEPALIVE_ :
748         throw new ::java::net::SocketException (
749           JvNewStringUTF ("SO_KEEPALIVE not valid for UDP"));
750         break;
751         
752       case _Jv_SO_BROADCAST_ :
753         if (::getsockopt (native_fd, SOL_SOCKET, SO_BROADCAST, (char *) &val,
754             &val_len) != 0)
755           goto error;
756         return new ::java::lang::Boolean (val != 0);
757         
758       case _Jv_SO_OOBINLINE_ :
759         throw new ::java::net::SocketException (
760           JvNewStringUTF ("SO_OOBINLINE not valid for UDP"));
761         break;
762       
763       case _Jv_SO_RCVBUF_ :
764       case _Jv_SO_SNDBUF_ :
765 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
766         int opt;
767         optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
768         if (::getsockopt (native_fd, SOL_SOCKET, opt, (char *) &val, &val_len) != 0)
769           goto error;    
770         else
771           return new ::java::lang::Integer (val);
772 #else
773         throw new ::java::lang::InternalError (
774           JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
775 #endif    
776         break;
777       case _Jv_SO_BINDADDR_:
778         // cache the local address
779         if (localAddress == NULL)
780           localAddress = getLocalAddress (native_fd);
781         return localAddress;  
782         break;
783       case _Jv_SO_REUSEADDR_ :
784 #if defined(SO_REUSEADDR)
785         if (::getsockopt (native_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
786             &val_len) != 0)
787           goto error;
788         return new ::java::lang::Boolean (val != 0);
789 #else
790         throw new ::java::lang::InternalError (
791           JvNewStringUTF ("SO_REUSEADDR not supported"));
792 #endif 
793         break;
794       case _Jv_IP_MULTICAST_IF_ :
795 #ifdef HAVE_INET_NTOA
796         struct in_addr inaddr;
797         socklen_t inaddr_len;
798         char *bytes;
799
800         inaddr_len = sizeof(inaddr);
801         if (::getsockopt (native_fd, IPPROTO_IP, IP_MULTICAST_IF, (char *) &inaddr,
802             &inaddr_len) != 0)
803           goto error;
804
805         bytes = inet_ntoa (inaddr);
806
807         return ::java::net::InetAddress::getByName (JvNewStringLatin1 (bytes));
808 #else
809         throw new ::java::net::SocketException (
810           JvNewStringUTF ("IP_MULTICAST_IF: not available - no inet_ntoa()"));
811 #endif
812         break;
813       case _Jv_SO_TIMEOUT_ :
814         return new ::java::lang::Integer (timeout);
815         break;
816         
817       case _Jv_IP_MULTICAST_IF2_ :
818         throw new ::java::net::SocketException (
819           JvNewStringUTF ("IP_MULTICAST_IF2: not yet implemented"));
820         break;
821         
822       case _Jv_IP_MULTICAST_LOOP_ :
823         // cache the local address
824         localAddress = getLocalAddress (native_fd);
825         if (localAddress->addr->length == 4) 
826           {
827             level = IPPROTO_IP;
828             opname = IP_MULTICAST_LOOP;
829           }
830 #if defined (HAVE_INET6) && defined (IPV6_MULTICAST_LOOP)
831         else if (localAddress->addr->length == 16)
832           {
833             level = IPPROTO_IPV6;
834             opname = IPV6_MULTICAST_LOOP;
835           }
836 #endif
837         else
838           throw
839             new ::java::net::SocketException (JvNewStringUTF ("invalid address length"));
840         if (::getsockopt (native_fd, level, opname, (char *) &val,
841                           &val_len) != 0)
842           goto error;
843         return new ::java::lang::Boolean (val != 0);
844         
845       case _Jv_IP_TOS_ :
846         if (::getsockopt (native_fd, SOL_SOCKET, IP_TOS, (char *) &val,
847            &val_len) != 0)
848           goto error;
849         return new ::java::lang::Integer (val);
850         
851       default :
852         errno = ENOPROTOOPT;
853     }
854
855  error:
856   char* strerr = strerror (errno);
857   throw new ::java::net::SocketException (JvNewStringUTF (strerr));
858 }