OSDN Git Service

628ac620f9a44a49a3c4f3502f7bfed896f11c15
[pf3gnuchains/gcc-fork.git] / libjava / java / net / natPlainDatagramSocketImpl.cc
1 /* Copyright (C) 1999, 2000  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
11 #include<platform.h>
12
13 #ifdef WIN32
14 #include <errno.h>
15 #include <string.h>
16 #ifndef ENOPROTOOPT
17 #define ENOPROTOOPT 109
18 #endif
19 #else /* WIN32 */
20 #ifdef HAVE_SYS_SOCKET_H
21 #include <sys/socket.h>
22 #endif
23 #ifdef HAVE_NETINET_IN_H
24 #include <netinet/in.h>
25 #endif
26 #ifdef HAVE_ARPA_INET_H
27 #include <arpa/inet.h>
28 #endif
29 #include <errno.h>
30 #include <string.h>
31 #endif /* WIN32 */
32
33 #if HAVE_BSTRING_H
34 // Needed for bzero, implicitly used by FD_ZERO on IRIX 5.2 
35 #include <bstring.h>
36 #endif
37
38 #ifndef DISABLE_JAVA_NET
39 // Avoid macro definitions of bind from system headers, e.g. on
40 // Solaris 7 with _XOPEN_SOURCE.  FIXME
41 static inline int
42 _Jv_bind (int fd, struct sockaddr *addr, int addrlen)
43 {
44   return ::bind (fd, addr, addrlen);
45 }
46 #endif /* DISABLE_JAVA_NET */
47
48 #ifdef bind
49 #undef bind
50 #endif
51
52 #include <gcj/cni.h>
53 #include <java/io/IOException.h>
54 #include <java/io/FileDescriptor.h>
55 #include <java/io/InterruptedIOException.h>
56 #include <java/net/BindException.h>
57 #include <java/net/SocketException.h>
58 #include <java/net/PlainDatagramSocketImpl.h>
59 #include <java/net/InetAddress.h>
60 #include <java/net/DatagramPacket.h>
61 #include <java/lang/InternalError.h>
62 #include <java/lang/Object.h>
63 #include <java/lang/Boolean.h>
64 #include <java/lang/Integer.h>
65
66 // FIXME: remove these
67 #define BooleanClass java::lang::Boolean::class$
68 #define IntegerClass java::lang::Integer::class$
69
70 #ifdef DISABLE_JAVA_NET
71
72 void
73 java::net::PlainDatagramSocketImpl::create ()
74 {
75   throw new SocketException (
76     JvNewStringLatin1 ("DatagramSocketImpl.create: unimplemented"));
77 }
78
79 void
80 java::net::PlainDatagramSocketImpl::bind (jint, java::net::InetAddress *)
81 {
82   throw new BindException (
83     JvNewStringLatin1 ("DatagramSocketImpl.bind: unimplemented"));
84 }
85
86 jint
87 java::net::PlainDatagramSocketImpl::peek (java::net::InetAddress *)
88 {
89   throw new java::io::IOException (
90     JvNewStringLatin1 ("DatagramSocketImpl.peek: unimplemented"));
91 }
92
93 void
94 java::net::PlainDatagramSocketImpl::send (java::net::DatagramPacket *)
95 {
96   throw new java::io::IOException (
97     JvNewStringLatin1 ("DatagramSocketImpl.send: unimplemented"));
98 }
99
100 void
101 java::net::PlainDatagramSocketImpl::receive (java::net::DatagramPacket *)
102 {
103   throw new java::io::IOException (
104     JvNewStringLatin1 ("DatagramSocketImpl.receive: unimplemented"));
105 }
106
107 void
108 java::net::PlainDatagramSocketImpl::setTimeToLive (jint)
109 {
110   throw new java::io::IOException (
111     JvNewStringLatin1 ("DatagramSocketImpl.setTimeToLive: unimplemented"));
112 }
113
114 jint
115 java::net::PlainDatagramSocketImpl::getTimeToLive ()
116 {
117   throw new java::io::IOException (
118     JvNewStringLatin1 ("DatagramSocketImpl.getTimeToLive: unimplemented"));
119 }
120
121 void
122 java::net::PlainDatagramSocketImpl::mcastGrp (java::net::InetAddress *,
123                                               jboolean)
124 {
125   throw new java::io::IOException (
126     JvNewStringLatin1 ("DatagramSocketImpl.mcastGrp: unimplemented"));
127 }
128
129 void
130 java::net::PlainDatagramSocketImpl::setOption (jint, java::lang::Object *)
131 {
132   throw new SocketException (
133     JvNewStringLatin1 ("DatagramSocketImpl.setOption: unimplemented"));
134 }
135
136 java::lang::Object *
137 java::net::PlainDatagramSocketImpl::getOption (jint)
138 {
139   throw new SocketException (
140     JvNewStringLatin1 ("DatagramSocketImpl.getOption: unimplemented"));
141 }
142
143 #else /* DISABLE_JAVA_NET */
144
145 #ifndef HAVE_SOCKLEN_T
146 typedef int socklen_t;
147 #endif
148
149 union SockAddr
150 {
151   struct sockaddr_in address;
152 #ifdef HAVE_INET6
153   struct sockaddr_in6 address6;
154 #endif
155 };
156
157 union McastReq
158 {
159 #if HAVE_STRUCT_IP_MREQ
160   struct ip_mreq mreq;
161 #endif
162 #if HAVE_STRUCT_IPV6_MREQ
163   struct ipv6_mreq mreq6;
164 #endif
165 };
166
167 union InAddr
168 {
169   struct in_addr addr;
170 #ifdef HAVE_INET6
171   struct in6_addr addr6;
172 #endif
173 };
174
175
176 // FIXME: routines here and/or in natPlainSocketImpl.cc could throw
177 // NoRouteToHostException; also consider UnknownHostException, ConnectException.
178
179 void
180 java::net::PlainDatagramSocketImpl::create ()
181 {
182   int sock = ::socket (AF_INET, SOCK_DGRAM, 0);
183   if (sock < 0)
184     {
185       char* strerr = strerror (errno);
186       throw new java::net::SocketException (JvNewStringUTF (strerr));
187     }
188   fnum = sock;
189   fd = new java::io::FileDescriptor (sock);
190 }
191
192 void
193 java::net::PlainDatagramSocketImpl::bind (jint lport,
194                                           java::net::InetAddress *host)
195 {
196   union SockAddr u;
197   struct sockaddr *ptr = (struct sockaddr *) &u.address;
198   // FIXME: Use getaddrinfo() to get actual protocol instead of assuming ipv4.
199   jbyteArray haddress = host->addr;
200   jbyte *bytes = elements (haddress);
201   int len = haddress->length;
202
203   if (len == 4)
204     {
205       u.address.sin_family = AF_INET;
206       if (host != NULL)
207         memcpy (&u.address.sin_addr, bytes, len);
208       else
209         u.address.sin_addr.s_addr = htonl (INADDR_ANY);
210       len = sizeof (struct sockaddr_in);
211       u.address.sin_port = htons (lport);
212     }
213 #ifdef HAVE_INET6
214   else if (len == 16)
215     {
216       u.address6.sin6_family = AF_INET6;
217       memcpy (&u.address6.sin6_addr, bytes, len);
218       len = sizeof (struct sockaddr_in6);
219       u.address6.sin6_port = htons (lport);
220     }
221 #endif
222   else
223     throw new java::net::SocketException (JvNewStringUTF ("invalid length"));
224
225   if (_Jv_bind (fnum, ptr, len) == 0)
226     {
227       socklen_t addrlen = sizeof(u);
228       if (lport != 0)
229         localPort = lport;
230       else if (::getsockname (fnum, (sockaddr*) &u, &addrlen) == 0)
231         localPort = ntohs (u.address.sin_port);
232       else
233         goto error;
234       /* Allow broadcast by default. */
235       int broadcast = 1;
236       if (::setsockopt (fnum, SOL_SOCKET, SO_BROADCAST, (char *) &broadcast, 
237                         sizeof (broadcast)) != 0)
238         goto error;
239       return;
240     }
241  error:
242   char* strerr = strerror (errno);
243   throw new java::net::BindException (JvNewStringUTF (strerr));
244 }
245
246 jint
247 java::net::PlainDatagramSocketImpl::peek (java::net::InetAddress *i)
248 {
249   // FIXME: Deal with Multicast and if the socket is connected.
250   union SockAddr u;
251   socklen_t addrlen = sizeof(u);
252   ssize_t retlen =
253     ::recvfrom (fnum, (char *) NULL, 0, MSG_PEEK, (sockaddr*) &u,
254       &addrlen);
255   if (retlen < 0)
256     goto error;
257   // FIXME: Deal with Multicast addressing and if the socket is connected.
258   jbyteArray raddr;
259   jint rport;
260   if (u.address.sin_family == AF_INET)
261     {
262       raddr = JvNewByteArray (4);
263       memcpy (elements (raddr), &u.address.sin_addr, 4);
264       rport = ntohs (u.address.sin_port);
265     }
266 #ifdef HAVE_INET6
267   else if (u.address.sin_family == AF_INET6)
268     {
269       raddr = JvNewByteArray (16);
270       memcpy (elements (raddr), &u.address6.sin6_addr, 16);
271       rport = ntohs (u.address6.sin6_port);
272     }
273 #endif
274   else
275     throw new java::net::SocketException (JvNewStringUTF ("invalid family"));
276
277   i->addr = raddr;
278   return rport;
279  error:
280   char* strerr = strerror (errno);
281   throw new java::io::IOException (JvNewStringUTF (strerr));
282 }
283
284 void
285 java::net::PlainDatagramSocketImpl::send (java::net::DatagramPacket *p)
286 {
287   // FIXME: Deal with Multicast and if the socket is connected.
288   jint rport = p->getPort();
289   union SockAddr u;
290   jbyteArray haddress = p->getAddress()->addr;
291   jbyte *bytes = elements (haddress);
292   int len = haddress->length;
293   struct sockaddr *ptr = (struct sockaddr *) &u.address;
294   jbyte *dbytes = elements (p->getData());
295   if (len == 4)
296     {
297       u.address.sin_family = AF_INET;
298       memcpy (&u.address.sin_addr, bytes, len);
299       len = sizeof (struct sockaddr_in);
300       u.address.sin_port = htons (rport);
301     }
302 #ifdef HAVE_INET6
303   else if (len == 16)
304     {
305       u.address6.sin6_family = AF_INET6;
306       memcpy (&u.address6.sin6_addr, bytes, len);
307       len = sizeof (struct sockaddr_in6);
308       u.address6.sin6_port = htons (rport);
309     }
310 #endif
311   else
312     throw new java::net::SocketException (JvNewStringUTF ("invalid length"));
313
314   if (::sendto (fnum, (char *) dbytes, p->getLength(), 0, ptr, len) >= 0)
315     return;
316
317   char* strerr = strerror (errno);
318   throw new java::io::IOException (JvNewStringUTF (strerr));
319 }
320
321 void
322 java::net::PlainDatagramSocketImpl::receive (java::net::DatagramPacket *p)
323 {
324   // FIXME: Deal with Multicast and if the socket is connected.
325   union SockAddr u;
326   socklen_t addrlen = sizeof(u);
327   jbyte *dbytes = elements (p->getData());
328   ssize_t retlen = 0;
329
330 // FIXME: implement timeout support for Win32
331 #ifndef WIN32
332   // Do timeouts via select since SO_RCVTIMEO is not always available.
333   if (timeout > 0)
334     {
335       fd_set rset;
336       struct timeval tv;
337       FD_ZERO(&rset);
338       FD_SET(fnum, &rset);
339       tv.tv_sec = timeout / 1000;
340       tv.tv_usec = (timeout % 1000) * 1000;
341       int retval;
342       if ((retval = _Jv_select (fnum + 1, &rset, NULL, NULL, &tv)) < 0)
343         goto error;
344       else if (retval == 0)
345         throw new java::io::InterruptedIOException ();
346     }
347 #endif /* WIN32 */
348
349   retlen =
350     ::recvfrom (fnum, (char *) dbytes, p->getLength(), 0, (sockaddr*) &u,
351       &addrlen);
352   if (retlen < 0)
353     goto error;
354   // FIXME: Deal with Multicast addressing and if the socket is connected.
355   jbyteArray raddr;
356   jint rport;
357   if (u.address.sin_family == AF_INET)
358     {
359       raddr = JvNewByteArray (4);
360       memcpy (elements (raddr), &u.address.sin_addr, 4);
361       rport = ntohs (u.address.sin_port);
362     }
363 #ifdef HAVE_INET6
364   else if (u.address.sin_family == AF_INET6)
365     {
366       raddr = JvNewByteArray (16);
367       memcpy (elements (raddr), &u.address6.sin6_addr, 16);
368       rport = ntohs (u.address6.sin6_port);
369     }
370 #endif
371   else
372     throw new java::net::SocketException (JvNewStringUTF ("invalid family"));
373
374   p->setAddress (new InetAddress (raddr, NULL));
375   p->setPort (rport);
376   p->setLength ((jint) retlen);
377   return;
378  error:
379   char* strerr = strerror (errno);
380   throw new java::io::IOException (JvNewStringUTF (strerr));
381 }
382
383 void
384 java::net::PlainDatagramSocketImpl::setTimeToLive (jint ttl)
385 {
386   // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4.
387   char val = (char) ttl;
388   socklen_t val_len = sizeof(val);
389   if (::setsockopt (fnum, IPPROTO_IP, IP_MULTICAST_TTL, &val, val_len) == 0)
390     return;
391
392   char* strerr = strerror (errno);
393   throw new java::io::IOException (JvNewStringUTF (strerr));
394 }
395
396 jint
397 java::net::PlainDatagramSocketImpl::getTimeToLive ()
398 {
399   // Assumes IPPROTO_IP rather than IPPROTO_IPV6 since socket created is IPv4.
400   char val;
401   socklen_t val_len = sizeof(val);
402   if (::getsockopt (fnum, IPPROTO_IP, IP_MULTICAST_TTL, &val, &val_len) == 0)
403     return ((int) val) & 0xFF;
404
405   char* strerr = strerror (errno);
406   throw new java::io::IOException (JvNewStringUTF (strerr));
407 }
408
409 void
410 java::net::PlainDatagramSocketImpl::mcastGrp (java::net::InetAddress *inetaddr,
411                                               jboolean join)
412 {
413   union McastReq u;
414   jbyteArray haddress = inetaddr->addr;
415   jbyte *bytes = elements (haddress);
416   int len = haddress->length;
417   int level, opname;
418   const char *ptr;
419   if (0)
420     ;
421 #if HAVE_STRUCT_IP_MREQ
422   else if (len == 4)
423     {
424       level = IPPROTO_IP;
425       opname = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
426       memcpy (&u.mreq.imr_multiaddr, bytes, len);
427       // FIXME:  If a non-default interface is set, use it; see Stevens p. 501.
428       // Maybe not, see note in last paragraph at bottom of Stevens p. 497.
429       u.mreq.imr_interface.s_addr = htonl (INADDR_ANY); 
430       len = sizeof (struct ip_mreq);
431       ptr = (const char *) &u.mreq;
432     }
433 #endif
434 #if HAVE_STRUCT_IPV6_MREQ
435   else if (len == 16)
436     {
437       level = IPPROTO_IPV6;
438
439       /* Prefer new RFC 2553 names.  */
440 #ifndef IPV6_JOIN_GROUP
441 #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
442 #endif
443 #ifndef IPV6_LEAVE_GROUP
444 #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
445 #endif
446
447       opname = join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP;
448       memcpy (&u.mreq6.ipv6mr_multiaddr, bytes, len);
449       // FIXME:  If a non-default interface is set, use it; see Stevens p. 501.
450       // Maybe not, see note in last paragraph at bottom of Stevens p. 497.
451       u.mreq6.ipv6mr_interface = 0;
452       len = sizeof (struct ipv6_mreq);
453       ptr = (const char *) &u.mreq6;
454     }
455 #endif
456   else
457     throw new java::net::SocketException (JvNewStringUTF ("invalid length"));
458
459   if (::setsockopt (fnum, level, opname, ptr, len) == 0)
460     return;
461
462   char* strerr = strerror (errno);
463   throw new java::io::IOException (JvNewStringUTF (strerr));
464 }
465
466 void
467 java::net::PlainDatagramSocketImpl::setOption (jint optID,
468                                                java::lang::Object *value)
469 {
470   int val;
471   socklen_t val_len = sizeof (val);
472
473   if (_Jv_IsInstanceOf (value, &BooleanClass))
474     {
475       java::lang::Boolean *boolobj = 
476         static_cast<java::lang::Boolean *> (value);
477       val = boolobj->booleanValue() ? 1 : 0;
478     }
479   else if (_Jv_IsInstanceOf (value, &IntegerClass))
480     {
481       java::lang::Integer *intobj = 
482         static_cast<java::lang::Integer *> (value);          
483       val = (int) intobj->intValue();
484     }
485   // Else assume value to be an InetAddress for use with IP_MULTICAST_IF.
486
487   switch (optID) 
488     {
489       case _Jv_TCP_NODELAY_ :
490         throw new java::net::SocketException (
491           JvNewStringUTF ("TCP_NODELAY not valid for UDP"));
492         return;
493       case _Jv_SO_LINGER_ :
494         throw new java::net::SocketException (
495           JvNewStringUTF ("SO_LINGER not valid for UDP"));
496         return;
497       case _Jv_SO_SNDBUF_ :
498       case _Jv_SO_RCVBUF_ :
499 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
500         int opt;
501         optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
502         if (::setsockopt (fnum, SOL_SOCKET, opt, (char *) &val, val_len) != 0)
503           goto error;    
504 #else
505         throw new java::lang::InternalError (
506           JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
507 #endif 
508         return;
509       case _Jv_SO_REUSEADDR_ :
510 #if defined(SO_REUSEADDR)
511         if (::setsockopt (fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
512             val_len) != 0)
513           goto error;
514 #else
515         throw new java::lang::InternalError (
516           JvNewStringUTF ("SO_REUSEADDR not supported"));
517 #endif 
518         return;
519       case _Jv_SO_BINDADDR_ :
520         throw new java::net::SocketException (
521           JvNewStringUTF ("SO_BINDADDR: read only option"));
522         return;
523       case _Jv_IP_MULTICAST_IF_ :
524         union InAddr u;
525         jbyteArray haddress;
526         jbyte *bytes;
527         int len;
528         int level, opname;
529         const char *ptr;
530
531         haddress = ((java::net::InetAddress *) value)->addr;
532         bytes = elements (haddress);
533         len = haddress->length;
534         if (len == 4)
535           {
536             level = IPPROTO_IP;
537             opname = IP_MULTICAST_IF;
538             memcpy (&u.addr, bytes, len);
539             len = sizeof (struct in_addr);
540             ptr = (const char *) &u.addr;
541           }
542 // Tru64 UNIX V5.0 has struct sockaddr_in6, but no IPV6_MULTICAST_IF
543 #if defined (HAVE_INET6) && defined (IPV6_MULTICAST_IF)
544         else if (len == 16)
545           {
546             level = IPPROTO_IPV6;
547             opname = IPV6_MULTICAST_IF;
548             memcpy (&u.addr6, bytes, len);
549             len = sizeof (struct in6_addr);
550             ptr = (const char *) &u.addr6;
551           }
552 #endif
553         else
554           throw
555             new java::net::SocketException (JvNewStringUTF ("invalid length"));
556
557         if (::setsockopt (fnum, level, opname, ptr, len) != 0)
558           goto error;
559         return;
560       case _Jv_SO_TIMEOUT_ :
561         timeout = val;
562         return;
563       default :
564         errno = ENOPROTOOPT;
565     }
566
567  error:
568   char* strerr = strerror (errno);
569   throw new java::net::SocketException (JvNewStringUTF (strerr));
570 }
571
572 java::lang::Object *
573 java::net::PlainDatagramSocketImpl::getOption (jint optID)
574 {
575   int val;
576   socklen_t val_len = sizeof(val);
577   union SockAddr u;
578   socklen_t addrlen = sizeof(u);
579
580   switch (optID)
581     {
582       case _Jv_TCP_NODELAY_ :
583         throw new java::net::SocketException (
584           JvNewStringUTF ("TCP_NODELAY not valid for UDP"));
585         break;
586
587       case _Jv_SO_LINGER_ :
588         throw new java::net::SocketException (
589           JvNewStringUTF ("SO_LINGER not valid for UDP"));
590         break;    
591       case _Jv_SO_RCVBUF_ :
592       case _Jv_SO_SNDBUF_ :
593 #if defined(SO_SNDBUF) && defined(SO_RCVBUF)
594         int opt;
595         optID == _Jv_SO_SNDBUF_ ? opt = SO_SNDBUF : opt = SO_RCVBUF;
596         if (::getsockopt (fnum, SOL_SOCKET, opt, (char *) &val, &val_len) != 0)
597           goto error;    
598         else
599           return new java::lang::Integer (val);
600 #else
601         throw new java::lang::InternalError (
602           JvNewStringUTF ("SO_RCVBUF/SO_SNDBUF not supported"));
603 #endif    
604         break;
605       case _Jv_SO_BINDADDR_:
606         // cache the local address
607         if (localAddress == NULL)
608           {     
609             jbyteArray laddr;
610             if (::getsockname (fnum, (sockaddr*) &u, &addrlen) != 0)
611               goto error;
612             if (u.address.sin_family == AF_INET)
613               {
614                 laddr = JvNewByteArray (4);
615                 memcpy (elements (laddr), &u.address.sin_addr, 4);
616               }
617 #ifdef HAVE_INET6
618             else if (u.address.sin_family == AF_INET6)
619               {
620                 laddr = JvNewByteArray (16);
621                 memcpy (elements (laddr), &u.address6.sin6_addr, 16);
622               }
623 #endif
624             else
625               throw new java::net::SocketException (JvNewStringUTF ("invalid family"));
626             localAddress = new java::net::InetAddress (laddr, NULL);
627           }
628         return localAddress;  
629         break;
630       case _Jv_SO_REUSEADDR_ :
631 #if defined(SO_REUSEADDR)
632         if (::getsockopt (fnum, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
633             &val_len) != 0)
634           goto error;
635         return new java::lang::Boolean (val != 0);
636 #else
637         throw new java::lang::InternalError (
638           JvNewStringUTF ("SO_REUSEADDR not supported"));
639 #endif 
640         break;
641       case _Jv_IP_MULTICAST_IF_ :
642 #ifdef HAVE_INET_NTOA
643         struct in_addr inaddr;
644         socklen_t inaddr_len;
645         char *bytes;
646
647         inaddr_len = sizeof(inaddr);
648         if (::getsockopt (fnum, IPPROTO_IP, IP_MULTICAST_IF, (char *) &inaddr,
649             &inaddr_len) != 0)
650           goto error;
651
652         bytes = inet_ntoa (inaddr);
653
654         return java::net::InetAddress::getByName (JvNewStringLatin1 (bytes));
655 #else
656         throw new java::net::SocketException (
657           JvNewStringUTF ("IP_MULTICAST_IF: not available - no inet_ntoa()"));
658 #endif
659         break;
660       case _Jv_SO_TIMEOUT_ :
661         return new java::lang::Integer (timeout);
662         break;
663       default :
664         errno = ENOPROTOOPT;
665     }
666
667  error:
668   char* strerr = strerror (errno);
669   throw new java::net::SocketException (JvNewStringUTF (strerr));
670 }
671
672 #endif /* DISABLE_JAVA_NET */