OSDN Git Service

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