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
create()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
bind(jint lport,::java::net::InetAddress * host)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
connect(::java::net::InetAddress * host,jint rport)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
disconnect()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
peek(::java::net::InetAddress * i)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
peekData(::java::net::DatagramPacket * p)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
close()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
send(::java::net::DatagramPacket * p)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
receive(::java::net::DatagramPacket * p)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
setTimeToLive(jint ttl)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
getTimeToLive()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
mcastGrp(::java::net::InetAddress * inetaddr,::java::net::NetworkInterface *,jboolean join)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 *
getLocalAddress(int native_fd)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
setOption(jint optID,::java::lang::Object * value)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 *
getOption(jint optID)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 }
853