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