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