d16f1d31f7b06adbb2c2560713760d6d7c5c2551
[platform/upstream/gcc.git] / libjava / gnu / java / net / natPlainSocketImplPosix.cc
1 /* Copyright (C) 2003, 2004, 2005, 2006, 2007  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_SYS_IOCTL_H
13 #define BSD_COMP /* Get FIONREAD on Solaris2. */
14 #include <sys/ioctl.h>
15 #endif
16
17 // Pick up FIONREAD on Solaris 2.5.
18 #ifdef HAVE_SYS_FILIO_H
19 #include <sys/filio.h>
20 #endif
21
22 #include <netinet/in.h>
23 #include <netinet/tcp.h>
24 #include <errno.h>
25 #include <string.h>
26
27 #if HAVE_BSTRING_H
28 // Needed for bzero, implicitly used by FD_ZERO on IRIX 5.2 
29 #include <bstring.h>
30 #endif
31
32 #include <gcj/cni.h>
33 #include <gcj/javaprims.h>
34 #include <gnu/java/net/PlainSocketImpl.h>
35 #include <gnu/java/net/PlainSocketImpl$SocketInputStream.h>
36 #include <gnu/java/net/PlainSocketImpl$SocketOutputStream.h>
37 #include <java/io/IOException.h>
38 #include <java/io/InterruptedIOException.h>
39 #include <java/net/BindException.h>
40 #include <java/net/ConnectException.h>
41 #include <java/net/InetAddress.h>
42 #include <java/net/InetSocketAddress.h>
43 #include <java/net/SocketException.h>
44 #include <java/net/SocketTimeoutException.h>
45 #include <java/lang/InternalError.h>
46 #include <java/lang/Object.h>
47 #include <java/lang/Boolean.h>
48 #include <java/lang/Class.h>
49 #include <java/lang/Integer.h>
50 #include <java/lang/Thread.h>
51 #include <java/lang/NullPointerException.h>
52 #include <java/lang/ArrayIndexOutOfBoundsException.h>
53 #include <java/lang/IllegalArgumentException.h>
54 #include <java/net/UnknownHostException.h>
55
56 union SockAddr
57 {
58   struct sockaddr_in address;
59 #ifdef HAVE_INET6
60   struct sockaddr_in6 address6;
61 #endif
62 };
63
64 void
65 gnu::java::net::PlainSocketImpl::create (jboolean stream)
66 {
67   int sock = _Jv_socket (AF_INET, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
68
69   if (sock < 0)
70     {
71       char* strerr = strerror (errno);
72       throw new ::java::io::IOException (JvNewStringUTF (strerr));
73     }
74
75   // We use native_fd in place of fd here.  From leaving fd null we avoid
76   // the double close problem in FileDescriptor.finalize.
77   native_fd = sock;
78 }
79
80 void
81 gnu::java::net::PlainSocketImpl::bind (::java::net::InetAddress *host, jint lport)
82 {
83   union SockAddr u;
84   struct sockaddr *ptr = (struct sockaddr *) &u.address;
85   jbyteArray haddress = host->addr;
86   jbyte *bytes = elements (haddress);
87   int len = haddress->length;
88   int i = 1;
89
90   // The following is needed for OS X/PPC, otherwise bind() fails with an
91   // error. I found the issue and following fix on some mailing list, but
92   // no explanation was given as to why this solved the problem.
93   memset (&u, 0, sizeof (u));
94
95   if (len == 4)
96     {
97       u.address.sin_family = AF_INET;
98
99       if (host != NULL)
100         memcpy (&u.address.sin_addr, bytes, len);
101       else
102         u.address.sin_addr.s_addr = htonl (INADDR_ANY);
103
104       len = sizeof (struct sockaddr_in);
105       u.address.sin_port = htons (lport);
106     }
107 #ifdef HAVE_INET6
108   else if (len == 16)
109     {
110       u.address6.sin6_family = AF_INET6;
111       memcpy (&u.address6.sin6_addr, bytes, len);
112       len = sizeof (struct sockaddr_in6);
113       u.address6.sin6_port = htons (lport);
114     }
115 #endif
116   else
117     throw new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
118
119   // Enable SO_REUSEADDR, so that servers can reuse ports left in TIME_WAIT.
120   ::setsockopt(native_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof(i));
121   
122   if (_Jv_bind (native_fd, ptr, len) == 0)
123     {
124       socklen_t addrlen = sizeof(u);
125
126       if (lport != 0)
127         localport = lport;
128       else if (::getsockname (native_fd, (sockaddr*) &u, &addrlen) == 0)
129         localport = ntohs (u.address.sin_port);
130       else
131         goto error;
132
133       return;
134     }
135
136  error:
137   char* strerr = strerror (errno);
138   throw new ::java::net::BindException (JvNewStringUTF (strerr));
139 }
140
141 void
142 gnu::java::net::PlainSocketImpl::connect (::java::net::SocketAddress *addr,
143                                           jint timeout)
144 {
145   ::java::net::InetSocketAddress *tmp = (::java::net::InetSocketAddress*) addr;
146   ::java::net::InetAddress *host = tmp->getAddress();
147   if (! host)
148     throw new ::java::net::UnknownHostException(tmp->toString());
149
150   jint rport = tmp->getPort();
151         
152   // Set the SocketImpl's address and port fields before we try to
153   // connect.  Note that the fact that these are set doesn't imply
154   // that we're actually connected to anything.  We need to record
155   // this data before we attempt the connect, since non-blocking
156   // SocketChannels will use this and almost certainly throw timeout
157   // exceptions.
158   address = host;
159   port = rport;
160
161   union SockAddr u;
162   socklen_t addrlen = sizeof(u);
163   jbyteArray haddress = host->addr;
164   jbyte *bytes = elements (haddress);
165   int len = haddress->length;
166   struct sockaddr *ptr = (struct sockaddr *) &u.address;
167   if (len == 4)
168     {
169       u.address.sin_family = AF_INET;
170       memcpy (&u.address.sin_addr, bytes, len);
171       len = sizeof (struct sockaddr_in);
172       u.address.sin_port = htons (rport);
173     }
174 #ifdef HAVE_INET6
175   else if (len == 16)
176     {
177       u.address6.sin6_family = AF_INET6;
178       memcpy (&u.address6.sin6_addr, bytes, len);
179       len = sizeof (struct sockaddr_in6);
180       u.address6.sin6_port = htons (rport);
181     }
182 #endif
183   else
184     throw new ::java::net::SocketException (JvNewStringUTF ("invalid length"));
185
186   if (timeout > 0)
187     {
188       int flags = ::fcntl (native_fd, F_GETFL);
189       ::fcntl (native_fd, F_SETFL, flags | O_NONBLOCK);
190       
191       if ((_Jv_connect (native_fd, ptr, len) != 0) && (errno != EINPROGRESS))
192         goto error;
193
194       fd_set fset;
195       struct timeval tv;
196       FD_ZERO(&fset);
197       FD_SET(native_fd, &fset);
198       tv.tv_sec = timeout / 1000;
199       tv.tv_usec = (timeout % 1000) * 1000;
200       int retval;
201       
202       if ((retval = _Jv_select (native_fd + 1, &fset, &fset, NULL, &tv)) < 0)
203         goto error;
204       else if (retval == 0)
205         throw new ::java::net::SocketTimeoutException
206           (JvNewStringUTF ("Connect timed out"));
207        // Set the socket back into a blocking state.
208        ::fcntl (native_fd, F_SETFL, flags);
209     }
210   else
211     {
212       if (_Jv_connect (native_fd, ptr, len) != 0)
213         goto error;
214     }
215
216   // A bind may not have been done on this socket; if so, set localport now.
217   if (localport == 0)
218     {
219       if (::getsockname (native_fd, (sockaddr*) &u, &addrlen) == 0)
220         localport = ntohs (u.address.sin_port);
221       else
222         goto error;
223     }
224
225   return;  
226
227  error:
228   char* strerr = strerror (errno);
229   throw new ::java::net::ConnectException (JvNewStringUTF (strerr));
230 }
231
232 void
233 gnu::java::net::PlainSocketImpl::listen (jint backlog)
234 {
235   if (::listen (native_fd, backlog) != 0)
236     {
237       char* strerr = strerror (errno);
238       throw new ::java::io::IOException (JvNewStringUTF (strerr));
239     }
240 }
241
242 static void 
243 throw_on_sock_closed (gnu::java::net::PlainSocketImpl *soc_impl)
244 {
245     // Avoid races from asynchronous close().
246     JvSynchronize sync (soc_impl);
247     if (soc_impl->native_fd == -1)
248       {
249         using namespace java::net;
250         // Socket was closed.
251         SocketException *se =
252             new SocketException (JvNewStringUTF ("Socket Closed"));
253         throw se;
254       }
255 }
256
257 void
258 gnu::java::net::PlainSocketImpl::accept (gnu::java::net::PlainSocketImpl *s)
259 {
260   union SockAddr u;
261   socklen_t addrlen = sizeof(u);
262   int new_socket = 0; 
263
264   // Do timeouts via select since SO_RCVTIMEO is not always available.
265   if (timeout > 0 && native_fd >= 0 && native_fd < FD_SETSIZE)
266     {
267       fd_set fset;
268       struct timeval tv;
269       FD_ZERO(&fset);
270       FD_SET(native_fd, &fset);
271       tv.tv_sec = timeout / 1000;
272       tv.tv_usec = (timeout % 1000) * 1000;
273       int retval;
274       if ((retval = _Jv_select (native_fd + 1, &fset, &fset, NULL, &tv)) < 0)
275         goto error;
276       else if (retval == 0)
277         throw new ::java::net::SocketTimeoutException (
278                                           JvNewStringUTF("Accept timed out"));
279     }
280
281   new_socket = _Jv_accept (native_fd, (sockaddr*) &u, &addrlen);
282
283   if (new_socket < 0)
284     goto error;
285
286   jbyteArray raddr;
287   jint rport;
288   if (u.address.sin_family == AF_INET)
289     {
290       raddr = JvNewByteArray (4);
291       memcpy (elements (raddr), &u.address.sin_addr, 4);
292       rport = ntohs (u.address.sin_port);
293     }
294 #ifdef HAVE_INET6
295   else if (u.address.sin_family == AF_INET6)
296     {
297       raddr = JvNewByteArray (16);
298       memcpy (elements (raddr), &u.address6.sin6_addr, 16);
299       rport = ntohs (u.address6.sin6_port);
300     }
301 #endif
302   else
303     throw new ::java::net::SocketException (JvNewStringUTF ("invalid family"));
304
305   s->native_fd = new_socket;
306   s->localport = localport;
307   s->address = ::java::net::InetAddress::getByAddress (raddr);
308   s->port = rport;
309   return;
310
311  error:
312   char* strerr = strerror (errno);
313   throw_on_sock_closed (this);
314   throw new ::java::io::IOException (JvNewStringUTF (strerr));
315 }
316
317 // Close(shutdown) the socket.
318 void
319 gnu::java::net::PlainSocketImpl::close()
320 {
321   // Avoid races from asynchronous finalization.
322   JvSynchronize sync (this);
323
324   // Should we use shutdown here? Yes.
325   // How would that effect so_linger? Uncertain.
326   ::shutdown (native_fd, 2);
327   // Ignore errors in shutdown as we are closing and all the same
328   // errors are handled in the close.
329   int res = _Jv_close (native_fd);
330
331   if (res == -1)
332     {
333       // These three errors are not errors according to tests performed
334       // on the reference implementation.
335       if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF)
336         throw new ::java::io::IOException  (JvNewStringUTF (strerror (errno)));
337     }
338   // Safe place to reset the file pointer.
339   native_fd = -1;
340   timeout = 0;
341 }
342
343 static void
344 write_helper (jint native_fd, jbyte *bytes, jint len);
345
346 // Write a byte to the socket.
347 void
348 gnu::java::net::PlainSocketImpl$SocketOutputStream::write(jint b)
349 {
350   jbyte data = (jbyte) b;
351   write_helper (this$0->native_fd, &data, 1);
352 }
353
354 // Write some bytes to the socket.
355 void
356 gnu::java::net::PlainSocketImpl$SocketOutputStream::write(jbyteArray b, jint offset, jint len)
357 {
358   if (! b)
359     throw new ::java::lang::NullPointerException;
360   if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b))
361     throw new ::java::lang::ArrayIndexOutOfBoundsException;
362
363   write_helper (this$0->native_fd, elements (b) + offset, len);
364 }
365
366 static void
367 write_helper(jint native_fd, jbyte *bytes, jint len)
368 {
369   int written = 0;
370
371   while (len > 0)
372     {
373       int r = _Jv_write (native_fd, bytes, len);
374
375       if (r == -1)
376         {
377           if (::java::lang::Thread::interrupted())
378             {
379               ::java::io::InterruptedIOException *iioe
380                 = new ::java::io::InterruptedIOException
381                 (JvNewStringLatin1 (strerror (errno)));
382               iioe->bytesTransferred = written;
383               throw iioe;
384             }
385           // Some errors should not cause exceptions.
386           if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF)
387             throw new ::java::io::IOException (JvNewStringUTF (strerror (errno)));
388           break;
389         }
390
391       written += r;
392       len -= r;
393       bytes += r;
394     }
395 }
396
397 void
398 gnu::java::net::PlainSocketImpl::sendUrgentData (jint)
399 {
400   throw new ::java::net::SocketException (JvNewStringLatin1 (
401     "PlainSocketImpl: sending of urgent data not supported by this socket"));
402 }
403
404 static jint
405 read_helper (gnu::java::net::PlainSocketImpl *soc_impl,
406              jbyte *bytes, jint count);
407
408 // Read a single byte from the socket.
409 jint
410 gnu::java::net::PlainSocketImpl$SocketInputStream::read(void)
411 {
412   jbyte data;
413
414   if (read_helper (this$0, &data, 1) == 1)
415     return data & 0xFF;
416
417   return -1;
418 }
419
420 // Read count bytes into the buffer, starting at offset.
421 jint
422 gnu::java::net::PlainSocketImpl$SocketInputStream::read(jbyteArray buffer,
423                                                         jint offset, 
424                                                         jint count)
425 {
426  if (! buffer)
427     throw new ::java::lang::NullPointerException;
428
429   jsize bsize = JvGetArrayLength (buffer);
430
431   if (offset < 0 || count < 0 || offset + count > bsize)
432     throw new ::java::lang::ArrayIndexOutOfBoundsException;
433
434   return read_helper (this$0, elements (buffer) + offset, count);
435 }
436
437 static jint
438 read_helper (gnu::java::net::PlainSocketImpl *soc_impl,
439              jbyte *bytes, jint count)
440 {
441   // If zero bytes were requested, short circuit so that recv
442   // doesn't signal EOF.
443   if (count == 0)
444     return 0;
445     
446   // Do timeouts via select.
447   if (soc_impl->timeout > 0
448       && soc_impl->native_fd >= 0
449       && soc_impl->native_fd < FD_SETSIZE)
450     {
451       // Create the file descriptor set.
452       fd_set read_fds;
453       FD_ZERO (&read_fds);
454       FD_SET (soc_impl->native_fd, &read_fds);
455       // Create the timeout struct based on our internal timeout value.
456       struct timeval timeout_value;
457       timeout_value.tv_sec = soc_impl->timeout / 1000;
458       timeout_value.tv_usec =(soc_impl->timeout % 1000) * 1000;
459       // Select on the fds.
460       int sel_retval =
461         _Jv_select (soc_impl->native_fd + 1,
462                     &read_fds, NULL, NULL, &timeout_value);
463       // We're only interested in the 0 return.
464       // error returns still require us to try to read 
465       // the socket to see what happened.
466       if (sel_retval == 0)
467         {
468           ::java::net::SocketTimeoutException *timeoutException =
469             new ::java::net::SocketTimeoutException
470             (JvNewStringUTF ("Read timed out"));
471           throw timeoutException;
472         }
473     }
474
475   // Read the socket.
476   int r = ::recv (soc_impl->native_fd, (char *) bytes, count, 0);
477
478   if (r == 0)
479     {
480       throw_on_sock_closed (soc_impl);
481       return -1;
482     }
483
484   if (::java::lang::Thread::interrupted())
485     {
486       ::java::io::InterruptedIOException *iioe =
487         new ::java::io::InterruptedIOException
488         (JvNewStringUTF ("Read interrupted"));
489       iioe->bytesTransferred = r == -1 ? 0 : r;
490       throw iioe;
491     }
492   else if (r == -1)
493     {
494       throw_on_sock_closed (soc_impl);
495       // Some errors cause us to return end of stream...
496       if (errno == ENOTCONN)
497         return -1;
498
499       // Other errors need to be signalled.
500       throw new ::java::io::IOException (JvNewStringUTF (strerror (errno)));
501     }
502
503   return r;
504 }
505
506 // How many bytes are available?
507 jint
508 gnu::java::net::PlainSocketImpl::available(void)
509 {
510 #if defined(FIONREAD) || defined(HAVE_SELECT)
511   int num = 0;
512   int r = 0;
513   bool num_set = false;
514
515 #if defined(FIONREAD)
516   r = ::ioctl (native_fd, FIONREAD, &num);
517
518   if (r == -1 && errno == ENOTTY)
519     {
520       // If the ioctl doesn't work, we don't care.
521       r = 0;
522       num = 0;
523     }
524   else
525     num_set = true;
526 #elif defined(HAVE_SELECT)
527   if (native_fd < 0)
528     {
529       errno = EBADF;
530       r = -1;
531     }
532 #endif
533
534   if (r == -1)
535     {
536     posix_error:
537       throw new ::java::io::IOException(JvNewStringUTF(strerror(errno)));
538     }
539
540   // If we didn't get anything we can use select.
541
542 #if defined(HAVE_SELECT)
543   if (! num_set)
544     if (! num_set && native_fd >= 0 && native_fd < FD_SETSIZE)
545       {
546         fd_set rd;
547         FD_ZERO (&rd);
548         FD_SET (native_fd, &rd);
549         struct timeval tv;
550         tv.tv_sec = 0;
551         tv.tv_usec = 0;
552         r = _Jv_select (native_fd + 1, &rd, NULL, NULL, &tv);
553         if(r == -1)
554           goto posix_error;
555         num = r == 0 ? 0 : 1;
556       }
557 #endif /* HAVE_SELECT */
558
559   return (jint) num;
560 #else
561   throw new ::java::io::IOException (JvNewStringUTF ("unimplemented"));
562 #endif
563 }
564
565 void
566 gnu::java::net::PlainSocketImpl::setOption (jint optID, ::java::lang::Object *value)
567 {
568   int val;
569   socklen_t val_len = sizeof (val);
570
571   if (native_fd < 0)
572     throw new ::java::net::SocketException (JvNewStringUTF ("Socket closed"));
573
574   if (_Jv_IsInstanceOf (value, &::java::lang::Boolean::class$))
575     {
576       ::java::lang::Boolean *boolobj = 
577         static_cast< ::java::lang::Boolean *> (value);
578       if (boolobj->booleanValue())
579         val = 1; 
580       else 
581         {
582           if (optID == _Jv_SO_LINGER_)
583             val = -1;
584           else
585             val = 0;
586         }
587     }
588   else if (_Jv_IsInstanceOf (value, &::java::lang::Integer::class$))
589     {
590       ::java::lang::Integer *intobj = 
591         static_cast< ::java::lang::Integer *> (value);          
592       val = (int) intobj->intValue();
593     }
594   else
595     {
596       throw new ::java::lang::IllegalArgumentException (
597         JvNewStringLatin1 ("`value' must be Boolean or Integer"));
598     }
599
600   switch (optID) 
601     {
602       case _Jv_TCP_NODELAY_ :
603 #ifdef TCP_NODELAY
604         if (::setsockopt (native_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &val,
605                           val_len) != 0)
606           goto error;
607 #else
608         throw new ::java::lang::InternalError
609           (JvNewStringUTF ("TCP_NODELAY not supported"));
610 #endif /* TCP_NODELAY */
611         return;
612
613       case _Jv_SO_KEEPALIVE_ :
614         if (::setsockopt (native_fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &val,
615                           val_len) != 0)
616           goto error;
617         return;
618       
619       case _Jv_SO_BROADCAST_ :
620         throw new ::java::net::SocketException
621           (JvNewStringUTF ("SO_BROADCAST not valid for TCP"));
622         return;
623         
624       case _Jv_SO_OOBINLINE_ :
625         if (::setsockopt (native_fd, SOL_SOCKET, SO_OOBINLINE, (char *) &val,
626                           val_len) != 0)
627           goto error;
628         return;
629
630       case _Jv_SO_LINGER_ :
631 #ifdef SO_LINGER
632         struct linger l_val;
633         l_val.l_onoff = (val != -1);
634         l_val.l_linger = val;
635
636         if (::setsockopt (native_fd, SOL_SOCKET, SO_LINGER, (char *) &l_val,
637                           sizeof(l_val)) != 0)
638           goto error;    
639 #else
640         throw new ::java::lang::InternalError (
641           JvNewStringUTF ("SO_LINGER not supported"));
642 #endif /* SO_LINGER */
643         return;
644
645       case _Jv_SO_SNDBUF_ :
646       case _Jv_SO_RCVBUF_ :
647 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
648         int opt;
649         optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
650         if (::setsockopt (native_fd, SOL_SOCKET, opt, (char *) &val, val_len) != 0)
651           goto error;    
652 #else
653         throw new ::java::lang::InternalError (
654           JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
655 #endif 
656         return;
657
658       case _Jv_SO_BINDADDR_ :
659         throw new ::java::net::SocketException (
660           JvNewStringUTF ("SO_BINDADDR: read only option"));
661         return;
662
663       case _Jv_IP_MULTICAST_IF_ :
664         throw new ::java::net::SocketException (
665           JvNewStringUTF ("IP_MULTICAST_IF: not valid for TCP"));
666         return;
667         
668       case _Jv_IP_MULTICAST_IF2_ :
669         throw new ::java::net::SocketException (
670           JvNewStringUTF ("IP_MULTICAST_IF2: not valid for TCP"));
671         return;
672         
673       case _Jv_IP_MULTICAST_LOOP_ :
674         throw new ::java::net::SocketException (
675           JvNewStringUTF ("IP_MULTICAST_LOOP: not valid for TCP"));
676         return;
677         
678       case _Jv_IP_TOS_ :
679         if (::setsockopt (native_fd, SOL_SOCKET, IP_TOS, (char *) &val,
680                           val_len) != 0)
681           goto error;    
682         return;
683         
684       case _Jv_SO_REUSEADDR_ :
685 #if defined(SO_REUSEADDR)
686         if (::setsockopt (native_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
687             val_len) != 0)
688           goto error;
689         return;
690 #else
691         throw new ::java::lang::InternalError (
692           JvNewStringUTF ("SO_REUSEADDR not supported"));
693 #endif 
694
695       case _Jv_SO_TIMEOUT_ :
696         timeout = val;
697         return;
698
699       default :
700         errno = ENOPROTOOPT;
701     }
702
703  error:
704   char* strerr = strerror (errno);
705   throw new ::java::net::SocketException (JvNewStringUTF (strerr));
706 }
707
708 ::java::lang::Object *
709 gnu::java::net::PlainSocketImpl::getOption (jint optID)
710 {
711   int val;
712   socklen_t val_len = sizeof(val);
713   union SockAddr u;
714   socklen_t addrlen = sizeof(u);
715   struct linger l_val;
716   socklen_t l_val_len = sizeof(l_val);
717
718   switch (optID)
719     {
720 #ifdef TCP_NODELAY
721     case _Jv_TCP_NODELAY_ :
722       if (::getsockopt (native_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &val,
723                         &val_len) != 0)
724         goto error;
725       else
726         return new ::java::lang::Boolean (val != 0);
727 #else
728       throw new ::java::lang::InternalError
729         (JvNewStringUTF ("TCP_NODELAY not supported"));
730 #endif       
731       break;
732       
733     case _Jv_SO_LINGER_ :
734 #ifdef SO_LINGER
735       if (::getsockopt (native_fd, SOL_SOCKET, SO_LINGER, (char *) &l_val,
736                         &l_val_len) != 0)
737         goto error;    
738  
739       if (l_val.l_onoff)
740         return new ::java::lang::Integer (l_val.l_linger);
741       else
742         return new ::java::lang::Boolean ((jboolean)false);
743 #else
744       throw new ::java::lang::InternalError
745         (JvNewStringUTF ("SO_LINGER not supported"));
746 #endif
747       break;    
748
749     case _Jv_SO_KEEPALIVE_ :
750       if (::getsockopt (native_fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &val,
751                         &val_len) != 0)
752         goto error;
753       else
754         return new ::java::lang::Boolean (val != 0);
755
756     case _Jv_SO_BROADCAST_ :
757       if (::getsockopt (native_fd, SOL_SOCKET, SO_BROADCAST, (char *) &val,
758                         &val_len) != 0)
759         goto error;    
760       return new ::java::lang::Boolean ((jboolean)val);
761         
762     case _Jv_SO_OOBINLINE_ :
763       if (::getsockopt (native_fd, SOL_SOCKET, SO_OOBINLINE, (char *) &val,
764                         &val_len) != 0)
765         goto error;    
766       return new ::java::lang::Boolean ((jboolean)val);
767         
768     case _Jv_SO_RCVBUF_ :
769     case _Jv_SO_SNDBUF_ :
770 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
771       int opt;
772       optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
773       if (::getsockopt (native_fd, SOL_SOCKET, opt, (char *) &val, &val_len) != 0)
774         goto error;    
775       else
776         return new ::java::lang::Integer (val);
777 #else
778       throw new ::java::lang::InternalError
779         (JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
780 #endif    
781       break;
782     case _Jv_SO_BINDADDR_:
783       // cache the local address 
784       if (localAddress == NULL)
785         {
786           jbyteArray laddr;
787
788           if (::getsockname (native_fd, (sockaddr*) &u, &addrlen) != 0)
789             goto error;
790
791           if (u.address.sin_family == AF_INET)
792             {
793               laddr = JvNewByteArray (4);
794               memcpy (elements (laddr), &u.address.sin_addr, 4);
795             }
796 #ifdef HAVE_INET6
797           else if (u.address.sin_family == AF_INET6)
798             {
799               laddr = JvNewByteArray (16);
800               memcpy (elements (laddr), &u.address6.sin6_addr, 16);
801             }
802 #endif
803           else
804             throw new ::java::net::SocketException
805               (JvNewStringUTF ("invalid family"));
806           localAddress = ::java::net::InetAddress::getByAddress (laddr);
807         }
808
809       return localAddress;
810       break;
811     case _Jv_IP_MULTICAST_IF_ :
812       throw new ::java::net::SocketException
813         (JvNewStringUTF ("IP_MULTICAST_IF: not valid for TCP"));
814       break;
815         
816     case _Jv_IP_MULTICAST_IF2_ :
817       throw new ::java::net::SocketException
818         (JvNewStringUTF ("IP_MULTICAST_IF2: not valid for TCP"));
819       break;
820         
821     case _Jv_IP_MULTICAST_LOOP_ :
822       throw new ::java::net::SocketException
823         (JvNewStringUTF ("IP_MULTICAST_LOOP: not valid for TCP"));
824       break;
825         
826     case _Jv_IP_TOS_ :
827       if (::getsockopt (native_fd, SOL_SOCKET, IP_TOS, (char *) &val,
828                         &val_len) != 0)
829         goto error;
830       return new ::java::lang::Integer (val);
831       break;
832         
833     case _Jv_SO_REUSEADDR_ :
834 #if defined(SO_REUSEADDR)
835       if (::getsockopt (native_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
836                         &val_len) != 0)
837         goto error;    
838 #else
839         throw new ::java::lang::InternalError (
840           JvNewStringUTF ("SO_REUSEADDR not supported"));
841 #endif 
842       break;
843
844     case _Jv_SO_TIMEOUT_ :
845       return new ::java::lang::Integer (timeout);
846       break;
847
848     default :
849       errno = ENOPROTOOPT;
850     }
851
852  error:
853   char* strerr = strerror (errno);
854   throw new ::java::net::SocketException (JvNewStringUTF (strerr));
855 }
856
857 void
858 gnu::java::net::PlainSocketImpl::shutdownInput (void)
859 {
860   if (::shutdown (native_fd, 0))
861     throw new ::java::net::SocketException (JvNewStringUTF (strerror (errno)));
862 }
863
864 void
865 gnu::java::net::PlainSocketImpl::shutdownOutput (void)
866 {
867   if (::shutdown (native_fd, 1))
868     throw new ::java::net::SocketException (JvNewStringUTF (strerror (errno)));
869 }