1 /*
2 * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 #include <errno.h>
27 #include <netinet/in.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32
33 #ifdef __solaris__
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <stropts.h>
37
38 #ifndef BSD_COMP
39 #define BSD_COMP
40 #endif
41 #endif
42 #ifdef __linux__
43 #include <unistd.h>
44 #include <sys/utsname.h>
45 #include <netinet/ip.h>
46
47 #define IPV6_MULTICAST_IF 17
48 #ifndef SO_BSDCOMPAT
49 #define SO_BSDCOMPAT 14
50 #endif
51 /**
52 * IP_MULTICAST_ALL has been supported since kernel version 2.6.31
53 * but we may be building on a machine that is older than that.
54 */
55 #ifndef IP_MULTICAST_ALL
56 #define IP_MULTICAST_ALL 49
57 #endif
58 #endif // __linux__
59
60 #include <sys/ioctl.h>
61
62 #ifndef IPTOS_TOS_MASK
63 #define IPTOS_TOS_MASK 0x1e
64 #endif
65 #ifndef IPTOS_PREC_MASK
66 #define IPTOS_PREC_MASK 0xe0
67 #endif
68
69 #include "jvm.h"
70 #include "jni_util.h"
71 #include "net_util.h"
72
73 #include "java_net_SocketOptions.h"
74 #include "java_net_PlainDatagramSocketImpl.h"
75 #include "java_net_NetworkInterface.h"
76 /************************************************************************
77 * PlainDatagramSocketImpl
78 */
79
80 static jfieldID IO_fd_fdID;
81
82 static jfieldID pdsi_fdID;
83 static jfieldID pdsi_timeoutID;
84 static jfieldID pdsi_trafficClassID;
85 static jfieldID pdsi_localPortID;
86 static jfieldID pdsi_connected;
87 static jfieldID pdsi_connectedAddress;
88 static jfieldID pdsi_connectedPort;
89
90 extern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him);
91 extern int getDefaultScopeID(JNIEnv *env);
92
93 /*
94 * Returns a java.lang.Integer based on 'i'
95 */
createInteger(JNIEnv * env,int i)96 static jobject createInteger(JNIEnv *env, int i) {
97 static jclass i_class;
98 static jmethodID i_ctrID;
99
100 if (i_class == NULL) {
101 jclass c = (*env)->FindClass(env, "java/lang/Integer");
102 CHECK_NULL_RETURN(c, NULL);
103 i_ctrID = (*env)->GetMethodID(env, c, "<init>", "(I)V");
104 CHECK_NULL_RETURN(i_ctrID, NULL);
105 i_class = (*env)->NewGlobalRef(env, c);
106 CHECK_NULL_RETURN(i_class, NULL);
107 }
108
109 return ( (*env)->NewObject(env, i_class, i_ctrID, i) );
110 }
111
112 /*
113 * Returns a java.lang.Boolean based on 'b'
114 */
createBoolean(JNIEnv * env,int b)115 static jobject createBoolean(JNIEnv *env, int b) {
116 static jclass b_class;
117 static jmethodID b_ctrID;
118
119 if (b_class == NULL) {
120 jclass c = (*env)->FindClass(env, "java/lang/Boolean");
121 CHECK_NULL_RETURN(c, NULL);
122 b_ctrID = (*env)->GetMethodID(env, c, "<init>", "(Z)V");
123 CHECK_NULL_RETURN(b_ctrID, NULL);
124 b_class = (*env)->NewGlobalRef(env, c);
125 CHECK_NULL_RETURN(b_class, NULL);
126 }
127
128 return( (*env)->NewObject(env, b_class, b_ctrID, (jboolean)(b!=0)) );
129 }
130
131
132 /*
133 * Returns the fd for a PlainDatagramSocketImpl or -1
134 * if closed.
135 */
getFD(JNIEnv * env,jobject this)136 static int getFD(JNIEnv *env, jobject this) {
137 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
138 if (fdObj == NULL) {
139 return -1;
140 }
141 return (*env)->GetIntField(env, fdObj, IO_fd_fdID);
142 }
143
144
145 /*
146 * Class: java_net_PlainDatagramSocketImpl
147 * Method: init
148 * Signature: ()V
149 */
150 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_init(JNIEnv * env,jclass cls)151 Java_java_net_PlainDatagramSocketImpl_init(JNIEnv *env, jclass cls) {
152
153 #ifdef __linux__
154 struct utsname sysinfo;
155 #endif
156 pdsi_fdID = (*env)->GetFieldID(env, cls, "fd",
157 "Ljava/io/FileDescriptor;");
158 CHECK_NULL(pdsi_fdID);
159 pdsi_timeoutID = (*env)->GetFieldID(env, cls, "timeout", "I");
160 CHECK_NULL(pdsi_timeoutID);
161 pdsi_trafficClassID = (*env)->GetFieldID(env, cls, "trafficClass", "I");
162 CHECK_NULL(pdsi_trafficClassID);
163 pdsi_localPortID = (*env)->GetFieldID(env, cls, "localPort", "I");
164 CHECK_NULL(pdsi_localPortID);
165 pdsi_connected = (*env)->GetFieldID(env, cls, "connected", "Z");
166 CHECK_NULL(pdsi_connected);
167 pdsi_connectedAddress = (*env)->GetFieldID(env, cls, "connectedAddress",
168 "Ljava/net/InetAddress;");
169 CHECK_NULL(pdsi_connectedAddress);
170 pdsi_connectedPort = (*env)->GetFieldID(env, cls, "connectedPort", "I");
171 CHECK_NULL(pdsi_connectedPort);
172
173 IO_fd_fdID = NET_GetFileDescriptorID(env);
174 CHECK_NULL(IO_fd_fdID);
175
176 initInetAddressIDs(env);
177 JNU_CHECK_EXCEPTION(env);
178 Java_java_net_NetworkInterface_init(env, 0);
179
180 }
181
182 /*
183 * Class: java_net_PlainDatagramSocketImpl
184 * Method: bind
185 * Signature: (ILjava/net/InetAddress;)V
186 */
187 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_bind0(JNIEnv * env,jobject this,jint localport,jobject iaObj)188 Java_java_net_PlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this,
189 jint localport, jobject iaObj) {
190 /* fdObj is the FileDescriptor field on this */
191 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
192 /* fd is an int field on fdObj */
193 int fd;
194 int len = 0;
195 SOCKADDR him;
196
197 if (IS_NULL(fdObj)) {
198 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
199 "Socket closed");
200 return;
201 } else {
202 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
203 }
204
205 if (IS_NULL(iaObj)) {
206 JNU_ThrowNullPointerException(env, "iaObj is null.");
207 return;
208 }
209
210 /* bind */
211 if (NET_InetAddressToSockaddr(env, iaObj, localport, (struct sockaddr *)&him, &len, JNI_TRUE) != 0) {
212 return;
213 }
214 setDefaultScopeID(env, (struct sockaddr *)&him);
215
216 if (NET_Bind(fd, (struct sockaddr *)&him, len) < 0) {
217 if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
218 errno == EPERM || errno == EACCES) {
219 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "BindException",
220 "Bind failed");
221 } else {
222 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
223 "Bind failed");
224 }
225 return;
226 }
227
228 /* initialize the local port */
229 if (localport == 0) {
230 /* Now that we're a connected socket, let's extract the port number
231 * that the system chose for us and store it in the Socket object.
232 */
233 if (JVM_GetSockName(fd, (struct sockaddr *)&him, &len) == -1) {
234 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
235 "Error getting socket name");
236 return;
237 }
238
239 localport = NET_GetPortFromSockaddr((struct sockaddr *)&him);
240
241 (*env)->SetIntField(env, this, pdsi_localPortID, localport);
242 } else {
243 (*env)->SetIntField(env, this, pdsi_localPortID, localport);
244 }
245 }
246
247 /*
248 * Class: java_net_PlainDatagramSocketImpl
249 * Method: connect0
250 * Signature: (Ljava/net/InetAddress;I)V
251 */
252 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_connect0(JNIEnv * env,jobject this,jobject address,jint port)253 Java_java_net_PlainDatagramSocketImpl_connect0(JNIEnv *env, jobject this,
254 jobject address, jint port) {
255 /* The object's field */
256 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
257 /* The fdObj'fd */
258 jint fd;
259 /* The packetAddress address, family and port */
260 SOCKADDR rmtaddr;
261 int len = 0;
262
263 if (IS_NULL(fdObj)) {
264 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
265 "Socket closed");
266 return;
267 }
268 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
269
270 if (IS_NULL(address)) {
271 JNU_ThrowNullPointerException(env, "address");
272 return;
273 }
274
275 if (NET_InetAddressToSockaddr(env, address, port, (struct sockaddr *)&rmtaddr, &len, JNI_TRUE) != 0) {
276 return;
277 }
278
279 setDefaultScopeID(env, (struct sockaddr *)&rmtaddr);
280
281 if (JVM_Connect(fd, (struct sockaddr *)&rmtaddr, len) == -1) {
282 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
283 "Connect failed");
284 return;
285 }
286
287 }
288
289 /*
290 * Class: java_net_PlainDatagramSocketImpl
291 * Method: disconnect0
292 * Signature: ()V
293 */
294 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_disconnect0(JNIEnv * env,jobject this,jint family)295 Java_java_net_PlainDatagramSocketImpl_disconnect0(JNIEnv *env, jobject this, jint family) {
296 /* The object's field */
297 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
298 /* The fdObj'fd */
299 jint fd;
300
301 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
302 SOCKADDR addr;
303 int len;
304 #endif
305
306 if (IS_NULL(fdObj)) {
307 return;
308 }
309 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
310
311 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
312 memset(&addr, 0, sizeof(addr));
313 #ifdef AF_INET6
314 if (ipv6_available()) {
315 struct sockaddr_in6 *him6 = (struct sockaddr_in6 *)&addr;
316 him6->sin6_family = AF_UNSPEC;
317 len = sizeof(struct sockaddr_in6);
318 } else
319 #endif
320 {
321 struct sockaddr_in *him4 = (struct sockaddr_in*)&addr;
322 him4->sin_family = AF_UNSPEC;
323 len = sizeof(struct sockaddr_in);
324 }
325 JVM_Connect(fd, (struct sockaddr *)&addr, len);
326
327 #ifdef __linux__
328 int localPort = 0;
329 if (JVM_GetSockName(fd, (struct sockaddr *)&addr, &len) == -1)
330 return;
331
332 localPort = NET_GetPortFromSockaddr((struct sockaddr *)&addr);
333 if (localPort == 0) {
334 localPort = (*env)->GetIntField(env, this, pdsi_localPortID);
335 #ifdef AF_INET6
336 if (((struct sockaddr*)&addr)->sa_family == AF_INET6) {
337 ((struct sockaddr_in6*)&addr)->sin6_port = htons(localPort);
338 } else
339 #endif /* AF_INET6 */
340 {
341 ((struct sockaddr_in*)&addr)->sin_port = htons(localPort);
342 }
343
344 NET_Bind(fd, (struct sockaddr *)&addr, len);
345 }
346
347 #endif
348 #else
349 JVM_Connect(fd, 0, 0);
350 #endif
351 }
352
353 /*
354 * Class: java_net_PlainDatagramSocketImpl
355 * Method: send
356 * Signature: (Ljava/net/DatagramPacket;)V
357 */
358 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_send(JNIEnv * env,jobject this,jobject packet)359 Java_java_net_PlainDatagramSocketImpl_send(JNIEnv *env, jobject this,
360 jobject packet) {
361
362 char BUF[MAX_BUFFER_LEN];
363 char *fullPacket = NULL;
364 int ret, mallocedPacket = JNI_FALSE;
365 /* The object's field */
366 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
367 jint trafficClass = (*env)->GetIntField(env, this, pdsi_trafficClassID);
368
369 jbyteArray packetBuffer;
370 jobject packetAddress;
371 jint packetBufferOffset, packetBufferLen, packetPort;
372 jboolean connected;
373
374 /* The fdObj'fd */
375 jint fd;
376
377 SOCKADDR rmtaddr, *rmtaddrP=&rmtaddr;
378 int len;
379
380 if (IS_NULL(fdObj)) {
381 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
382 "Socket closed");
383 return;
384 }
385 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
386
387 if (IS_NULL(packet)) {
388 JNU_ThrowNullPointerException(env, "packet");
389 return;
390 }
391
392 connected = (*env)->GetBooleanField(env, this, pdsi_connected);
393
394 packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
395 packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
396 if (IS_NULL(packetBuffer) || IS_NULL(packetAddress)) {
397 JNU_ThrowNullPointerException(env, "null buffer || null address");
398 return;
399 }
400
401 packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
402 packetBufferLen = (*env)->GetIntField(env, packet, dp_lengthID);
403
404 if (connected) {
405 /* arg to NET_Sendto () null in this case */
406 len = 0;
407 rmtaddrP = 0;
408 } else {
409 packetPort = (*env)->GetIntField(env, packet, dp_portID);
410 if (NET_InetAddressToSockaddr(env, packetAddress, packetPort, (struct sockaddr *)&rmtaddr, &len, JNI_TRUE) != 0) {
411 return;
412 }
413 }
414 setDefaultScopeID(env, (struct sockaddr *)&rmtaddr);
415
416 if (packetBufferLen > MAX_BUFFER_LEN) {
417 /* When JNI-ifying the JDK's IO routines, we turned
418 * reads and writes of byte arrays of size greater
419 * than 2048 bytes into several operations of size 2048.
420 * This saves a malloc()/memcpy()/free() for big
421 * buffers. This is OK for file IO and TCP, but that
422 * strategy violates the semantics of a datagram protocol.
423 * (one big send) != (several smaller sends). So here
424 * we *must* allocate the buffer. Note it needn't be bigger
425 * than 65,536 (0xFFFF), the max size of an IP packet.
426 * Anything bigger should be truncated anyway.
427 *
428 * We may want to use a smarter allocation scheme at some
429 * point.
430 */
431 if (packetBufferLen > MAX_PACKET_LEN) {
432 packetBufferLen = MAX_PACKET_LEN;
433 }
434 fullPacket = (char *)malloc(packetBufferLen);
435
436 if (!fullPacket) {
437 JNU_ThrowOutOfMemoryError(env, "Send buffer native heap allocation failed");
438 return;
439 } else {
440 mallocedPacket = JNI_TRUE;
441 }
442 } else {
443 fullPacket = &(BUF[0]);
444 }
445
446 (*env)->GetByteArrayRegion(env, packetBuffer, packetBufferOffset, packetBufferLen,
447 (jbyte *)fullPacket);
448 #ifdef AF_INET6
449 if (trafficClass != 0 && ipv6_available()) {
450 NET_SetTrafficClass((struct sockaddr *)&rmtaddr, trafficClass);
451 }
452 #endif /* AF_INET6 */
453
454
455 /*
456 * Send the datagram.
457 *
458 * If we are connected it's possible that sendto will return
459 * ECONNREFUSED indicating that an ICMP port unreachable has
460 * received.
461 */
462 ret = NET_SendTo(fd, fullPacket, packetBufferLen, 0,
463 (struct sockaddr *)rmtaddrP, len);
464
465 if (ret < 0) {
466 switch (ret) {
467 case JVM_IO_ERR :
468 if (errno == ECONNREFUSED) {
469 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
470 "ICMP Port Unreachable");
471 } else {
472 NET_ThrowByNameWithLastError(env, "java/io/IOException", "sendto failed");
473 }
474 break;
475
476 case JVM_IO_INTR:
477 JNU_ThrowByName(env, "java/io/InterruptedIOException",
478 "operation interrupted");
479 break;
480 }
481 }
482
483 if (mallocedPacket) {
484 free(fullPacket);
485 }
486 return;
487 }
488
489 /*
490 * Class: java_net_PlainDatagramSocketImpl
491 * Method: peek
492 * Signature: (Ljava/net/InetAddress;)I
493 */
494 JNIEXPORT jint JNICALL
Java_java_net_PlainDatagramSocketImpl_peek(JNIEnv * env,jobject this,jobject addressObj)495 Java_java_net_PlainDatagramSocketImpl_peek(JNIEnv *env, jobject this,
496 jobject addressObj) {
497
498 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
499 jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
500 jint fd;
501 ssize_t n;
502 SOCKADDR remote_addr;
503 int len;
504 char buf[1];
505 jint family;
506 jobject iaObj;
507 int port;
508 if (IS_NULL(fdObj)) {
509 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
510 return -1;
511 } else {
512 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
513 }
514 if (IS_NULL(addressObj)) {
515 JNU_ThrowNullPointerException(env, "Null address in peek()");
516 }
517 if (timeout) {
518 int ret = NET_Timeout(fd, timeout);
519 if (ret == 0) {
520 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
521 "Peek timed out");
522 return ret;
523 } else if (ret == JVM_IO_ERR) {
524 if (errno == EBADF) {
525 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
526 } else if (errno == ENOMEM) {
527 JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
528 } else {
529 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Peek failed");
530 }
531 return ret;
532 } else if (ret == JVM_IO_INTR) {
533 JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
534 "operation interrupted");
535 return ret; /* WARNING: SHOULD WE REALLY RETURN -2??? */
536 }
537 }
538
539 len = SOCKADDR_LEN;
540 n = NET_RecvFrom(fd, buf, 1, MSG_PEEK,
541 (struct sockaddr *)&remote_addr, &len);
542
543 if (n == JVM_IO_ERR) {
544
545 #ifdef __solaris__
546 if (errno == ECONNREFUSED) {
547 int orig_errno = errno;
548 (void) recv(fd, buf, 1, 0);
549 errno = orig_errno;
550 }
551 #endif
552 if (errno == ECONNREFUSED) {
553 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
554 "ICMP Port Unreachable");
555 } else {
556 if (errno == EBADF) {
557 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
558 } else {
559 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Peek failed");
560 }
561 }
562 return 0;
563 } else if (n == JVM_IO_INTR) {
564 JNU_ThrowByName(env, "java/io/InterruptedIOException", 0);
565 return 0;
566 }
567
568 iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&remote_addr, &port);
569 #ifdef AF_INET6
570 family = getInetAddress_family(env, iaObj) == IPv4? AF_INET : AF_INET6;
571 JNU_CHECK_EXCEPTION_RETURN(env, -1);
572 #else
573 family = AF_INET;
574 #endif
575 if (family == AF_INET) { /* this API can't handle IPV6 addresses */
576 int address = getInetAddress_addr(env, iaObj);
577 JNU_CHECK_EXCEPTION_RETURN(env, -1);
578 setInetAddress_addr(env, addressObj, address);
579 JNU_CHECK_EXCEPTION_RETURN(env, -1);
580 }
581 return port;
582 }
583
584 JNIEXPORT jint JNICALL
Java_java_net_PlainDatagramSocketImpl_peekData(JNIEnv * env,jobject this,jobject packet)585 Java_java_net_PlainDatagramSocketImpl_peekData(JNIEnv *env, jobject this,
586 jobject packet) {
587
588 char BUF[MAX_BUFFER_LEN];
589 char *fullPacket = NULL;
590 int mallocedPacket = JNI_FALSE;
591 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
592 jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
593
594 jbyteArray packetBuffer;
595 jint packetBufferOffset, packetBufferLen;
596
597 int fd;
598
599 int n;
600 SOCKADDR remote_addr;
601 int len;
602 int port;
603
604 if (IS_NULL(fdObj)) {
605 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
606 "Socket closed");
607 return -1;
608 }
609
610 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
611
612 if (IS_NULL(packet)) {
613 JNU_ThrowNullPointerException(env, "packet");
614 return -1;
615 }
616
617 packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
618 if (IS_NULL(packetBuffer)) {
619 JNU_ThrowNullPointerException(env, "packet buffer");
620 return -1;
621 }
622 packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
623 packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
624 if (timeout) {
625 int ret = NET_Timeout(fd, timeout);
626 if (ret == 0) {
627 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
628 "Receive timed out");
629 return -1;
630 } else if (ret == JVM_IO_ERR) {
631 if (errno == ENOMEM) {
632 JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
633 #ifdef __linux__
634 } else if (errno == EBADF) {
635 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
636 } else {
637 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
638 #else
639 } else {
640 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
641 #endif
642 }
643 return -1;
644 } else if (ret == JVM_IO_INTR) {
645 JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
646 "operation interrupted");
647 return -1;
648 }
649 }
650
651 if (packetBufferLen > MAX_BUFFER_LEN) {
652
653 /* When JNI-ifying the JDK's IO routines, we turned
654 * reads and writes of byte arrays of size greater
655 * than 2048 bytes into several operations of size 2048.
656 * This saves a malloc()/memcpy()/free() for big
657 * buffers. This is OK for file IO and TCP, but that
658 * strategy violates the semantics of a datagram protocol.
659 * (one big send) != (several smaller sends). So here
660 * we *must* allocate the buffer. Note it needn't be bigger
661 * than 65,536 (0xFFFF), the max size of an IP packet.
662 * anything bigger is truncated anyway.
663 *
664 * We may want to use a smarter allocation scheme at some
665 * point.
666 */
667 if (packetBufferLen > MAX_PACKET_LEN) {
668 packetBufferLen = MAX_PACKET_LEN;
669 }
670 fullPacket = (char *)malloc(packetBufferLen);
671
672 if (!fullPacket) {
673 JNU_ThrowOutOfMemoryError(env, "Peek buffer native heap allocation failed");
674 return -1;
675 } else {
676 mallocedPacket = JNI_TRUE;
677 }
678 } else {
679 fullPacket = &(BUF[0]);
680 }
681
682 len = SOCKADDR_LEN;
683 n = NET_RecvFrom(fd, fullPacket, packetBufferLen, MSG_PEEK,
684 (struct sockaddr *)&remote_addr, &len);
685 /* truncate the data if the packet's length is too small */
686 if (n > packetBufferLen) {
687 n = packetBufferLen;
688 }
689 if (n == JVM_IO_ERR) {
690
691 #ifdef __solaris__
692 if (errno == ECONNREFUSED) {
693 int orig_errno = errno;
694 (void) recv(fd, fullPacket, 1, 0);
695 errno = orig_errno;
696 }
697 #endif
698 (*env)->SetIntField(env, packet, dp_offsetID, 0);
699 (*env)->SetIntField(env, packet, dp_lengthID, 0);
700 if (errno == ECONNREFUSED) {
701 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
702 "ICMP Port Unreachable");
703 } else {
704 if (errno == EBADF) {
705 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
706 } else {
707 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
708 }
709 }
710 } else if (n == JVM_IO_INTR) {
711 (*env)->SetIntField(env, packet, dp_offsetID, 0);
712 (*env)->SetIntField(env, packet, dp_lengthID, 0);
713 JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
714 "operation interrupted");
715 } else {
716 /*
717 * success - fill in received address...
718 *
719 * REMIND: Fill in an int on the packet, and create inetadd
720 * object in Java, as a performance improvement. Also
721 * construct the inetadd object lazily.
722 */
723
724 jobject packetAddress;
725
726 /*
727 * Check if there is an InetAddress already associated with this
728 * packet. If so we check if it is the same source address. We
729 * can't update any existing InetAddress because it is immutable
730 */
731 packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
732 if (packetAddress != NULL) {
733 if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&remote_addr, packetAddress)) {
734 /* force a new InetAddress to be created */
735 packetAddress = NULL;
736 }
737 }
738 if (packetAddress == NULL) {
739 packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&remote_addr, &port);
740 /* stuff the new Inetaddress in the packet */
741 (*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
742 } else {
743 /* only get the new port number */
744 port = NET_GetPortFromSockaddr((struct sockaddr *)&remote_addr);
745 }
746 /* and fill in the data, remote address/port and such */
747 (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
748 (jbyte *)fullPacket);
749 (*env)->SetIntField(env, packet, dp_portID, port);
750 (*env)->SetIntField(env, packet, dp_lengthID, n);
751 }
752
753 if (mallocedPacket) {
754 free(fullPacket);
755 }
756 return port;
757 }
758
759 /*
760 * Class: java_net_PlainDatagramSocketImpl
761 * Method: receive
762 * Signature: (Ljava/net/DatagramPacket;)V
763 */
764 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_receive0(JNIEnv * env,jobject this,jobject packet)765 Java_java_net_PlainDatagramSocketImpl_receive0(JNIEnv *env, jobject this,
766 jobject packet) {
767
768 char BUF[MAX_BUFFER_LEN];
769 char *fullPacket = NULL;
770 int mallocedPacket = JNI_FALSE;
771 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
772 jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
773
774 jbyteArray packetBuffer;
775 jint packetBufferOffset, packetBufferLen;
776
777 int fd;
778
779 int n;
780 SOCKADDR remote_addr;
781 int len;
782 jboolean retry;
783 #ifdef __linux__
784 jboolean connected = JNI_FALSE;
785 jobject connectedAddress = NULL;
786 jint connectedPort = 0;
787 jlong prevTime = 0;
788 #endif
789
790 if (IS_NULL(fdObj)) {
791 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
792 "Socket closed");
793 return;
794 }
795
796 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
797
798 if (IS_NULL(packet)) {
799 JNU_ThrowNullPointerException(env, "packet");
800 return;
801 }
802
803 packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
804 if (IS_NULL(packetBuffer)) {
805 JNU_ThrowNullPointerException(env, "packet buffer");
806 return;
807 }
808 packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
809 packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
810
811 if (packetBufferLen > MAX_BUFFER_LEN) {
812
813 /* When JNI-ifying the JDK's IO routines, we turned
814 * reads and writes of byte arrays of size greater
815 * than 2048 bytes into several operations of size 2048.
816 * This saves a malloc()/memcpy()/free() for big
817 * buffers. This is OK for file IO and TCP, but that
818 * strategy violates the semantics of a datagram protocol.
819 * (one big send) != (several smaller sends). So here
820 * we *must* allocate the buffer. Note it needn't be bigger
821 * than 65,536 (0xFFFF) the max size of an IP packet,
822 * anything bigger is truncated anyway.
823 *
824 * We may want to use a smarter allocation scheme at some
825 * point.
826 */
827 if (packetBufferLen > MAX_PACKET_LEN) {
828 packetBufferLen = MAX_PACKET_LEN;
829 }
830 fullPacket = (char *)malloc(packetBufferLen);
831
832 if (!fullPacket) {
833 JNU_ThrowOutOfMemoryError(env, "Receive buffer native heap allocation failed");
834 return;
835 } else {
836 mallocedPacket = JNI_TRUE;
837 }
838 } else {
839 fullPacket = &(BUF[0]);
840 }
841
842 do {
843 retry = JNI_FALSE;
844
845 if (timeout) {
846 int ret = NET_Timeout(fd, timeout);
847 if (ret <= 0) {
848 if (ret == 0) {
849 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
850 "Receive timed out");
851 } else if (ret == JVM_IO_ERR) {
852 if (errno == ENOMEM) {
853 JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
854 #ifdef __linux__
855 } else if (errno == EBADF) {
856 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
857 } else {
858 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
859 #else
860 } else {
861 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
862 #endif
863 }
864 } else if (ret == JVM_IO_INTR) {
865 JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
866 "operation interrupted");
867 }
868
869 if (mallocedPacket) {
870 free(fullPacket);
871 }
872
873 return;
874 }
875 }
876
877 len = SOCKADDR_LEN;
878 n = NET_RecvFrom(fd, fullPacket, packetBufferLen, 0,
879 (struct sockaddr *)&remote_addr, &len);
880 /* truncate the data if the packet's length is too small */
881 if (n > packetBufferLen) {
882 n = packetBufferLen;
883 }
884 if (n == JVM_IO_ERR) {
885 (*env)->SetIntField(env, packet, dp_offsetID, 0);
886 (*env)->SetIntField(env, packet, dp_lengthID, 0);
887 if (errno == ECONNREFUSED) {
888 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
889 "ICMP Port Unreachable");
890 } else {
891 if (errno == EBADF) {
892 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
893 } else {
894 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
895 }
896 }
897 } else if (n == JVM_IO_INTR) {
898 (*env)->SetIntField(env, packet, dp_offsetID, 0);
899 (*env)->SetIntField(env, packet, dp_lengthID, 0);
900 JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
901 "operation interrupted");
902 } else {
903 int port;
904 jobject packetAddress;
905
906 /*
907 * success - fill in received address...
908 *
909 * REMIND: Fill in an int on the packet, and create inetadd
910 * object in Java, as a performance improvement. Also
911 * construct the inetadd object lazily.
912 */
913
914 /*
915 * Check if there is an InetAddress already associated with this
916 * packet. If so we check if it is the same source address. We
917 * can't update any existing InetAddress because it is immutable
918 */
919 packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
920 if (packetAddress != NULL) {
921 if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&remote_addr, packetAddress)) {
922 /* force a new InetAddress to be created */
923 packetAddress = NULL;
924 }
925 }
926 if (packetAddress == NULL) {
927 packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&remote_addr, &port);
928 /* stuff the new Inetaddress in the packet */
929 (*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
930 } else {
931 /* only get the new port number */
932 port = NET_GetPortFromSockaddr((struct sockaddr *)&remote_addr);
933 }
934 /* and fill in the data, remote address/port and such */
935 (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
936 (jbyte *)fullPacket);
937 (*env)->SetIntField(env, packet, dp_portID, port);
938 (*env)->SetIntField(env, packet, dp_lengthID, n);
939 }
940
941 } while (retry);
942
943 if (mallocedPacket) {
944 free(fullPacket);
945 }
946 }
947
948 /*
949 * Class: java_net_PlainDatagramSocketImpl
950 * Method: datagramSocketCreate
951 * Signature: ()V
952 */
953 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate(JNIEnv * env,jobject this)954 Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate(JNIEnv *env,
955 jobject this) {
956 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
957 int arg, fd, t = 1;
958 char tmpbuf[1024];
959 #ifdef AF_INET6
960 int domain = ipv6_available() ? AF_INET6 : AF_INET;
961 #else
962 int domain = AF_INET;
963 #endif
964
965 if (IS_NULL(fdObj)) {
966 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
967 "Socket closed");
968 return;
969 }
970
971 if ((fd = JVM_Socket(domain, SOCK_DGRAM, 0)) == JVM_IO_ERR) {
972 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
973 "Error creating socket");
974 return;
975 }
976
977 #ifdef AF_INET6
978 /* Disable IPV6_V6ONLY to ensure dual-socket support */
979 if (domain == AF_INET6) {
980 arg = 0;
981 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
982 sizeof(int)) < 0) {
983 NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6");
984 close(fd);
985 return;
986 }
987 }
988 #endif /* AF_INET6 */
989
990 #ifdef __APPLE__
991 arg = 65507;
992 if (JVM_SetSockOpt(fd, SOL_SOCKET, SO_SNDBUF,
993 (char *)&arg, sizeof(arg)) < 0) {
994 getErrorString(errno, tmpbuf, sizeof(tmpbuf));
995 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
996 close(fd);
997 return;
998 }
999 if (JVM_SetSockOpt(fd, SOL_SOCKET, SO_RCVBUF,
1000 (char *)&arg, sizeof(arg)) < 0) {
1001 getErrorString(errno, tmpbuf, sizeof(tmpbuf));
1002 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
1003 close(fd);
1004 return;
1005 }
1006 #endif /* __APPLE__ */
1007
1008 if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*) &t, sizeof (int)) < 0) {
1009 getErrorString(errno, tmpbuf, sizeof(tmpbuf));
1010 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
1011 close(fd);
1012 return;
1013 }
1014
1015 #if defined(__linux__)
1016 arg = 0;
1017 int level = (domain == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP;
1018 if ((setsockopt(fd, level, IP_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0) &&
1019 (errno != ENOPROTOOPT))
1020 {
1021 getErrorString(errno, tmpbuf, sizeof(tmpbuf));
1022 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
1023 close(fd);
1024 return;
1025 }
1026 #endif
1027
1028 #if defined (__linux__) && defined (AF_INET6)
1029 /*
1030 * On Linux for IPv6 sockets we must set the hop limit
1031 * to 1 to be compatible with default TTL of 1 for IPv4 sockets.
1032 */
1033 if (domain == AF_INET6) {
1034 int ttl = 1;
1035 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &ttl,
1036 sizeof (ttl)) < 0) {
1037 getErrorString(errno, tmpbuf, sizeof(tmpbuf));
1038 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
1039 close(fd);
1040 return;
1041 }
1042 }
1043 #endif /* __linux__ */
1044
1045 (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
1046 }
1047
1048 /*
1049 * Class: java_net_PlainDatagramSocketImpl
1050 * Method: datagramSocketClose
1051 * Signature: ()V
1052 */
1053 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_datagramSocketClose(JNIEnv * env,jobject this)1054 Java_java_net_PlainDatagramSocketImpl_datagramSocketClose(JNIEnv *env,
1055 jobject this) {
1056 /*
1057 * REMIND: PUT A LOCK AROUND THIS CODE
1058 */
1059 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1060 int fd;
1061
1062 if (IS_NULL(fdObj)) {
1063 return;
1064 }
1065 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1066 if (fd == -1) {
1067 return;
1068 }
1069 (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);
1070 NET_SocketClose(fd);
1071 }
1072
1073
1074 /*
1075 * Set outgoing multicast interface designated by a NetworkInterface.
1076 * Throw exception if failed.
1077 */
mcast_set_if_by_if_v4(JNIEnv * env,jobject this,int fd,jobject value)1078 static void mcast_set_if_by_if_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1079 static jfieldID ni_addrsID;
1080 struct in_addr in;
1081 jobjectArray addrArray;
1082 jsize len;
1083 jint family;
1084 jobject addr;
1085 int i;
1086
1087 if (ni_addrsID == NULL ) {
1088 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1089 CHECK_NULL(c);
1090 ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1091 "[Ljava/net/InetAddress;");
1092 CHECK_NULL(ni_addrsID);
1093 }
1094
1095 addrArray = (*env)->GetObjectField(env, value, ni_addrsID);
1096 len = (*env)->GetArrayLength(env, addrArray);
1097
1098 /*
1099 * Check that there is at least one address bound to this
1100 * interface.
1101 */
1102 if (len < 1) {
1103 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1104 "bad argument for IP_MULTICAST_IF2: No IP addresses bound to interface");
1105 return;
1106 }
1107
1108 /*
1109 * We need an ipv4 address here
1110 */
1111 for (i = 0; i < len; i++) {
1112 addr = (*env)->GetObjectArrayElement(env, addrArray, i);
1113 family = getInetAddress_family(env, addr);
1114 JNU_CHECK_EXCEPTION(env);
1115 if (family == IPv4) {
1116 JNU_CHECK_EXCEPTION(env);
1117 in.s_addr = htonl(getInetAddress_addr(env, addr));
1118 JNU_CHECK_EXCEPTION(env);
1119 break;
1120 }
1121 }
1122
1123 if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1124 (const char*)&in, sizeof(in)) < 0) {
1125 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1126 "Error setting socket option");
1127 }
1128 }
1129
1130 /*
1131 * Set outgoing multicast interface designated by a NetworkInterface.
1132 * Throw exception if failed.
1133 */
1134 #ifdef AF_INET6
mcast_set_if_by_if_v6(JNIEnv * env,jobject this,int fd,jobject value)1135 static void mcast_set_if_by_if_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1136 static jfieldID ni_indexID;
1137 int index;
1138
1139 if (ni_indexID == NULL) {
1140 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1141 CHECK_NULL(c);
1142 ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1143 CHECK_NULL(ni_indexID);
1144 }
1145 index = (*env)->GetIntField(env, value, ni_indexID);
1146
1147 if (JVM_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1148 (const char*)&index, sizeof(index)) < 0) {
1149 if (errno == EINVAL && index > 0) {
1150 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1151 "IPV6_MULTICAST_IF failed (interface has IPv4 "
1152 "address only?)");
1153 } else {
1154 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1155 "Error setting socket option");
1156 }
1157 return;
1158 }
1159
1160 }
1161 #endif /* AF_INET6 */
1162
1163 /*
1164 * Set outgoing multicast interface designated by an InetAddress.
1165 * Throw exception if failed.
1166 */
mcast_set_if_by_addr_v4(JNIEnv * env,jobject this,int fd,jobject value)1167 static void mcast_set_if_by_addr_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1168 struct in_addr in;
1169
1170 in.s_addr = htonl( getInetAddress_addr(env, value) );
1171
1172 JNU_CHECK_EXCEPTION(env);
1173 if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1174 (const char*)&in, sizeof(in)) < 0) {
1175 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1176 "Error setting socket option");
1177 }
1178 }
1179
1180 /*
1181 * Set outgoing multicast interface designated by an InetAddress.
1182 * Throw exception if failed.
1183 */
1184 #ifdef AF_INET6
mcast_set_if_by_addr_v6(JNIEnv * env,jobject this,int fd,jobject value)1185 static void mcast_set_if_by_addr_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1186 static jclass ni_class;
1187 if (ni_class == NULL) {
1188 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1189 CHECK_NULL(c);
1190 ni_class = (*env)->NewGlobalRef(env, c);
1191 CHECK_NULL(ni_class);
1192 }
1193
1194 value = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, value);
1195 if (value == NULL) {
1196 if (!(*env)->ExceptionOccurred(env)) {
1197 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1198 "bad argument for IP_MULTICAST_IF"
1199 ": address not bound to any interface");
1200 }
1201 return;
1202 }
1203
1204 mcast_set_if_by_if_v6(env, this, fd, value);
1205 }
1206 #endif
1207
1208 /*
1209 * Sets the multicast interface.
1210 *
1211 * SocketOptions.IP_MULTICAST_IF :-
1212 * value is a InetAddress
1213 * IPv4: set outgoing multicast interface using
1214 * IPPROTO_IP/IP_MULTICAST_IF
1215 * IPv6: Get the index of the interface to which the
1216 * InetAddress is bound
1217 * Set outgoing multicast interface using
1218 * IPPROTO_IPV6/IPV6_MULTICAST_IF
1219 *
1220 * SockOptions.IF_MULTICAST_IF2 :-
1221 * value is a NetworkInterface
1222 * IPv4: Obtain IP address bound to network interface
1223 * (NetworkInterface.addres[0])
1224 * set outgoing multicast interface using
1225 * IPPROTO_IP/IP_MULTICAST_IF
1226 * IPv6: Obtain NetworkInterface.index
1227 * Set outgoing multicast interface using
1228 * IPPROTO_IPV6/IPV6_MULTICAST_IF
1229 *
1230 */
setMulticastInterface(JNIEnv * env,jobject this,int fd,jint opt,jobject value)1231 static void setMulticastInterface(JNIEnv *env, jobject this, int fd,
1232 jint opt, jobject value)
1233 {
1234 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1235 /*
1236 * value is an InetAddress.
1237 */
1238 #ifdef AF_INET6
1239 #ifdef __linux__
1240 mcast_set_if_by_addr_v4(env, this, fd, value);
1241 if (ipv6_available()) {
1242 mcast_set_if_by_addr_v6(env, this, fd, value);
1243 }
1244 #else /* __linux__ not defined */
1245 if (ipv6_available()) {
1246 mcast_set_if_by_addr_v6(env, this, fd, value);
1247 } else {
1248 mcast_set_if_by_addr_v4(env, this, fd, value);
1249 }
1250 #endif /* __linux__ */
1251 #else
1252 mcast_set_if_by_addr_v4(env, this, fd, value);
1253 #endif /* AF_INET6 */
1254 }
1255
1256 if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1257 /*
1258 * value is a NetworkInterface.
1259 */
1260 #ifdef AF_INET6
1261 #ifdef __linux__
1262 mcast_set_if_by_if_v4(env, this, fd, value);
1263 if (ipv6_available()) {
1264 mcast_set_if_by_if_v6(env, this, fd, value);
1265 }
1266 #else /* __linux__ not defined */
1267 if (ipv6_available()) {
1268 mcast_set_if_by_if_v6(env, this, fd, value);
1269 } else {
1270 mcast_set_if_by_if_v4(env, this, fd, value);
1271 }
1272 #endif /* __linux__ */
1273 #else
1274 mcast_set_if_by_if_v4(env, this, fd, value);
1275 #endif /* AF_INET6 */
1276 }
1277 }
1278
1279 /*
1280 * Enable/disable local loopback of multicast datagrams.
1281 */
mcast_set_loop_v4(JNIEnv * env,jobject this,int fd,jobject value)1282 static void mcast_set_loop_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1283 jclass cls;
1284 jfieldID fid;
1285 jboolean on;
1286 char loopback;
1287
1288 cls = (*env)->FindClass(env, "java/lang/Boolean");
1289 CHECK_NULL(cls);
1290 fid = (*env)->GetFieldID(env, cls, "value", "Z");
1291 CHECK_NULL(fid);
1292
1293 on = (*env)->GetBooleanField(env, value, fid);
1294 loopback = (!on ? 1 : 0);
1295
1296 if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void *)&loopback, sizeof(char)) < 0) {
1297 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1298 return;
1299 }
1300 }
1301
1302 /*
1303 * Enable/disable local loopback of multicast datagrams.
1304 */
1305 #ifdef AF_INET6
mcast_set_loop_v6(JNIEnv * env,jobject this,int fd,jobject value)1306 static void mcast_set_loop_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1307 jclass cls;
1308 jfieldID fid;
1309 jboolean on;
1310 int loopback;
1311
1312 cls = (*env)->FindClass(env, "java/lang/Boolean");
1313 CHECK_NULL(cls);
1314 fid = (*env)->GetFieldID(env, cls, "value", "Z");
1315 CHECK_NULL(fid);
1316
1317 on = (*env)->GetBooleanField(env, value, fid);
1318 loopback = (!on ? 1 : 0);
1319
1320 if (NET_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const void *)&loopback, sizeof(int)) < 0) {
1321 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1322 return;
1323 }
1324
1325 }
1326 #endif /* AF_INET6 */
1327
1328 /*
1329 * Sets the multicast loopback mode.
1330 */
setMulticastLoopbackMode(JNIEnv * env,jobject this,int fd,jint opt,jobject value)1331 static void setMulticastLoopbackMode(JNIEnv *env, jobject this, int fd,
1332 jint opt, jobject value) {
1333 #ifdef AF_INET6
1334 #ifdef __linux__
1335 mcast_set_loop_v4(env, this, fd, value);
1336 if (ipv6_available()) {
1337 mcast_set_loop_v6(env, this, fd, value);
1338 }
1339 #else /* __linux__ not defined */
1340 if (ipv6_available()) {
1341 mcast_set_loop_v6(env, this, fd, value);
1342 } else {
1343 mcast_set_loop_v4(env, this, fd, value);
1344 }
1345 #endif /* __linux__ */
1346 #else
1347 mcast_set_loop_v4(env, this, fd, value);
1348 #endif /* AF_INET6 */
1349 }
1350
1351 /*
1352 * Class: java_net_PlainDatagramSocketImpl
1353 * Method: socketSetOption0
1354 * Signature: (ILjava/lang/Object;)V
1355 */
1356 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_socketSetOption0(JNIEnv * env,jobject this,jint opt,jobject value)1357 Java_java_net_PlainDatagramSocketImpl_socketSetOption0(JNIEnv *env,
1358 jobject this,
1359 jint opt,
1360 jobject value) {
1361 int fd;
1362 int level, optname, optlen;
1363 int optval;
1364 optlen = sizeof(int);
1365
1366 /*
1367 * Check that socket hasn't been closed
1368 */
1369 fd = getFD(env, this);
1370 if (fd < 0) {
1371 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1372 "Socket closed");
1373 return;
1374 }
1375
1376 /*
1377 * Check argument has been provided
1378 */
1379 if (IS_NULL(value)) {
1380 JNU_ThrowNullPointerException(env, "value argument");
1381 return;
1382 }
1383
1384 /*
1385 * Setting the multicast interface handled separately
1386 */
1387 if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1388 opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1389
1390 setMulticastInterface(env, this, fd, opt, value);
1391 return;
1392 }
1393
1394 /*
1395 * Setting the multicast loopback mode handled separately
1396 */
1397 if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) {
1398 setMulticastLoopbackMode(env, this, fd, opt, value);
1399 return;
1400 }
1401
1402 /*
1403 * Map the Java level socket option to the platform specific
1404 * level and option name.
1405 */
1406 if (NET_MapSocketOption(opt, &level, &optname)) {
1407 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
1408 return;
1409 }
1410
1411 switch (opt) {
1412 case java_net_SocketOptions_SO_SNDBUF :
1413 case java_net_SocketOptions_SO_RCVBUF :
1414 case java_net_SocketOptions_IP_TOS :
1415 {
1416 jclass cls;
1417 jfieldID fid;
1418
1419 cls = (*env)->FindClass(env, "java/lang/Integer");
1420 CHECK_NULL(cls);
1421 fid = (*env)->GetFieldID(env, cls, "value", "I");
1422 CHECK_NULL(fid);
1423
1424 optval = (*env)->GetIntField(env, value, fid);
1425 break;
1426 }
1427
1428 case java_net_SocketOptions_SO_REUSEADDR:
1429 case java_net_SocketOptions_SO_BROADCAST:
1430 {
1431 jclass cls;
1432 jfieldID fid;
1433 jboolean on;
1434
1435 cls = (*env)->FindClass(env, "java/lang/Boolean");
1436 CHECK_NULL(cls);
1437 fid = (*env)->GetFieldID(env, cls, "value", "Z");
1438 CHECK_NULL(fid);
1439
1440 on = (*env)->GetBooleanField(env, value, fid);
1441
1442 /* SO_REUSEADDR or SO_BROADCAST */
1443 optval = (on ? 1 : 0);
1444
1445 break;
1446 }
1447
1448 default :
1449 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1450 "Socket option not supported by PlainDatagramSocketImp");
1451 break;
1452
1453 }
1454
1455 if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {
1456 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1457 return;
1458 }
1459 }
1460
1461
1462 /*
1463 * Return the multicast interface:
1464 *
1465 * SocketOptions.IP_MULTICAST_IF
1466 * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF
1467 * Create InetAddress
1468 * IP_MULTICAST_IF returns struct ip_mreqn on 2.2
1469 * kernel but struct in_addr on 2.4 kernel
1470 * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
1471 * If index == 0 return InetAddress representing
1472 * anyLocalAddress.
1473 * If index > 0 query NetworkInterface by index
1474 * and returns addrs[0]
1475 *
1476 * SocketOptions.IP_MULTICAST_IF2
1477 * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF
1478 * Query NetworkInterface by IP address and
1479 * return the NetworkInterface that the address
1480 * is bound too.
1481 * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
1482 * (except Linux .2 kernel)
1483 * Query NetworkInterface by index and
1484 * return NetworkInterface.
1485 */
getMulticastInterface(JNIEnv * env,jobject this,int fd,jint opt)1486 jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, jint opt) {
1487 jboolean isIPV4 = JNI_TRUE;
1488
1489 #ifdef AF_INET6
1490 if (ipv6_available()) {
1491 isIPV4 = JNI_FALSE;
1492 }
1493 #endif
1494
1495 /*
1496 * IPv4 implementation
1497 */
1498 if (isIPV4) {
1499 static jclass inet4_class;
1500 static jmethodID inet4_ctrID;
1501
1502 static jclass ni_class;
1503 static jmethodID ni_ctrID;
1504 static jfieldID ni_indexID;
1505 static jfieldID ni_addrsID;
1506 static jfieldID ni_nameID;
1507
1508 jobjectArray addrArray;
1509 jobject addr;
1510 jobject ni;
1511 jobject ni_name;
1512
1513 struct in_addr in;
1514 struct in_addr *inP = ∈
1515 int len = sizeof(struct in_addr);
1516
1517 if (JVM_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1518 (char *)inP, &len) < 0) {
1519 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1520 "Error getting socket option");
1521 return NULL;
1522 }
1523
1524 /*
1525 * Construct and populate an Inet4Address
1526 */
1527 if (inet4_class == NULL) {
1528 jclass c = (*env)->FindClass(env, "java/net/Inet4Address");
1529 CHECK_NULL_RETURN(c, NULL);
1530 inet4_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1531 CHECK_NULL_RETURN(inet4_ctrID, NULL);
1532 inet4_class = (*env)->NewGlobalRef(env, c);
1533 CHECK_NULL_RETURN(inet4_class, NULL);
1534 }
1535 addr = (*env)->NewObject(env, inet4_class, inet4_ctrID, 0);
1536 CHECK_NULL_RETURN(addr, NULL);
1537
1538 setInetAddress_addr(env, addr, ntohl(in.s_addr));
1539 JNU_CHECK_EXCEPTION_RETURN(env, NULL);
1540
1541 /*
1542 * For IP_MULTICAST_IF return InetAddress
1543 */
1544 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1545 return addr;
1546 }
1547
1548 /*
1549 * For IP_MULTICAST_IF2 we get the NetworkInterface for
1550 * this address and return it
1551 */
1552 if (ni_class == NULL) {
1553 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1554 CHECK_NULL_RETURN(c, NULL);
1555 ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1556 CHECK_NULL_RETURN(ni_ctrID, NULL);
1557 ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1558 CHECK_NULL_RETURN(ni_indexID, NULL);
1559 ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1560 "[Ljava/net/InetAddress;");
1561 CHECK_NULL_RETURN(ni_addrsID, NULL);
1562 ni_nameID = (*env)->GetFieldID(env, c,"name", "Ljava/lang/String;");
1563 CHECK_NULL_RETURN(ni_nameID, NULL);
1564 ni_class = (*env)->NewGlobalRef(env, c);
1565 CHECK_NULL_RETURN(ni_class, NULL);
1566 }
1567 ni = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, addr);
1568 JNU_CHECK_EXCEPTION_RETURN(env, NULL);
1569 if (ni) {
1570 return ni;
1571 }
1572
1573 /*
1574 * The address doesn't appear to be bound at any known
1575 * NetworkInterface. Therefore we construct a NetworkInterface
1576 * with this address.
1577 */
1578 ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
1579 CHECK_NULL_RETURN(ni, NULL);
1580
1581 (*env)->SetIntField(env, ni, ni_indexID, -1);
1582 addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL);
1583 CHECK_NULL_RETURN(addrArray, NULL);
1584 (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
1585 (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
1586 ni_name = (*env)->NewStringUTF(env, "");
1587 if (ni_name != NULL) {
1588 (*env)->SetObjectField(env, ni, ni_nameID, ni_name);
1589 }
1590 return ni;
1591 }
1592
1593
1594 #ifdef AF_INET6
1595 /*
1596 * IPv6 implementation
1597 */
1598 if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) ||
1599 (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) {
1600
1601 static jclass ni_class;
1602 static jmethodID ni_ctrID;
1603 static jfieldID ni_indexID;
1604 static jfieldID ni_addrsID;
1605 static jclass ia_class;
1606 static jfieldID ni_nameID;
1607 static jmethodID ia_anyLocalAddressID;
1608
1609 int index = 0;
1610 int len = sizeof(index);
1611
1612 jobjectArray addrArray;
1613 jobject addr;
1614 jobject ni;
1615 jobject ni_name;
1616
1617 if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1618 (char*)&index, &len) < 0) {
1619 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1620 "Error getting socket option");
1621 return NULL;
1622 }
1623
1624 if (ni_class == NULL) {
1625 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1626 CHECK_NULL_RETURN(c, NULL);
1627 ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1628 CHECK_NULL_RETURN(ni_ctrID, NULL);
1629 ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1630 CHECK_NULL_RETURN(ni_indexID, NULL);
1631 ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1632 "[Ljava/net/InetAddress;");
1633 CHECK_NULL_RETURN(ni_addrsID, NULL);
1634
1635 ia_class = (*env)->FindClass(env, "java/net/InetAddress");
1636 CHECK_NULL_RETURN(ia_class, NULL);
1637 ia_class = (*env)->NewGlobalRef(env, ia_class);
1638 CHECK_NULL_RETURN(ia_class, NULL);
1639 ia_anyLocalAddressID = (*env)->GetStaticMethodID(env,
1640 ia_class,
1641 "anyLocalAddress",
1642 "()Ljava/net/InetAddress;");
1643 CHECK_NULL_RETURN(ia_anyLocalAddressID, NULL);
1644 ni_nameID = (*env)->GetFieldID(env, c,"name", "Ljava/lang/String;");
1645 CHECK_NULL_RETURN(ni_nameID, NULL);
1646 ni_class = (*env)->NewGlobalRef(env, c);
1647 CHECK_NULL_RETURN(ni_class, NULL);
1648 }
1649
1650 /*
1651 * If multicast to a specific interface then return the
1652 * interface (for IF2) or the any address on that interface
1653 * (for IF).
1654 */
1655 if (index > 0) {
1656 ni = Java_java_net_NetworkInterface_getByIndex0(env, ni_class,
1657 index);
1658 if (ni == NULL) {
1659 char errmsg[255];
1660 sprintf(errmsg,
1661 "IPV6_MULTICAST_IF returned index to unrecognized interface: %d",
1662 index);
1663 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg);
1664 return NULL;
1665 }
1666
1667 /*
1668 * For IP_MULTICAST_IF2 return the NetworkInterface
1669 */
1670 if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1671 return ni;
1672 }
1673
1674 /*
1675 * For IP_MULTICAST_IF return addrs[0]
1676 */
1677 addrArray = (*env)->GetObjectField(env, ni, ni_addrsID);
1678 if ((*env)->GetArrayLength(env, addrArray) < 1) {
1679 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1680 "IPV6_MULTICAST_IF returned interface without IP bindings");
1681 return NULL;
1682 }
1683
1684 addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
1685 return addr;
1686 }
1687
1688 /*
1689 * Multicast to any address - return anyLocalAddress
1690 * or a NetworkInterface with addrs[0] set to anyLocalAddress
1691 */
1692
1693 addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID,
1694 NULL);
1695 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1696 return addr;
1697 }
1698
1699 ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
1700 CHECK_NULL_RETURN(ni, NULL);
1701 (*env)->SetIntField(env, ni, ni_indexID, -1);
1702 addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL);
1703 CHECK_NULL_RETURN(addrArray, NULL);
1704 (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
1705 (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
1706 ni_name = (*env)->NewStringUTF(env, "");
1707 if (ni_name != NULL) {
1708 (*env)->SetObjectField(env, ni, ni_nameID, ni_name);
1709 }
1710 return ni;
1711 }
1712 #endif
1713 return NULL;
1714 }
1715
1716
1717
1718 /*
1719 * Returns relevant info as a jint.
1720 *
1721 * Class: java_net_PlainDatagramSocketImpl
1722 * Method: socketGetOption
1723 * Signature: (I)Ljava/lang/Object;
1724 */
1725 JNIEXPORT jobject JNICALL
Java_java_net_PlainDatagramSocketImpl_socketGetOption(JNIEnv * env,jobject this,jint opt)1726 Java_java_net_PlainDatagramSocketImpl_socketGetOption(JNIEnv *env, jobject this,
1727 jint opt) {
1728 int fd;
1729 int level, optname, optlen;
1730 union {
1731 int i;
1732 char c;
1733 } optval;
1734
1735 fd = getFD(env, this);
1736 if (fd < 0) {
1737 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1738 "socket closed");
1739 return NULL;
1740 }
1741
1742 /*
1743 * Handle IP_MULTICAST_IF separately
1744 */
1745 if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1746 opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1747 return getMulticastInterface(env, this, fd, opt);
1748
1749 }
1750
1751 /*
1752 * SO_BINDADDR implemented using getsockname
1753 */
1754 if (opt == java_net_SocketOptions_SO_BINDADDR) {
1755 /* find out local IP address */
1756 SOCKADDR him;
1757 socklen_t len = 0;
1758 int port;
1759 jobject iaObj;
1760
1761 len = SOCKADDR_LEN;
1762
1763 if (getsockname(fd, (struct sockaddr *)&him, &len) == -1) {
1764 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1765 "Error getting socket name");
1766 return NULL;
1767 }
1768 iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port);
1769
1770 return iaObj;
1771 }
1772
1773 /*
1774 * Map the Java level socket option to the platform specific
1775 * level and option name.
1776 */
1777 if (NET_MapSocketOption(opt, &level, &optname)) {
1778 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
1779 return NULL;
1780 }
1781
1782 if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP &&
1783 level == IPPROTO_IP) {
1784 optlen = sizeof(optval.c);
1785 } else {
1786 optlen = sizeof(optval.i);
1787 }
1788
1789 if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
1790 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1791 "Error getting socket option");
1792 return NULL;
1793 }
1794
1795 switch (opt) {
1796 case java_net_SocketOptions_IP_MULTICAST_LOOP:
1797 /* getLoopbackMode() returns true if IP_MULTICAST_LOOP disabled */
1798 if (level == IPPROTO_IP) {
1799 return createBoolean(env, (int)!optval.c);
1800 } else {
1801 return createBoolean(env, !optval.i);
1802 }
1803
1804 case java_net_SocketOptions_SO_BROADCAST:
1805 case java_net_SocketOptions_SO_REUSEADDR:
1806 return createBoolean(env, optval.i);
1807
1808 case java_net_SocketOptions_SO_SNDBUF:
1809 case java_net_SocketOptions_SO_RCVBUF:
1810 case java_net_SocketOptions_IP_TOS:
1811 return createInteger(env, optval.i);
1812
1813 }
1814
1815 /* should never reach here */
1816 return NULL;
1817 }
1818
1819 /*
1820 * Multicast-related calls
1821 */
1822
1823 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_setTTL(JNIEnv * env,jobject this,jbyte ttl)1824 Java_java_net_PlainDatagramSocketImpl_setTTL(JNIEnv *env, jobject this,
1825 jbyte ttl) {
1826 jint ittl = ttl;
1827 if (ittl < 0) {
1828 ittl += 0x100;
1829 }
1830 Java_java_net_PlainDatagramSocketImpl_setTimeToLive(env, this, ittl);
1831 }
1832
1833 /*
1834 * Set TTL for a socket. Throw exception if failed.
1835 */
setTTL(JNIEnv * env,int fd,jint ttl)1836 static void setTTL(JNIEnv *env, int fd, jint ttl) {
1837 char ittl = (char)ttl;
1838 if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl,
1839 sizeof(ittl)) < 0) {
1840 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1841 "Error setting socket option");
1842 }
1843 }
1844
1845 /*
1846 * Set hops limit for a socket. Throw exception if failed.
1847 */
1848 #ifdef AF_INET6
setHopLimit(JNIEnv * env,int fd,jint ttl)1849 static void setHopLimit(JNIEnv *env, int fd, jint ttl) {
1850 int ittl = (int)ttl;
1851 if (JVM_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1852 (char*)&ittl, sizeof(ittl)) < 0) {
1853 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1854 "Error setting socket option");
1855 }
1856 }
1857 #endif
1858
1859 /*
1860 * Class: java_net_PlainDatagramSocketImpl
1861 * Method: setTTL
1862 * Signature: (B)V
1863 */
1864 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_setTimeToLive(JNIEnv * env,jobject this,jint ttl)1865 Java_java_net_PlainDatagramSocketImpl_setTimeToLive(JNIEnv *env, jobject this,
1866 jint ttl) {
1867
1868 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1869 int fd;
1870 /* it is important to cast this to a char, otherwise setsockopt gets confused */
1871
1872 if (IS_NULL(fdObj)) {
1873 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1874 "Socket closed");
1875 return;
1876 } else {
1877 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1878 }
1879 /* setsockopt to be correct TTL */
1880 #ifdef AF_INET6
1881 #ifdef __linux__
1882 setTTL(env, fd, ttl);
1883 if (ipv6_available()) {
1884 setHopLimit(env, fd, ttl);
1885 }
1886 #else /* __linux__ not defined */
1887 if (ipv6_available()) {
1888 setHopLimit(env, fd, ttl);
1889 } else {
1890 setTTL(env, fd, ttl);
1891 }
1892 #endif /* __linux__ */
1893 #else
1894 setTTL(env, fd, ttl);
1895 #endif /* AF_INET6 */
1896 }
1897
1898 /*
1899 * Class: java_net_PlainDatagramSocketImpl
1900 * Method: getTTL
1901 * Signature: ()B
1902 */
1903 JNIEXPORT jbyte JNICALL
Java_java_net_PlainDatagramSocketImpl_getTTL(JNIEnv * env,jobject this)1904 Java_java_net_PlainDatagramSocketImpl_getTTL(JNIEnv *env, jobject this) {
1905 return (jbyte)Java_java_net_PlainDatagramSocketImpl_getTimeToLive(env, this);
1906 }
1907
1908
1909 /*
1910 * Class: java_net_PlainDatagramSocketImpl
1911 * Method: getTTL
1912 * Signature: ()B
1913 */
1914 JNIEXPORT jint JNICALL
Java_java_net_PlainDatagramSocketImpl_getTimeToLive(JNIEnv * env,jobject this)1915 Java_java_net_PlainDatagramSocketImpl_getTimeToLive(JNIEnv *env, jobject this) {
1916
1917 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1918 jint fd = -1;
1919
1920 if (IS_NULL(fdObj)) {
1921 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1922 "Socket closed");
1923 return -1;
1924 } else {
1925 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1926 }
1927 /* getsockopt of TTL */
1928 #ifdef AF_INET6
1929 if (ipv6_available()) {
1930 int ttl = 0;
1931 int len = sizeof(ttl);
1932
1933 if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1934 (char*)&ttl, &len) < 0) {
1935 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1936 "Error getting socket option");
1937 return -1;
1938 }
1939 return (jint)ttl;
1940 } else
1941 #endif /* AF_INET6 */
1942 {
1943 u_char ttl = 0;
1944 int len = sizeof(ttl);
1945 if (JVM_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
1946 (char*)&ttl, &len) < 0) {
1947 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1948 "Error getting socket option");
1949 return -1;
1950 }
1951 return (jint)ttl;
1952 }
1953 }
1954
1955
1956 /*
1957 * mcast_join_leave: Join or leave a multicast group.
1958 *
1959 * For IPv4 sockets use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
1960 * to join/leave multicast group.
1961 *
1962 * For IPv6 sockets use IPV6_ADD_MEMBERSHIP/IPV6_DROP_MEMBERSHIP socket option
1963 * to join/leave multicast group. If multicast group is an IPv4 address then
1964 * an IPv4-mapped address is used.
1965 *
1966 * On Linux with IPv6 if we wish to join/leave an IPv4 multicast group then
1967 * we must use the IPv4 socket options. This is because the IPv6 socket options
1968 * don't support IPv4-mapped addresses. This is true as per 2.2.19 and 2.4.7
1969 * kernel releases. In the future it's possible that IP_ADD_MEMBERSHIP
1970 * will be updated to return ENOPROTOOPT if uses with an IPv6 socket (Solaris
1971 * already does this). Thus to cater for this we first try with the IPv4
1972 * socket options and if they fail we use the IPv6 socket options. This
1973 * seems a reasonable failsafe solution.
1974 */
mcast_join_leave(JNIEnv * env,jobject this,jobject iaObj,jobject niObj,jboolean join)1975 static void mcast_join_leave(JNIEnv *env, jobject this,
1976 jobject iaObj, jobject niObj,
1977 jboolean join) {
1978
1979 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1980 jint fd;
1981 jint family;
1982 jint ipv6_join_leave;
1983
1984 if (IS_NULL(fdObj)) {
1985 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1986 "Socket closed");
1987 return;
1988 } else {
1989 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1990 }
1991 if (IS_NULL(iaObj)) {
1992 JNU_ThrowNullPointerException(env, "iaObj");
1993 return;
1994 }
1995
1996 /*
1997 * Determine if this is an IPv4 or IPv6 join/leave.
1998 */
1999 #ifdef AF_INET6
2000 ipv6_join_leave = ipv6_available();
2001
2002 #ifdef __linux__
2003 family = getInetAddress_family(env, iaObj);
2004 JNU_CHECK_EXCEPTION(env);
2005 if (family == IPv4) {
2006 JNU_CHECK_EXCEPTION(env);
2007 ipv6_join_leave = JNI_FALSE;
2008 }
2009 #endif
2010
2011 #else
2012 /*
2013 * IPv6 not compiled in
2014 */
2015 ipv6_join_leave = JNI_FALSE;
2016 #endif
2017
2018 /*
2019 * For IPv4 join use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
2020 *
2021 * On Linux if IPv4 or IPv6 use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP
2022 */
2023 if (!ipv6_join_leave) {
2024 #ifdef __linux__
2025 struct ip_mreqn mname;
2026 #else
2027 struct ip_mreq mname;
2028 #endif
2029 int mname_len;
2030
2031 /*
2032 * joinGroup(InetAddress, NetworkInterface) implementation :-
2033 *
2034 * Linux/IPv6: use ip_mreqn structure populated with multicast
2035 * address and interface index.
2036 *
2037 * IPv4: use ip_mreq structure populated with multicast
2038 * address and first address obtained from
2039 * NetworkInterface
2040 */
2041 if (niObj != NULL) {
2042 #if defined(__linux__) && defined(AF_INET6)
2043 if (ipv6_available()) {
2044 static jfieldID ni_indexID;
2045
2046 if (ni_indexID == NULL) {
2047 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
2048 CHECK_NULL(c);
2049 ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
2050 CHECK_NULL(ni_indexID);
2051 }
2052
2053 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
2054 JNU_CHECK_EXCEPTION(env);
2055 mname.imr_address.s_addr = 0;
2056 mname.imr_ifindex = (*env)->GetIntField(env, niObj, ni_indexID);
2057 mname_len = sizeof(struct ip_mreqn);
2058 } else
2059 #endif
2060 {
2061 jobjectArray addrArray = (*env)->GetObjectField(env, niObj, ni_addrsID);
2062 jobject addr;
2063
2064 if ((*env)->GetArrayLength(env, addrArray) < 1) {
2065 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2066 "bad argument for IP_ADD_MEMBERSHIP: "
2067 "No IP addresses bound to interface");
2068 return;
2069 }
2070 addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
2071
2072 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
2073 JNU_CHECK_EXCEPTION(env);
2074 #ifdef __linux__
2075 mname.imr_address.s_addr = htonl(getInetAddress_addr(env, addr));
2076 JNU_CHECK_EXCEPTION(env);
2077 #else
2078 mname.imr_interface.s_addr = htonl(getInetAddress_addr(env, addr));
2079 JNU_CHECK_EXCEPTION(env);
2080 #endif
2081 mname_len = sizeof(struct ip_mreq);
2082 }
2083 }
2084
2085
2086 /*
2087 * joinGroup(InetAddress) implementation :-
2088 *
2089 * Linux/IPv6: use ip_mreqn structure populated with multicast
2090 * address and interface index. index obtained
2091 * from cached value or IPV6_MULTICAST_IF.
2092 *
2093 * IPv4: use ip_mreq structure populated with multicast
2094 * address and local address obtained from
2095 * IP_MULTICAST_IF. On Linux IP_MULTICAST_IF
2096 * returns different structure depending on
2097 * kernel.
2098 */
2099
2100 if (niObj == NULL) {
2101
2102 #if defined(__linux__) && defined(AF_INET6)
2103 if (ipv6_available()) {
2104
2105 int index;
2106 int len = sizeof(index);
2107
2108 if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
2109 (char*)&index, &len) < 0) {
2110 NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
2111 return;
2112 }
2113
2114 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
2115 JNU_CHECK_EXCEPTION(env);
2116 mname.imr_address.s_addr = 0 ;
2117 mname.imr_ifindex = index;
2118 mname_len = sizeof(struct ip_mreqn);
2119 } else
2120 #endif
2121 {
2122 struct in_addr in;
2123 struct in_addr *inP = ∈
2124 socklen_t len = sizeof(struct in_addr);
2125
2126 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)inP, &len) < 0) {
2127 NET_ThrowCurrent(env, "getsockopt IP_MULTICAST_IF failed");
2128 return;
2129 }
2130
2131 #ifdef __linux__
2132 mname.imr_address.s_addr = in.s_addr;
2133
2134 #else
2135 mname.imr_interface.s_addr = in.s_addr;
2136 #endif
2137 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
2138 JNU_CHECK_EXCEPTION(env);
2139 mname_len = sizeof(struct ip_mreq);
2140 }
2141 }
2142
2143
2144 /*
2145 * Join the multicast group.
2146 */
2147 if (JVM_SetSockOpt(fd, IPPROTO_IP, (join ? IP_ADD_MEMBERSHIP:IP_DROP_MEMBERSHIP),
2148 (char *) &mname, mname_len) < 0) {
2149
2150 /*
2151 * If IP_ADD_MEMBERSHIP returns ENOPROTOOPT on Linux and we've got
2152 * IPv6 enabled then it's possible that the kernel has been fixed
2153 * so we switch to IPV6_ADD_MEMBERSHIP socket option.
2154 * As of 2.4.7 kernel IPV6_ADD_MEMBERSHIP can't handle IPv4-mapped
2155 * addresses so we have to use IP_ADD_MEMBERSHIP for IPv4 multicast
2156 * groups. However if the socket is an IPv6 socket then then setsockopt
2157 * should return ENOPROTOOPT. We assume this will be fixed in Linux
2158 * at some stage.
2159 */
2160 #if defined(__linux__) && defined(AF_INET6)
2161 if (errno == ENOPROTOOPT) {
2162 if (ipv6_available()) {
2163 ipv6_join_leave = JNI_TRUE;
2164 errno = 0;
2165 } else {
2166 errno = ENOPROTOOPT; /* errno can be changed by ipv6_available */
2167 }
2168 }
2169 #endif
2170 if (errno) {
2171 if (join) {
2172 NET_ThrowCurrent(env, "setsockopt IP_ADD_MEMBERSHIP failed");
2173 } else {
2174 if (errno == ENOENT)
2175 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2176 "Not a member of the multicast group");
2177 else
2178 NET_ThrowCurrent(env, "setsockopt IP_DROP_MEMBERSHIP failed");
2179 }
2180 }
2181 }
2182
2183 /*
2184 * If we haven't switched to IPv6 socket option then we're done.
2185 */
2186 if (!ipv6_join_leave) {
2187 return;
2188 }
2189 }
2190
2191
2192 /*
2193 * IPv6 join. If it's an IPv4 multicast group then we use an IPv4-mapped
2194 * address.
2195 */
2196 #ifdef AF_INET6
2197 {
2198 struct ipv6_mreq mname6;
2199 jbyteArray ipaddress;
2200 jbyte caddr[16];
2201 jint family;
2202 jint address;
2203 family = getInetAddress_family(env, iaObj) == IPv4? AF_INET : AF_INET6;
2204 JNU_CHECK_EXCEPTION(env);
2205 if (family == AF_INET) { /* will convert to IPv4-mapped address */
2206 memset((char *) caddr, 0, 16);
2207 address = getInetAddress_addr(env, iaObj);
2208
2209 JNU_CHECK_EXCEPTION(env);
2210 caddr[10] = 0xff;
2211 caddr[11] = 0xff;
2212
2213 caddr[12] = ((address >> 24) & 0xff);
2214 caddr[13] = ((address >> 16) & 0xff);
2215 caddr[14] = ((address >> 8) & 0xff);
2216 caddr[15] = (address & 0xff);
2217 } else {
2218 getInet6Address_ipaddress(env, iaObj, (char *) caddr);
2219 }
2220
2221 memcpy((void *)&(mname6.ipv6mr_multiaddr), caddr, sizeof(struct in6_addr));
2222 if (IS_NULL(niObj)) {
2223 int index;
2224 int len = sizeof(index);
2225
2226 if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
2227 (char*)&index, &len) < 0) {
2228 NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
2229 return;
2230 }
2231
2232 #ifdef __linux__
2233 /*
2234 * On 2.4.8+ if we join a group with the interface set to 0
2235 * then the kernel records the interface it decides. This causes
2236 * subsequent leave groups to fail as there is no match. Thus we
2237 * pick the interface if there is a matching route.
2238 */
2239 if (index == 0) {
2240 int rt_index = getDefaultIPv6Interface(&(mname6.ipv6mr_multiaddr));
2241 if (rt_index > 0) {
2242 index = rt_index;
2243 }
2244 }
2245 #endif
2246 #ifdef _ALLBSD_SOURCE
2247 if (family == AF_INET6 && index == 0) {
2248 index = getDefaultScopeID(env);
2249 }
2250 #endif
2251 mname6.ipv6mr_interface = index;
2252 } else {
2253 jint idx = (*env)->GetIntField(env, niObj, ni_indexID);
2254 mname6.ipv6mr_interface = idx;
2255 }
2256
2257 #if defined(_ALLBSD_SOURCE)
2258 #define ADD_MEMBERSHIP IPV6_JOIN_GROUP
2259 #define DRP_MEMBERSHIP IPV6_LEAVE_GROUP
2260 #define S_ADD_MEMBERSHIP "IPV6_JOIN_GROUP"
2261 #define S_DRP_MEMBERSHIP "IPV6_LEAVE_GROUP"
2262 #else
2263 #define ADD_MEMBERSHIP IPV6_ADD_MEMBERSHIP
2264 #define DRP_MEMBERSHIP IPV6_DROP_MEMBERSHIP
2265 #define S_ADD_MEMBERSHIP "IPV6_ADD_MEMBERSHIP"
2266 #define S_DRP_MEMBERSHIP "IPV6_DROP_MEMBERSHIP"
2267 #endif
2268
2269 /* Join the multicast group */
2270 if (JVM_SetSockOpt(fd, IPPROTO_IPV6, (join ? ADD_MEMBERSHIP : DRP_MEMBERSHIP),
2271 (char *) &mname6, sizeof (mname6)) < 0) {
2272
2273 if (join) {
2274 NET_ThrowCurrent(env, "setsockopt " S_ADD_MEMBERSHIP " failed");
2275 } else {
2276 if (errno == ENOENT) {
2277 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2278 "Not a member of the multicast group");
2279 } else {
2280 NET_ThrowCurrent(env, "setsockopt " S_DRP_MEMBERSHIP " failed");
2281 }
2282 }
2283 }
2284 }
2285 #endif
2286 }
2287
2288 /*
2289 * Class: java_net_PlainDatagramSocketImpl
2290 * Method: join
2291 * Signature: (Ljava/net/InetAddress;)V
2292 */
2293 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_join(JNIEnv * env,jobject this,jobject iaObj,jobject niObj)2294 Java_java_net_PlainDatagramSocketImpl_join(JNIEnv *env, jobject this,
2295 jobject iaObj, jobject niObj)
2296 {
2297 mcast_join_leave(env, this, iaObj, niObj, JNI_TRUE);
2298 }
2299
2300 /*
2301 * Class: java_net_PlainDatagramSocketImpl
2302 * Method: leave
2303 * Signature: (Ljava/net/InetAddress;)V
2304 */
2305 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_leave(JNIEnv * env,jobject this,jobject iaObj,jobject niObj)2306 Java_java_net_PlainDatagramSocketImpl_leave(JNIEnv *env, jobject this,
2307 jobject iaObj, jobject niObj)
2308 {
2309 mcast_join_leave(env, this, iaObj, niObj, JNI_FALSE);
2310 }
2311
2312 /*
2313 * Class: java_net_PlainDatagramSocketImpl
2314 * Method: dataAvailable
2315 * Signature: ()I
2316 */
2317 JNIEXPORT jint JNICALL
Java_java_net_PlainDatagramSocketImpl_dataAvailable(JNIEnv * env,jobject this)2318 Java_java_net_PlainDatagramSocketImpl_dataAvailable(JNIEnv *env, jobject this)
2319 {
2320 int fd, retval;
2321
2322 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
2323
2324 if (IS_NULL(fdObj)) {
2325 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2326 "Socket closed");
2327 return -1;
2328 }
2329 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
2330
2331 if (ioctl(fd, FIONREAD, &retval) < 0) {
2332 return -1;
2333 }
2334 return retval;
2335 }
2336