OSDN Git Service

* gnu/java/net/natPlainDatagramSocketImplPosix.cc
[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   // FIXME: Deal with Multicast and if the socket is connected.
290   jint rport = p->getPort();
291   union SockAddr u;
292   jbyteArray haddress = p->getAddress()->addr;
293   jbyte *bytes = elements (haddress);
294   int len = haddress->length;
295   struct sockaddr *ptr = (struct sockaddr *) &u.address;
296   jbyte *dbytes = elements (p->getData()) + p->getOffset();
297   if (len == 4)
298     {
299       u.address.sin_family = AF_INET;
300       memcpy (&u.address.sin_addr, bytes, len);
301       len = sizeof (struct sockaddr_in);
302       u.address.sin_port = htons (rport);
303     }
304 #ifdef HAVE_INET6
305   else if (len == 16)
306     {
307       u.address6.sin6_family = AF_INET6;
308       memcpy (&u.address6.sin6_addr, bytes, len);
309       len = sizeof (struct sockaddr_in6);
310       u.address6.sin6_port = htons (rport);
311     }
312 #endif
313   else
314     throw new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
315
316   if (::sendto (native_fd, (char *) dbytes, p->getLength(), 0, ptr, len) >= 0)
317     return;
318
319   char* strerr = strerror (errno);
320
321   if (errno == ECONNREFUSED)
322     throw new ::java::net::PortUnreachableException (JvNewStringUTF (strerr));
323
324   throw new ::java::io::IOException (JvNewStringUTF (strerr));
325 }
326
327 void
328 gnu::java::net::PlainDatagramSocketImpl::receive (::java::net::DatagramPacket *p)
329 {
330   // FIXME: Deal with Multicast and if the socket is connected.
331   union SockAddr u;
332   socklen_t addrlen = sizeof(u);
333   jbyte *dbytes = elements (p->getData()) + p->getOffset();
334   jint maxlen = p->maxlen - p->getOffset();
335   ssize_t retlen = 0;
336
337   // Do timeouts via select since SO_RCVTIMEO is not always available.
338   if (timeout > 0 && native_fd >= 0 && native_fd < FD_SETSIZE)
339     {
340       fd_set rset;
341       struct timeval tv;
342       FD_ZERO(&rset);
343       FD_SET(native_fd, &rset);
344       tv.tv_sec = timeout / 1000;
345       tv.tv_usec = (timeout % 1000) * 1000;
346       int retval;
347       if ((retval = _Jv_select (native_fd + 1, &rset, NULL, NULL, &tv)) < 0)
348         goto error;
349       else if (retval == 0)
350         throw new ::java::net::SocketTimeoutException
351           (JvNewStringUTF ("Receive timed out") );
352     }
353
354   retlen =
355     ::recvfrom (native_fd, (char *) dbytes, maxlen, 0, (sockaddr*) &u,
356       &addrlen);
357   if (retlen < 0)
358     goto error;
359   // FIXME: Deal with Multicast addressing and if the socket is connected.
360   jbyteArray raddr;
361   jint rport;
362   if (u.address.sin_family == AF_INET)
363     {
364       raddr = JvNewByteArray (4);
365       memcpy (elements (raddr), &u.address.sin_addr, 4);
366       rport = ntohs (u.address.sin_port);
367     }
368 #ifdef HAVE_INET6
369   else if (u.address.sin_family == AF_INET6)
370     {
371       raddr = JvNewByteArray (16);
372       memcpy (elements (raddr), &u.address6.sin6_addr, 16);
373       rport = ntohs (u.address6.sin6_port);
374     }
375 #endif
376   else
377     throw new ::java::net::SocketException (JvNewStringUTF ("invalid family"));
378
379   p->setAddress (new ::java::net::InetAddress (raddr, NULL));
380   p->setPort (rport);
381   p->length = (jint) retlen;
382   return;
383
384  error:
385   char* strerr = strerror (errno);
386
387   if (errno == ECONNREFUSED)
388     throw new ::java::net::PortUnreachableException (JvNewStringUTF (strerr));
389
390   throw new ::java::io::IOException (JvNewStringUTF (strerr));
391 }
392
393 void
394 gnu::java::net::PlainDatagramSocketImpl::setTimeToLive (jint ttl)
395 {
396   // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4.
397   char val = (char) ttl;
398   socklen_t val_len = sizeof(val);
399
400   if (::setsockopt (native_fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, val_len) == 0)
401     return;
402
403   char* strerr = strerror (errno);
404   throw new ::java::io::IOException (JvNewStringUTF (strerr));
405 }
406
407 jint
408 gnu::java::net::PlainDatagramSocketImpl::getTimeToLive ()
409 {
410   // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4.
411   char val;
412   socklen_t val_len = sizeof(val);
413
414   if (::getsockopt (native_fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, &val_len) == 0)
415     return ((int) val) & 0xFF;
416
417   char* strerr = strerror (errno);
418   throw new ::java::io::IOException (JvNewStringUTF (strerr));
419 }
420
421 void
422 gnu::java::net::PlainDatagramSocketImpl::mcastGrp (::java::net::InetAddress *inetaddr,
423                                                    ::java::net::NetworkInterface *,
424                                                    jboolean join)
425 {
426   // FIXME: implement use of NetworkInterface
427
428   union McastReq u;
429   jbyteArray haddress = inetaddr->addr;
430   jbyte *bytes = elements (haddress);
431   int len = haddress->length;
432   int level, opname;
433   const char *ptr;
434   if (0)
435     ;
436 #if HAVE_STRUCT_IP_MREQ
437   else if (len == 4)
438     {
439       level = IPPROTO_IP;
440       opname = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
441       memcpy (&u.mreq.imr_multiaddr, bytes, len);
442       // FIXME:  If a non-default interface is set, use it; see Stevens p. 501.
443       // Maybe not, see note in last paragraph at bottom of Stevens p. 497.
444       u.mreq.imr_interface.s_addr = htonl (INADDR_ANY); 
445       len = sizeof (struct ip_mreq);
446       ptr = (const char *) &u.mreq;
447     }
448 #endif
449 #if HAVE_STRUCT_IPV6_MREQ
450   else if (len == 16)
451     {
452       level = IPPROTO_IPV6;
453
454       /* Prefer new RFC 2553 names.  */
455 #ifndef IPV6_JOIN_GROUP
456 #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
457 #endif
458 #ifndef IPV6_LEAVE_GROUP
459 #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
460 #endif
461
462       opname = join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP;
463       memcpy (&u.mreq6.ipv6mr_multiaddr, bytes, len);
464       // FIXME:  If a non-default interface is set, use it; see Stevens p. 501.
465       // Maybe not, see note in last paragraph at bottom of Stevens p. 497.
466       u.mreq6.ipv6mr_interface = 0;
467       len = sizeof (struct ipv6_mreq);
468       ptr = (const char *) &u.mreq6;
469     }
470 #endif
471   else
472     throw new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
473
474   if (::setsockopt (native_fd, level, opname, ptr, len) == 0)
475     return;
476
477   char* strerr = strerror (errno);
478   throw new ::java::io::IOException (JvNewStringUTF (strerr));
479 }
480
481 void
482 gnu::java::net::PlainDatagramSocketImpl::setOption (jint optID,
483                                                     ::java::lang::Object *value)
484 {
485   int val;
486   socklen_t val_len = sizeof (val);
487
488   if (native_fd < 0)
489     throw new ::java::net::SocketException (JvNewStringUTF ("Socket closed"));
490
491   if (_Jv_IsInstanceOf (value, &::java::lang::Boolean::class$))
492     {
493       ::java::lang::Boolean *boolobj = 
494         static_cast< ::java::lang::Boolean *> (value);
495       val = boolobj->booleanValue() ? 1 : 0;
496     }
497   else if (_Jv_IsInstanceOf (value, &::java::lang::Integer::class$))
498     {
499       ::java::lang::Integer *intobj = 
500         static_cast< ::java::lang::Integer *> (value);          
501       val = (int) intobj->intValue();
502     }
503   // Else assume value to be an InetAddress for use with IP_MULTICAST_IF.
504
505   switch (optID) 
506     {
507       case _Jv_TCP_NODELAY_ :
508         throw new ::java::net::SocketException (
509           JvNewStringUTF ("TCP_NODELAY not valid for UDP"));
510         return;
511       case _Jv_SO_LINGER_ :
512         throw new ::java::net::SocketException (
513           JvNewStringUTF ("SO_LINGER not valid for UDP"));
514         return;
515       case _Jv_SO_KEEPALIVE_ :
516         throw new ::java::net::SocketException (
517           JvNewStringUTF ("SO_KEEPALIVE not valid for UDP"));
518         return;
519
520       case _Jv_SO_BROADCAST_ :
521         if (::setsockopt (native_fd, SOL_SOCKET, SO_BROADCAST, (char *) &val,
522                           val_len) != 0)
523           goto error;
524         return;
525         
526       case _Jv_SO_OOBINLINE_ :
527         throw new ::java::net::SocketException (
528           JvNewStringUTF ("SO_OOBINLINE: not valid for UDP"));
529         return;
530         
531       case _Jv_SO_SNDBUF_ :
532       case _Jv_SO_RCVBUF_ :
533 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
534         int opt;
535         optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
536         if (::setsockopt (native_fd, SOL_SOCKET, opt, (char *) &val, val_len) != 0)
537           goto error;    
538 #else
539         throw new ::java::lang::InternalError (
540           JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
541 #endif 
542         return;
543       case _Jv_SO_REUSEADDR_ :
544 #if defined(SO_REUSEADDR)
545         if (::setsockopt (native_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
546             val_len) != 0)
547           goto error;
548 #else
549         throw new ::java::lang::InternalError (
550           JvNewStringUTF ("SO_REUSEADDR not supported"));
551 #endif 
552         return;
553       case _Jv_SO_BINDADDR_ :
554         throw new ::java::net::SocketException (
555           JvNewStringUTF ("SO_BINDADDR: read only option"));
556         return;
557       case _Jv_IP_MULTICAST_IF_ :
558         union InAddr u;
559         jbyteArray haddress;
560         jbyte *bytes;
561         int len;
562         int level, opname;
563         const char *ptr;
564
565         haddress = ((::java::net::InetAddress *) value)->addr;
566         bytes = elements (haddress);
567         len = haddress->length;
568         if (len == 4)
569           {
570             level = IPPROTO_IP;
571             opname = IP_MULTICAST_IF;
572             memcpy (&u.addr, bytes, len);
573             len = sizeof (struct in_addr);
574             ptr = (const char *) &u.addr;
575           }
576 // Tru64 UNIX V5.0 has struct sockaddr_in6, but no IPV6_MULTICAST_IF
577 #if defined (HAVE_INET6) && defined (IPV6_MULTICAST_IF)
578         else if (len == 16)
579           {
580             level = IPPROTO_IPV6;
581             opname = IPV6_MULTICAST_IF;
582             memcpy (&u.addr6, bytes, len);
583             len = sizeof (struct in6_addr);
584             ptr = (const char *) &u.addr6;
585           }
586 #endif
587         else
588           throw
589             new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
590
591         if (::setsockopt (native_fd, level, opname, ptr, len) != 0)
592           goto error;
593         return;
594         
595       case _Jv_IP_MULTICAST_IF2_ :
596         throw new ::java::net::SocketException (
597           JvNewStringUTF ("IP_MULTICAST_IF2: not yet implemented"));
598         return;
599         
600       case _Jv_IP_MULTICAST_LOOP_ :
601         throw new ::java::net::SocketException (
602           JvNewStringUTF ("IP_MULTICAST_LOOP: not yet implemented"));
603         return;
604         
605       case _Jv_IP_TOS_ :
606         if (::setsockopt (native_fd, SOL_SOCKET, IP_TOS, (char *) &val,
607            val_len) != 0)
608           goto error;    
609         return;
610         
611       case _Jv_SO_TIMEOUT_ :
612         timeout = val;
613         return;
614       default :
615         errno = ENOPROTOOPT;
616     }
617
618  error:
619   char* strerr = strerror (errno);
620   throw new ::java::net::SocketException (JvNewStringUTF (strerr));
621 }
622
623 ::java::lang::Object *
624 gnu::java::net::PlainDatagramSocketImpl::getOption (jint optID)
625 {
626   int val;
627   socklen_t val_len = sizeof(val);
628   union SockAddr u;
629   socklen_t addrlen = sizeof(u);
630
631   switch (optID)
632     {
633       case _Jv_TCP_NODELAY_ :
634         throw new ::java::net::SocketException (
635           JvNewStringUTF ("TCP_NODELAY not valid for UDP"));
636         break;
637       case _Jv_SO_LINGER_ :
638         throw new ::java::net::SocketException (
639           JvNewStringUTF ("SO_LINGER not valid for UDP"));
640         break;    
641       case _Jv_SO_KEEPALIVE_ :
642         throw new ::java::net::SocketException (
643           JvNewStringUTF ("SO_KEEPALIVE not valid for UDP"));
644         break;
645         
646       case _Jv_SO_BROADCAST_ :
647         if (::getsockopt (native_fd, SOL_SOCKET, SO_BROADCAST, (char *) &val,
648             &val_len) != 0)
649           goto error;
650         return new ::java::lang::Boolean (val != 0);
651         
652       case _Jv_SO_OOBINLINE_ :
653         throw new ::java::net::SocketException (
654           JvNewStringUTF ("SO_OOBINLINE not valid for UDP"));
655         break;
656       
657       case _Jv_SO_RCVBUF_ :
658       case _Jv_SO_SNDBUF_ :
659 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
660         int opt;
661         optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
662         if (::getsockopt (native_fd, SOL_SOCKET, opt, (char *) &val, &val_len) != 0)
663           goto error;    
664         else
665           return new ::java::lang::Integer (val);
666 #else
667         throw new ::java::lang::InternalError (
668           JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
669 #endif    
670         break;
671       case _Jv_SO_BINDADDR_:
672         // cache the local address
673         if (localAddress == NULL)
674           {     
675             jbyteArray laddr;
676             if (::getsockname (native_fd, (sockaddr*) &u, &addrlen) != 0)
677               goto error;
678             if (u.address.sin_family == AF_INET)
679               {
680                 laddr = JvNewByteArray (4);
681                 memcpy (elements (laddr), &u.address.sin_addr, 4);
682               }
683 #ifdef HAVE_INET6
684             else if (u.address.sin_family == AF_INET6)
685               {
686                 laddr = JvNewByteArray (16);
687                 memcpy (elements (laddr), &u.address6.sin6_addr, 16);
688               }
689 #endif
690             else
691               throw new ::java::net::SocketException (
692                               JvNewStringUTF ("invalid family"));
693             localAddress = new ::java::net::InetAddress (laddr, NULL);
694           }
695         return localAddress;  
696         break;
697       case _Jv_SO_REUSEADDR_ :
698 #if defined(SO_REUSEADDR)
699         if (::getsockopt (native_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
700             &val_len) != 0)
701           goto error;
702         return new ::java::lang::Boolean (val != 0);
703 #else
704         throw new ::java::lang::InternalError (
705           JvNewStringUTF ("SO_REUSEADDR not supported"));
706 #endif 
707         break;
708       case _Jv_IP_MULTICAST_IF_ :
709 #ifdef HAVE_INET_NTOA
710         struct in_addr inaddr;
711         socklen_t inaddr_len;
712         char *bytes;
713
714         inaddr_len = sizeof(inaddr);
715         if (::getsockopt (native_fd, IPPROTO_IP, IP_MULTICAST_IF, (char *) &inaddr,
716             &inaddr_len) != 0)
717           goto error;
718
719         bytes = inet_ntoa (inaddr);
720
721         return ::java::net::InetAddress::getByName (JvNewStringLatin1 (bytes));
722 #else
723         throw new ::java::net::SocketException (
724           JvNewStringUTF ("IP_MULTICAST_IF: not available - no inet_ntoa()"));
725 #endif
726         break;
727       case _Jv_SO_TIMEOUT_ :
728         return new ::java::lang::Integer (timeout);
729         break;
730         
731       case _Jv_IP_MULTICAST_IF2_ :
732         throw new ::java::net::SocketException (
733           JvNewStringUTF ("IP_MULTICAST_IF2: not yet implemented"));
734         break;
735         
736       case _Jv_IP_MULTICAST_LOOP_ :
737         if (::getsockopt (native_fd, SOL_SOCKET, IP_MULTICAST_LOOP, (char *) &val,
738             &val_len) != 0)
739           goto error;
740         return new ::java::lang::Boolean (val != 0);
741         
742       case _Jv_IP_TOS_ :
743         if (::getsockopt (native_fd, SOL_SOCKET, IP_TOS, (char *) &val,
744            &val_len) != 0)
745           goto error;
746         return new ::java::lang::Integer (val);
747         
748       default :
749         errno = ENOPROTOOPT;
750     }
751
752  error:
753   char* strerr = strerror (errno);
754   throw new ::java::net::SocketException (JvNewStringUTF (strerr));
755 }