OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / native / jni / java-net / gnu_java_net_VMPlainSocketImpl.c
1 /* VMPlainSocketImpl.c - Native methods for PlainSocketImpl class
2    Copyright (C) 2005, 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10  
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif /* HAVE_CONFIG_H */
42
43 #include <config-int.h>
44
45 #include <sys/ioctl.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <sys/time.h>
49 #ifdef HAVE_IFADDRS_H
50 #include <ifaddrs.h>
51 #endif
52 #include <netinet/in.h>
53 #include <netinet/tcp.h>
54 #include <net/if.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <unistd.h>
60  
61 #include <jni.h>
62 #include <jcl.h>
63
64 #include "cpnative.h"
65 #include "cpnet.h"
66 #include "cpio.h"
67 #include "javanet.h"
68
69 #include "gnu_java_net_VMPlainSocketImpl.h"
70
71 #define THROW_NO_NETWORK(env) JCL_ThrowException (env, "java/lang/InternalError", "this platform not configured for network support")
72
73 /*
74  * Class:     gnu_java_net_VMPlainSocketImpl
75  * Method:    bind
76  * Signature: (I[BI)V
77  */
78 JNIEXPORT void JNICALL
79 Java_gnu_java_net_VMPlainSocketImpl_bind (JNIEnv *env,
80                                           jclass clazz __attribute__((unused)),
81                                           jint fd, jbyteArray addr, jint port)
82 {
83   struct sockaddr_in sockaddr;
84   jbyte *elems = NULL;
85   int ret;
86
87   if (addr != NULL)
88     elems = (*env)->GetByteArrayElements (env, addr, NULL);
89
90   memset(&sockaddr, 0, sizeof (struct sockaddr_in));
91   sockaddr.sin_family = AF_INET;
92   sockaddr.sin_port = htons (port);
93   /* addr is already in network byte order. */
94   if (elems != NULL)
95     sockaddr.sin_addr.s_addr = *((uint32_t *) elems);
96   else
97     sockaddr.sin_addr.s_addr = INADDR_ANY;
98
99   /* bind(2) from BSD says bind will never return EINTR */
100   /* bind is not a blocking system call */
101   ret = bind (fd, (struct sockaddr *) &sockaddr, sizeof (struct sockaddr_in));
102
103   if (elems != NULL)
104     (*env)->ReleaseByteArrayElements (env, addr, elems, JNI_ABORT);
105
106   if (-1 == ret)
107     JCL_ThrowException (env, BIND_EXCEPTION, strerror (errno));
108     
109   cpio_closeOnExec(ret);
110 }
111
112
113 /*
114  * Class:     gnu_java_net_VMPlainSocketImpl
115  * Method:    bind6
116  * Signature: (I[BI)V
117  */
118 JNIEXPORT void JNICALL
119 Java_gnu_java_net_VMPlainSocketImpl_bind6 (JNIEnv *env,
120                                            jclass c __attribute__((unused)),
121                                            jint fd, jbyteArray addr, jint port)
122 {
123   /* FIXME! Add check if we have IPv6! */
124   struct sockaddr_in6 sockaddr;
125   jbyte *elems;
126   int ret;
127
128   elems = (*env)->GetByteArrayElements (env, addr, NULL);
129
130   memset (&sockaddr, 0, sizeof (struct sockaddr_in6));
131   sockaddr.sin6_family = AF_INET6;
132   sockaddr.sin6_port = htons (port);
133   memcpy (&sockaddr.sin6_addr.s6_addr, elems, 16);
134
135   /* bind(2) from BSD says bind will never return EINTR */
136   /* bind is not a blocking system call */
137   ret = bind (fd, (struct sockaddr *) &sockaddr,
138               sizeof (struct sockaddr_in6));
139
140   (*env)->ReleaseByteArrayElements (env, addr, elems, JNI_ABORT);
141
142   if (-1 == ret)
143     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
144 }
145
146
147 /*
148  * Class:     gnu_java_net_VMPlainSocketImpl
149  * Method:    listen
150  * Signature: (II)V
151  */
152 JNIEXPORT void JNICALL
153 Java_gnu_java_net_VMPlainSocketImpl_listen (JNIEnv *env,
154                                             jclass c __attribute__((unused)),
155                                             jint fd, jint backlog)
156 {
157   int ret;
158
159   /* listen(2) says that this call will never return EINTR */
160   /* listen is not a blocking system call */
161   if ((ret = listen (fd, backlog)) == -1)
162     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
163 }
164
165
166 /* These constants are also defined in java/net/SocketOptions.java.
167  * Except for CPNET_IP_TTL which is defined in 
168  * vm/reference/gnu/java/net/VMPlainSocketImpl.java .
169  */
170 enum java_sockopt {
171   CPNET_SO_KEEPALIVE = 0x8,
172   CPNET_SO_LINGER = 0x80,
173   CPNET_SO_TIMEOUT = 0x1006,
174   CPNET_SO_BINDADDR = 0x0F,
175   CPNET_SO_SNDBUF = 0x1001,
176   CPNET_SO_RCVBUF = 0x1002,
177   CPNET_SO_REUSEADDR = 0x04,
178   CPNET_SO_BROADCAST = 0x20,
179   CPNET_SO_OOBINLINE = 0x1003,
180   CPNET_TCP_NODELAY = 0x01,
181   CPNET_IP_MULTICAST_IF = 0x10,
182   CPNET_IP_MULTICAST_IF2 = 0x1F,
183   CPNET_IP_MULTICAST_LOOP = 0x12,
184   CPNET_IP_TOS = 0x03,
185   CPNET_IP_TTL = 0x1E61
186 };
187
188
189 /*
190  * Class:     gnu_java_net_VMPlainSocketImpl
191  * Method:    setOption
192  * Signature: (III)V
193  */
194 JNIEXPORT void JNICALL
195 Java_gnu_java_net_VMPlainSocketImpl_setOption (JNIEnv *env,
196                                                jclass c __attribute__((unused)),
197                                                jint fd, jint option, jint value)
198 {
199   enum java_sockopt joption = (enum java_sockopt) option;
200   int optname = -1;
201   int level = SOL_SOCKET;
202   const int _value = value;
203   struct linger _linger;
204   struct timeval _timeo;
205   void *optval = (void *) &_value;
206   socklen_t optlen = sizeof (int);
207   
208   switch (joption)
209     {
210     case CPNET_IP_MULTICAST_LOOP:
211       level = IPPROTO_IP;
212       optname = IP_MULTICAST_LOOP;
213       break;
214
215     case CPNET_SO_KEEPALIVE:
216       optname = SO_KEEPALIVE;
217       break;
218
219     case CPNET_SO_LINGER:
220       optname = SO_LINGER;
221       if (_value == -1)
222         _linger.l_onoff = 0;
223       else
224         _linger.l_onoff = 1;
225       _linger.l_linger = _value;
226       optval = &_linger;
227       optlen = sizeof (struct linger);
228       break;
229
230     case CPNET_SO_TIMEOUT:
231       optname = SO_RCVTIMEO;
232       _timeo.tv_sec = value / 1000;
233       _timeo.tv_usec = (value % 1000) * 1000;
234       optval = &_timeo;
235       optlen = sizeof (struct timeval);
236       break;
237
238     case CPNET_SO_SNDBUF:
239       optname = SO_SNDBUF;
240       break;
241
242     case CPNET_SO_RCVBUF:
243       optname = SO_RCVBUF;
244       break;
245
246     case CPNET_SO_REUSEADDR:
247       optname = SO_REUSEADDR;
248       break;
249
250     case CPNET_SO_BROADCAST:
251       optname = SO_BROADCAST;
252       break;
253
254     case CPNET_SO_OOBINLINE:
255       optname = SO_OOBINLINE;
256       break;
257
258     case CPNET_TCP_NODELAY:
259       level = IPPROTO_TCP;
260       optname = TCP_NODELAY;
261       break;
262
263     case CPNET_IP_TOS:
264       level = IPPROTO_IP;
265       optname = IP_TOS;
266       break;
267
268     case CPNET_IP_TTL:
269       level = IPPROTO_IP;
270       optname = IP_TTL;
271       break;
272
273     case CPNET_SO_BINDADDR:
274     case CPNET_IP_MULTICAST_IF:
275     case CPNET_IP_MULTICAST_IF2:
276       JCL_ThrowException (env, IO_EXCEPTION, "argument not a boolean or integer option");
277       return;
278     }
279
280   if (setsockopt (fd, level, optname, (const void *) optval, optlen) == -1)
281     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
282 }
283
284 /*
285  * Class:     gnu_java_net_VMPlainSocketImpl
286  * Method:    getOption
287  * Signature: (II)I
288  */
289 JNIEXPORT jint JNICALL
290 Java_gnu_java_net_VMPlainSocketImpl_getOption (JNIEnv *env,
291                                                jclass c __attribute__((unused)),
292                                                jint fd, jint option)
293 {
294   enum java_sockopt joption = (enum java_sockopt) option;
295   int optname = -1;
296   int level = SOL_SOCKET;
297   int value;
298   struct linger linger;
299   struct timeval timeo;
300   void *optval = &value;
301   socklen_t optlen = sizeof (int);
302
303   switch (joption)
304     {
305     case CPNET_IP_MULTICAST_LOOP:
306       level = IPPROTO_IP;
307       optname = IP_MULTICAST_LOOP;
308       break;
309
310     case CPNET_SO_KEEPALIVE:
311       optname = SO_KEEPALIVE;
312       break;
313
314     case CPNET_SO_LINGER:
315       optname = SO_LINGER;
316       optval = &linger;
317       optlen = sizeof (struct linger);
318       break;
319
320     case CPNET_SO_TIMEOUT:
321       optname = SO_RCVTIMEO;
322       optval = &timeo;
323       optlen = sizeof (struct timeval);
324       break;
325
326     case CPNET_SO_SNDBUF:
327       optname = SO_SNDBUF;
328       break;
329
330     case CPNET_SO_RCVBUF:
331       optname = SO_RCVBUF;
332       break;
333
334     case CPNET_SO_REUSEADDR:
335       optname = SO_REUSEADDR;
336       break;
337
338     case CPNET_SO_BROADCAST:
339       optname = SO_BROADCAST;
340       break;
341
342     case CPNET_SO_OOBINLINE:
343       optname = SO_OOBINLINE;
344       break;
345
346     case CPNET_TCP_NODELAY:
347       level = IPPROTO_TCP;
348       optname = TCP_NODELAY;
349       break;
350
351     case CPNET_IP_TOS:
352       level = IPPROTO_IP;
353       optname = IP_TOS;
354       break;
355
356     case CPNET_IP_TTL:
357       level = IPPROTO_IP;
358       optname = IP_TTL;
359       break;
360
361     case CPNET_SO_BINDADDR:
362     case CPNET_IP_MULTICAST_IF:
363     case CPNET_IP_MULTICAST_IF2:
364       JCL_ThrowException (env, IO_EXCEPTION, "argument not a boolean or integer option");
365       return -1;
366     }
367
368   if (getsockopt (fd, level, optname, optval, &optlen) == -1)
369     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
370
371   /* Returns the linger value if it is enabled or -1 in case
372    * it is disabled. This is how the Java API expects it.
373    */
374   if (joption == CPNET_SO_LINGER)
375     return (linger.l_onoff) ? linger.l_linger : -1;
376   if (joption == CPNET_SO_TIMEOUT)
377     return (timeo.tv_sec * 1000) + (timeo.tv_usec / 1000);
378
379   return value;
380 }
381
382 JNIEXPORT void JNICALL
383 Java_gnu_java_net_VMPlainSocketImpl_setMulticastInterface (JNIEnv *env,
384                                                            jclass c __attribute__((unused)),
385                                                            jint fd,
386                                                            jint optionId __attribute__((unused)),
387                                                            jobject addr)
388 {
389   int result;
390   cpnet_address *cpaddr = _javanet_get_ip_netaddr (env, addr);
391
392   if ((*env)->ExceptionOccurred (env))
393     return;
394
395   result = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
396                       (struct sockaddr *) cpaddr->data, cpaddr->len);
397
398   cpnet_freeAddress (env, cpaddr);
399   
400   if (result == -1)
401     JCL_ThrowException (env, SOCKET_EXCEPTION, cpnative_getErrorString (errno));
402 }
403
404 JNIEXPORT void JNICALL
405 Java_gnu_java_net_VMPlainSocketImpl_setMulticastInterface6 (JNIEnv *env,
406                                                            jclass c __attribute__((unused)),
407                                                            jint fd,
408                                                            jint optionId __attribute__((unused)),
409                                                            jstring ifname)
410 {
411 #ifdef HAVE_SETSOCKOPT
412 #ifdef HAVE_INET6       
413   int result;
414   const char *str_ifname = JCL_jstring_to_cstring (env, ifname);
415   u_int if_index;
416
417   if ((*env)->ExceptionOccurred (env))
418     {
419       JCL_free_cstring(env, ifname, str_ifname);
420       return;
421     }
422
423   if_index = if_nametoindex(str_ifname);
424   if (!if_index)
425     {
426       JCL_free_cstring(env, ifname, str_ifname);
427       JCL_ThrowException (env, SOCKET_EXCEPTION, "interface does not exist");
428       return;
429     }
430
431   result = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
432                       (u_int *) &if_index, sizeof(if_index));
433
434   JCL_free_cstring(env, ifname, str_ifname);
435   
436   if (result == -1)
437     JCL_ThrowException (env, SOCKET_EXCEPTION, cpnative_getErrorString (errno));
438 #else
439   (void) fd;
440   JCL_ThrowException (env, "java/lang/InternalError",
441                       "IPv6 support not available");
442 #endif /* HAVE_INET6 */
443 #else
444   (void) fd;
445   JCL_ThrowException (env, "java/lang/InternalError",
446                       "socket options not supported");
447 #endif /* HAVE_SETSOCKOPT */
448 }
449
450 JNIEXPORT jobject JNICALL
451 Java_gnu_java_net_VMPlainSocketImpl_getMulticastInterface (JNIEnv *env,
452                                                            jclass c __attribute__((unused)),
453                                                            jint fd,
454                                                            jint optionId __attribute__((unused)))
455 {
456   jobject obj;
457   cpnet_address *cpaddr;
458   int result = cpnet_getMulticastIF (env, fd, &cpaddr);
459  
460   if (result != CPNATIVE_OK)
461     {
462       JCL_ThrowException (env, SOCKET_EXCEPTION,
463                           cpnative_getErrorString (result));
464       return (0);
465     }
466
467   obj = _javanet_create_inetaddress (env, cpaddr);
468   cpnet_freeAddress (env, cpaddr);
469
470   return obj;
471 }
472
473
474 /*
475  * Class:     gnu_java_net_VMPlainSocketImpl
476  * Method:    shutdownInput
477  * Signature: (I)V
478  */
479 JNIEXPORT void JNICALL
480 Java_gnu_java_net_VMPlainSocketImpl_shutdownInput (JNIEnv *env,
481                                                    jclass c __attribute__((unused)),
482                                                    jint fd)
483 {
484   if (shutdown (fd, SHUT_RD) == -1)
485     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
486 }
487
488 /*
489  * Class:     gnu_java_net_VMPlainSocketImpl
490  * Method:    shutdownOutput
491  * Signature: (I)V
492  */
493 JNIEXPORT void JNICALL
494 Java_gnu_java_net_VMPlainSocketImpl_shutdownOutput (JNIEnv *env,
495                                                     jclass c __attribute__((unused)),
496                                                     jint fd)
497 {
498   if (shutdown (fd, SHUT_WR) == -1)
499     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
500 }
501
502
503 /*
504  * Class:     gnu_java_net_VMPlainSocketImpl
505  * Method:    sendUrgentData
506  * Signature: (II)V
507  */
508 JNIEXPORT void JNICALL
509 Java_gnu_java_net_VMPlainSocketImpl_sendUrgentData (JNIEnv *env,
510                                                     jclass c __attribute__((unused)),
511                                                     jint fd, jint data)
512 {
513   const char x = (char) data;
514
515   if (send (fd, &x, 1, MSG_OOB) == -1)
516     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
517 }
518
519
520 /*
521  * Class:     gnu_java_net_VMPlainSocketImpl
522  * Method:    join
523  * Signature: (I[B)V
524  */
525 JNIEXPORT void JNICALL
526 Java_gnu_java_net_VMPlainSocketImpl_join (JNIEnv *env,
527                                           jclass clazz __attribute__((unused)),
528                                           jint fd, jbyteArray addr)
529 {
530 #ifdef HAVE_SETSOCKOPT
531   struct ip_mreq maddr;
532   jbyte *addr_elems;
533
534   addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
535   if (addr_elems == NULL)
536     return;
537
538   maddr.imr_multiaddr.s_addr = * ((uint32_t *) addr_elems);
539   maddr.imr_interface.s_addr = INADDR_ANY;
540
541   (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
542
543   if (-1 == setsockopt (fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
544                         &maddr, sizeof (struct ip_mreq)))
545     JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
546 #else
547   (void) fd;
548   (void) addr;
549   JCL_ThrowException (env, "java/lang/InternalError",
550                       "socket options not supported");
551 #endif /* HAVE_SETSOCKOPT */
552 }
553
554
555 /*
556  * Class:     gnu_java_net_VMPlainSocketImpl
557  * Method:    join6
558  * Signature: (I[B)V
559  */
560 JNIEXPORT void JNICALL
561 Java_gnu_java_net_VMPlainSocketImpl_join6 (JNIEnv *env,
562                                            jclass clazz __attribute__((unused)),
563                                            jint fd, jbyteArray addr)
564 {
565 #ifdef HAVE_SETSOCKOPT
566 #ifdef HAVE_INET6
567   struct ipv6_mreq maddr;
568   jbyte *addr_elems;
569
570   addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
571   if (addr_elems == NULL)
572     return;
573
574   memcpy (&(maddr.ipv6mr_multiaddr.s6_addr), addr_elems, 16);
575   maddr.ipv6mr_interface = 0;
576
577   (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
578
579   if (-1 == setsockopt (fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
580                         &maddr, sizeof (struct ipv6_mreq)))
581     JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
582 #else
583   (void) fd;
584   (void) addr;
585   JCL_ThrowException (env, "java/lang/InternalError",
586                       "IPv6 support not available");
587 #endif /* HAVE_INET6 */
588 #else
589   (void) fd;
590   (void) addr;
591   JCL_ThrowException (env, "java/lang/InternalError",
592                       "socket options not supported");
593 #endif /* HAVE_SETSOCKOPT */
594 }
595
596 /*
597  * Class:     gnu_java_net_VMPlainSocketImpl
598  * Method:    leave
599  * Signature: (I[B)V
600  */
601 JNIEXPORT void JNICALL
602 Java_gnu_java_net_VMPlainSocketImpl_leave (JNIEnv *env,
603                                            jclass c __attribute__((unused)),
604                                            jint fd, jbyteArray addr)
605 {
606 #ifdef HAVE_SETSOCKOPT
607   struct ip_mreq maddr;
608   jbyte *addr_elems;
609
610   addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
611   if (addr_elems == NULL)
612     return;
613
614   maddr.imr_multiaddr.s_addr = * ((uint32_t *) addr_elems);
615   maddr.imr_interface.s_addr = INADDR_ANY;
616
617   (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
618
619   if (-1 == setsockopt (fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
620                         &maddr, sizeof (struct ip_mreq)))
621     JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
622 #else
623   (void) fd;
624   (void) addr;
625   JCL_ThrowException (env, "java/lang/InternalError",
626                       "socket options not supported");
627 #endif /* HAVE_SETSOCKOPT */
628 }
629
630 /*
631  * Class:     gnu_java_net_VMPlainSocketImpl
632  * Method:    leave6
633  * Signature: (I[B)V
634  */
635 JNIEXPORT void JNICALL
636 Java_gnu_java_net_VMPlainSocketImpl_leave6 (JNIEnv *env,
637                                             jclass c __attribute__((unused)),
638                                             jint fd, jbyteArray addr)
639 {
640 #ifdef HAVE_SETSOCKOPT
641 #ifdef HAVE_INET6
642   struct ipv6_mreq maddr;
643   jbyte *addr_elems;
644
645   addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
646   if (addr_elems == NULL)
647     return;
648
649   memcpy (&(maddr.ipv6mr_multiaddr.s6_addr), addr_elems, 16);
650   maddr.ipv6mr_interface = 0;
651
652   (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
653
654   if (-1 == setsockopt (fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
655                         &maddr, sizeof (struct ipv6_mreq)))
656     JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
657 #else
658   (void) fd;
659   (void) addr;
660   JCL_ThrowException (env, "java/lang/InternalError",
661                       "IPv6 support not available");
662 #endif /* HAVE_INET6 */
663 #else
664   (void) fd;
665   (void) addr;
666   JCL_ThrowException (env, "java/lang/InternalError",
667                       "socket options not supported");
668 #endif /* HAVE_SETSOCKOPT */
669 }
670
671 static uint32_t getif_address (JNIEnv *env, const char *ifname);
672 static int getif_index (JNIEnv *env, const char *ifname);
673
674 /*
675  * Class:     gnu_java_net_VMPlainSocketImpl
676  * Method:    joinGroup
677  * Signature: (I[BILjava/lang/String;)V
678  */
679 JNIEXPORT void JNICALL
680 Java_gnu_java_net_VMPlainSocketImpl_joinGroup (JNIEnv *env,
681                                                jclass c __attribute__((unused)),
682                                                jint fd, jbyteArray addr,
683                                                jstring ifname)
684 {
685 #ifdef HAVE_SETSOCKOPT
686   struct ip_mreq maddr;
687   jbyte *addr_elems;
688   const char *str_ifname;
689
690   if (ifname != NULL)
691     {
692       str_ifname = JCL_jstring_to_cstring(env, ifname);
693       maddr.imr_interface.s_addr = getif_address (env, str_ifname);
694       JCL_free_cstring(env, ifname, str_ifname);
695
696       if ((*env)->ExceptionCheck (env))
697         return;
698     }
699   else
700     maddr.imr_interface.s_addr = INADDR_ANY;
701
702   addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
703   if (addr_elems == NULL)
704     return;
705
706   maddr.imr_multiaddr.s_addr = * ((uint32_t *) addr_elems);
707
708   (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
709
710   if (-1 == setsockopt (fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
711                         &maddr, sizeof (struct ip_mreq)))
712     JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
713
714 #else
715   (void) fd;
716   (void) addr;
717   (void) ifname;
718   JCL_ThrowException (env, "java/lang/InternalError",
719                       "socket options not supported");
720 #endif /* HAVE_SETSOCKOPT */
721 }
722
723 /*
724  * Class:     gnu_java_net_VMPlainSocketImpl
725  * Method:    joinGroup6
726  * Signature: (I[BILjava/lang/String;)V
727  */
728 JNIEXPORT void JNICALL
729 Java_gnu_java_net_VMPlainSocketImpl_joinGroup6 (JNIEnv *env,
730                                                 jclass c __attribute__((unused)),
731                                                 jint fd, jbyteArray addr,
732                                                 jstring ifname)
733 {
734 #ifdef HAVE_SETSOCKOPT
735 #ifdef HAVE_INET6
736   struct ipv6_mreq maddr;
737   jbyte *addr_elems;
738   const char *str_ifname;
739
740   if (ifname == NULL)
741     {
742       str_ifname = JCL_jstring_to_cstring(env, ifname);
743       maddr.ipv6mr_interface = getif_index (env, str_ifname);
744       JCL_free_cstring(env, ifname, str_ifname);
745
746       if ((*env)->ExceptionCheck (env))
747         return;
748     }
749   else
750     maddr.ipv6mr_interface = 0;
751
752   addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
753   if (addr_elems == NULL)
754     return;
755
756   memcpy (&(maddr.ipv6mr_multiaddr.s6_addr), addr_elems, 16);
757
758   (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
759
760   if (-1 == setsockopt (fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
761                         &maddr, sizeof (struct ipv6_mreq)))
762     JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
763 #else
764   (void) fd;
765   (void) addr;
766   JCL_ThrowException (env, "java/lang/InternalError",
767                       "IPv6 support not available");
768 #endif /* HAVE_INET6 */
769 #else
770   (void) fd;
771   (void) addr;
772   JCL_ThrowException (env, "java/lang/InternalError",
773                       "socket options not supported");
774 #endif /* HAVE_SETSOCKOPT */
775 }
776
777 /*
778  * Class:     gnu_java_net_VMPlainSocketImpl
779  * Method:    leaveGroup
780  * Signature: (I[BILjava/lang/String;)V
781  */
782 JNIEXPORT void JNICALL
783 Java_gnu_java_net_VMPlainSocketImpl_leaveGroup (JNIEnv *env,
784                                                 jclass c __attribute__((unused)),
785                                                 jint fd, jbyteArray addr,
786                                                 jstring ifname)
787 {
788 #ifdef HAVE_SETSOCKOPT
789   struct ip_mreq maddr;
790   jbyte *addr_elems;
791   const char *str_ifname;
792
793   if (ifname != NULL)
794     {
795       str_ifname = JCL_jstring_to_cstring(env, ifname);
796       maddr.imr_interface.s_addr = getif_address (env, str_ifname);
797       JCL_free_cstring(env, ifname, str_ifname);
798
799       if ((*env)->ExceptionCheck (env))
800         return;
801     }
802   else
803     maddr.imr_interface.s_addr = INADDR_ANY;
804
805   addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
806   if (addr_elems == NULL)
807     return;
808
809   maddr.imr_multiaddr.s_addr = * ((uint32_t *) addr_elems);
810
811   (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
812
813   if (-1 == setsockopt (fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
814                         &maddr, sizeof (struct ip_mreq)))
815     JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
816 #else
817   (void) fd;
818   (void) addr;
819   (void) ifname;
820   JCL_ThrowException (env, "java/lang/InternalError",
821                       "socket options not supported");
822 #endif /* HAVE_SETSOCKOPT */
823 }
824
825 /*
826  * Class:     gnu_java_net_VMPlainSocketImpl
827  * Method:    leaveGroup6
828  * Signature: (I[BILjava/lang/String;)V
829  */
830 JNIEXPORT void JNICALL
831 Java_gnu_java_net_VMPlainSocketImpl_leaveGroup6 (JNIEnv *env,
832                                                 jclass c __attribute__((unused)),
833                                                 jint fd, jbyteArray addr,
834                                                 jstring ifname)
835 {
836 #ifdef HAVE_SETSOCKOPT
837 #ifdef HAVE_INET6
838   struct ipv6_mreq maddr;
839   jbyte *addr_elems;
840   const char *str_ifname;
841
842   if (ifname == NULL)
843     {
844       str_ifname = JCL_jstring_to_cstring(env, ifname);
845       maddr.ipv6mr_interface = getif_index (env, str_ifname);
846       JCL_free_cstring(env, ifname, str_ifname);
847
848       if ((*env)->ExceptionCheck (env))
849         return;
850     }
851   else
852     maddr.ipv6mr_interface = 0;
853
854   addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
855   if (addr_elems == NULL)
856     return;
857
858   memcpy (&(maddr.ipv6mr_multiaddr.s6_addr), addr_elems, 16);
859
860   (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
861
862   if (-1 == setsockopt (fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
863                         &maddr, sizeof (struct ipv6_mreq)))
864     JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
865 #else
866   (void) fd;
867   (void) addr;
868   JCL_ThrowException (env, "java/lang/InternalError",
869                       "IPv6 support not available");
870 #endif /* HAVE_INET6 */
871 #else
872   (void) fd;
873   (void) addr;
874   JCL_ThrowException (env, "java/lang/InternalError",
875                       "socket options not supported");
876 #endif /* HAVE_SETSOCKOPT */
877 }
878
879 static uint32_t
880 getif_address (JNIEnv *env, const char *ifname)
881 {
882 #if defined (HAVE_IFADDRS_H) && defined (HAVE_GETIFADDRS)
883   struct ifaddrs *ifaddrs, *i;
884   uint32_t addr = 0;
885   int foundaddr = 0;
886
887   if (getifaddrs (&ifaddrs) == -1)
888     {
889       JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
890       return 0;
891     }
892
893   for (i = ifaddrs; i != NULL; i = i->ifa_next)
894     {
895       if (strcmp (ifname, i->ifa_name) == 0)
896         {
897           /* Matched the name; see if there is an IPv4 address. */
898           if (i->ifa_addr->sa_family == AF_INET)
899             {
900               foundaddr = 1;
901               addr = ((struct sockaddr_in *) i->ifa_addr)->sin_addr.s_addr;
902               break;
903             }
904         }
905     }
906
907   if (!foundaddr)
908     JCL_ThrowException (env, SOCKET_EXCEPTION, "interface has no IPv4 address");
909
910   freeifaddrs (ifaddrs);
911
912   return addr;
913 #else
914   (void) ifname;
915   JCL_ThrowException (env, "java/lang/InternalError",
916                       "getifaddrs not available");
917   return 0;
918 #endif /* HAVE_IFADDRS_H && HAVE_GETIFADDRS */
919 }
920
921 static int
922 getif_index (JNIEnv *env, const char *ifname)
923 {
924 #if defined (HAVE_IFADDRS_H) && defined (HAVE_GETIFADDRS)
925   struct ifaddrs *ifaddrs, *i;
926   char *lastname = NULL;
927   int index = 1;
928   int foundname = 0;
929
930   if (getifaddrs (&ifaddrs) == -1)
931     {
932       JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
933       return -1;
934     }
935
936   lastname = ifaddrs->ifa_name;
937   for (i = ifaddrs; i != NULL; i = i->ifa_next)
938     {
939       if (strcmp (lastname, ifaddrs->ifa_name) != 0)
940         {
941           lastname = ifaddrs->ifa_name;
942           index++;
943         }
944       if (strcmp (ifname, ifaddrs->ifa_name) == 0)
945         {
946           foundname = 1;
947           break;
948         }
949     }
950
951   if (!foundname)
952     JCL_ThrowException (env, SOCKET_EXCEPTION,
953                         "no interface with that name");
954
955   freeifaddrs (ifaddrs);
956
957   return index;
958 #else
959   (void) ifname;
960   JCL_ThrowException (env, "java/lang/InternalError",
961                       "getifaddrs not available");
962   return -1;
963 #endif /* HAVE_GETIFADDRS */
964 }