OSDN Git Service

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