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