OSDN Git Service

5c76779d9dd02d78817bc18d65c3d367f25b0631
[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   union McastReq u;
433   jbyteArray haddress = inetaddr->addr;
434   jbyte *bytes = elements (haddress);
435   int len = haddress->length;
436   int level, opname;
437   const char *ptr;
438   if (0)
439     ;
440 #if HAVE_STRUCT_IP_MREQ
441   else if (len == 4)
442     {
443       level = IPPROTO_IP;
444       opname = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
445       memcpy (&u.mreq.imr_multiaddr, bytes, len);
446       // FIXME:  If a non-default interface is set, use it; see Stevens p. 501.
447       // Maybe not, see note in last paragraph at bottom of Stevens p. 497.
448       u.mreq.imr_interface.s_addr = htonl (INADDR_ANY); 
449       len = sizeof (struct ip_mreq);
450       ptr = (const char *) &u.mreq;
451     }
452 #endif
453 #if HAVE_STRUCT_IPV6_MREQ
454   else if (len == 16)
455     {
456       level = IPPROTO_IPV6;
457
458       /* Prefer new RFC 2553 names.  */
459 #ifndef IPV6_JOIN_GROUP
460 #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
461 #endif
462 #ifndef IPV6_LEAVE_GROUP
463 #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
464 #endif
465
466       opname = join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP;
467       memcpy (&u.mreq6.ipv6mr_multiaddr, bytes, len);
468       // FIXME:  If a non-default interface is set, use it; see Stevens p. 501.
469       // Maybe not, see note in last paragraph at bottom of Stevens p. 497.
470       u.mreq6.ipv6mr_interface = 0;
471       len = sizeof (struct ipv6_mreq);
472       ptr = (const char *) &u.mreq6;
473     }
474 #endif
475   else
476     throw new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
477
478   if (::setsockopt (native_fd, level, opname, ptr, len) == 0)
479     return;
480
481   char* strerr = strerror (errno);
482   throw new ::java::io::IOException (JvNewStringUTF (strerr));
483 }
484
485 void
486 gnu::java::net::PlainDatagramSocketImpl::setOption (jint optID,
487                                                     ::java::lang::Object *value)
488 {
489   int val;
490   socklen_t val_len = sizeof (val);
491
492   if (native_fd < 0)
493     throw new ::java::net::SocketException (JvNewStringUTF ("Socket closed"));
494
495   if (_Jv_IsInstanceOf (value, &::java::lang::Boolean::class$))
496     {
497       ::java::lang::Boolean *boolobj = 
498         static_cast< ::java::lang::Boolean *> (value);
499       val = boolobj->booleanValue() ? 1 : 0;
500     }
501   else if (_Jv_IsInstanceOf (value, &::java::lang::Integer::class$))
502     {
503       ::java::lang::Integer *intobj = 
504         static_cast< ::java::lang::Integer *> (value);          
505       val = (int) intobj->intValue();
506     }
507   // Else assume value to be an InetAddress for use with IP_MULTICAST_IF.
508
509   switch (optID) 
510     {
511       case _Jv_TCP_NODELAY_ :
512         throw new ::java::net::SocketException (
513           JvNewStringUTF ("TCP_NODELAY not valid for UDP"));
514         return;
515       case _Jv_SO_LINGER_ :
516         throw new ::java::net::SocketException (
517           JvNewStringUTF ("SO_LINGER not valid for UDP"));
518         return;
519       case _Jv_SO_KEEPALIVE_ :
520         throw new ::java::net::SocketException (
521           JvNewStringUTF ("SO_KEEPALIVE not valid for UDP"));
522         return;
523
524       case _Jv_SO_BROADCAST_ :
525         if (::setsockopt (native_fd, SOL_SOCKET, SO_BROADCAST, (char *) &val,
526                           val_len) != 0)
527           goto error;
528         return;
529         
530       case _Jv_SO_OOBINLINE_ :
531         throw new ::java::net::SocketException (
532           JvNewStringUTF ("SO_OOBINLINE: not valid for UDP"));
533         return;
534         
535       case _Jv_SO_SNDBUF_ :
536       case _Jv_SO_RCVBUF_ :
537 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
538         int opt;
539         optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
540         if (::setsockopt (native_fd, SOL_SOCKET, opt, (char *) &val, val_len) != 0)
541           goto error;    
542 #else
543         throw new ::java::lang::InternalError (
544           JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
545 #endif 
546         return;
547       case _Jv_SO_REUSEADDR_ :
548 #if defined(SO_REUSEADDR)
549         if (::setsockopt (native_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
550             val_len) != 0)
551           goto error;
552 #else
553         throw new ::java::lang::InternalError (
554           JvNewStringUTF ("SO_REUSEADDR not supported"));
555 #endif 
556         return;
557       case _Jv_SO_BINDADDR_ :
558         throw new ::java::net::SocketException (
559           JvNewStringUTF ("SO_BINDADDR: read only option"));
560         return;
561       case _Jv_IP_MULTICAST_IF_ :
562         union InAddr u;
563         jbyteArray haddress;
564         jbyte *bytes;
565         int len;
566         int level, opname;
567         const char *ptr;
568
569         haddress = ((::java::net::InetAddress *) value)->addr;
570         bytes = elements (haddress);
571         len = haddress->length;
572         if (len == 4)
573           {
574             level = IPPROTO_IP;
575             opname = IP_MULTICAST_IF;
576             memcpy (&u.addr, bytes, len);
577             len = sizeof (struct in_addr);
578             ptr = (const char *) &u.addr;
579           }
580 // Tru64 UNIX V5.0 has struct sockaddr_in6, but no IPV6_MULTICAST_IF
581 #if defined (HAVE_INET6) && defined (IPV6_MULTICAST_IF)
582         else if (len == 16)
583           {
584             level = IPPROTO_IPV6;
585             opname = IPV6_MULTICAST_IF;
586             memcpy (&u.addr6, bytes, len);
587             len = sizeof (struct in6_addr);
588             ptr = (const char *) &u.addr6;
589           }
590 #endif
591         else
592           throw
593             new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
594
595         if (::setsockopt (native_fd, level, opname, ptr, len) != 0)
596           goto error;
597         return;
598         
599       case _Jv_IP_MULTICAST_IF2_ :
600         throw new ::java::net::SocketException (
601           JvNewStringUTF ("IP_MULTICAST_IF2: not yet implemented"));
602         return;
603         
604       case _Jv_IP_MULTICAST_LOOP_ :
605         haddress = ((::java::net::InetAddress *) value)->addr;
606         len = haddress->length;
607         if (len == 4)
608           {
609             level = IPPROTO_IP;
610             opname = IP_MULTICAST_LOOP;
611           }
612 #if defined (HAVE_INET6) && defined (IPV6_MULTICAST_LOOP)
613         else if (len == 16)
614           {
615             level = IPPROTO_IPV6;
616             opname = IPV6_MULTICAST_LOOP;
617           }
618 #endif
619         else
620           throw
621             new ::java::net::SocketException (JvNewStringUTF ("invalid address length"));
622         if (::setsockopt (native_fd, level, opname, (char *) &val,
623                           val_len) != 0)
624           goto error;
625         return;
626         
627       case _Jv_IP_TOS_ :
628         if (::setsockopt (native_fd, SOL_SOCKET, IP_TOS, (char *) &val,
629            val_len) != 0)
630           goto error;    
631         return;
632         
633       case _Jv_SO_TIMEOUT_ :
634         timeout = val;
635         return;
636       default :
637         errno = ENOPROTOOPT;
638     }
639
640  error:
641   char* strerr = strerror (errno);
642   throw new ::java::net::SocketException (JvNewStringUTF (strerr));
643 }
644
645 ::java::lang::Object *
646 gnu::java::net::PlainDatagramSocketImpl::getOption (jint optID)
647 {
648   int val;
649   socklen_t val_len = sizeof(val);
650   union SockAddr u;
651   socklen_t addrlen = sizeof(u);
652   int level, opname;
653
654   switch (optID)
655     {
656       case _Jv_TCP_NODELAY_ :
657         throw new ::java::net::SocketException (
658           JvNewStringUTF ("TCP_NODELAY not valid for UDP"));
659         break;
660       case _Jv_SO_LINGER_ :
661         throw new ::java::net::SocketException (
662           JvNewStringUTF ("SO_LINGER not valid for UDP"));
663         break;    
664       case _Jv_SO_KEEPALIVE_ :
665         throw new ::java::net::SocketException (
666           JvNewStringUTF ("SO_KEEPALIVE not valid for UDP"));
667         break;
668         
669       case _Jv_SO_BROADCAST_ :
670         if (::getsockopt (native_fd, SOL_SOCKET, SO_BROADCAST, (char *) &val,
671             &val_len) != 0)
672           goto error;
673         return new ::java::lang::Boolean (val != 0);
674         
675       case _Jv_SO_OOBINLINE_ :
676         throw new ::java::net::SocketException (
677           JvNewStringUTF ("SO_OOBINLINE not valid for UDP"));
678         break;
679       
680       case _Jv_SO_RCVBUF_ :
681       case _Jv_SO_SNDBUF_ :
682 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
683         int opt;
684         optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
685         if (::getsockopt (native_fd, SOL_SOCKET, opt, (char *) &val, &val_len) != 0)
686           goto error;    
687         else
688           return new ::java::lang::Integer (val);
689 #else
690         throw new ::java::lang::InternalError (
691           JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
692 #endif    
693         break;
694       case _Jv_SO_BINDADDR_:
695         // cache the local address
696         if (localAddress == NULL)
697           {     
698             jbyteArray laddr;
699             if (::getsockname (native_fd, (sockaddr*) &u, &addrlen) != 0)
700               goto error;
701             if (u.address.sin_family == AF_INET)
702               {
703                 laddr = JvNewByteArray (4);
704                 memcpy (elements (laddr), &u.address.sin_addr, 4);
705               }
706 #ifdef HAVE_INET6
707             else if (u.address.sin_family == AF_INET6)
708               {
709                 laddr = JvNewByteArray (16);
710                 memcpy (elements (laddr), &u.address6.sin6_addr, 16);
711               }
712 #endif
713             else
714               throw new ::java::net::SocketException (
715                               JvNewStringUTF ("invalid family"));
716             localAddress = new ::java::net::InetAddress (laddr, NULL);
717           }
718         return localAddress;  
719         break;
720       case _Jv_SO_REUSEADDR_ :
721 #if defined(SO_REUSEADDR)
722         if (::getsockopt (native_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
723             &val_len) != 0)
724           goto error;
725         return new ::java::lang::Boolean (val != 0);
726 #else
727         throw new ::java::lang::InternalError (
728           JvNewStringUTF ("SO_REUSEADDR not supported"));
729 #endif 
730         break;
731       case _Jv_IP_MULTICAST_IF_ :
732 #ifdef HAVE_INET_NTOA
733         struct in_addr inaddr;
734         socklen_t inaddr_len;
735         char *bytes;
736
737         inaddr_len = sizeof(inaddr);
738         if (::getsockopt (native_fd, IPPROTO_IP, IP_MULTICAST_IF, (char *) &inaddr,
739             &inaddr_len) != 0)
740           goto error;
741
742         bytes = inet_ntoa (inaddr);
743
744         return ::java::net::InetAddress::getByName (JvNewStringLatin1 (bytes));
745 #else
746         throw new ::java::net::SocketException (
747           JvNewStringUTF ("IP_MULTICAST_IF: not available - no inet_ntoa()"));
748 #endif
749         break;
750       case _Jv_SO_TIMEOUT_ :
751         return new ::java::lang::Integer (timeout);
752         break;
753         
754       case _Jv_IP_MULTICAST_IF2_ :
755         throw new ::java::net::SocketException (
756           JvNewStringUTF ("IP_MULTICAST_IF2: not yet implemented"));
757         break;
758         
759       case _Jv_IP_MULTICAST_LOOP_ :
760         // cache the local address
761         if (localAddress == NULL)
762           {     
763             jbyteArray laddr;
764             if (::getsockname (native_fd, (sockaddr*) &u, &addrlen) != 0)
765               goto error;
766             if (u.address.sin_family == AF_INET)
767               {
768                 laddr = JvNewByteArray (4);
769                 memcpy (elements (laddr), &u.address.sin_addr, 4);
770               }
771 #ifdef HAVE_INET6
772             else if (u.address.sin_family == AF_INET6)
773               {
774                 laddr = JvNewByteArray (16);
775                 memcpy (elements (laddr), &u.address6.sin6_addr, 16);
776               }
777 #endif
778             else
779               throw new ::java::net::SocketException (
780                               JvNewStringUTF ("invalid family"));
781             localAddress = new ::java::net::InetAddress (laddr, NULL);
782             
783           }
784         if (localAddress->addr->length == 4) 
785           {
786             level = IPPROTO_IP;
787             opname = IP_MULTICAST_LOOP;
788           }
789 #if defined (HAVE_INET6) && defined (IPV6_MULTICAST_LOOP)
790         else if (localAddress->addr->length == 16)
791           {
792             level = IPPROTO_IPV6;
793             opname = IPV6_MULTICAST_LOOP;
794           }
795 #endif
796         else
797           throw
798             new ::java::net::SocketException (JvNewStringUTF ("invalid address length"));
799         if (::getsockopt (native_fd, level, opname, (char *) &val,
800                           &val_len) != 0)
801           goto error;
802         return new ::java::lang::Boolean (val != 0);
803         
804       case _Jv_IP_TOS_ :
805         if (::getsockopt (native_fd, SOL_SOCKET, IP_TOS, (char *) &val,
806            &val_len) != 0)
807           goto error;
808         return new ::java::lang::Integer (val);
809         
810       default :
811         errno = ENOPROTOOPT;
812     }
813
814  error:
815   char* strerr = strerror (errno);
816   throw new ::java::net::SocketException (JvNewStringUTF (strerr));
817 }