blob: 149827f59c5ba0edc521cf6f4035ea71cb658a25 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26#include <windows.h>
27#include <winsock2.h>
28#include <ctype.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <malloc.h>
32#include <sys/types.h>
33
34#include "java_net_InetAddress.h"
35#include "java_net_Inet4AddressImpl.h"
36#include "net_util.h"
37#include "icmp.h"
38
39
40/*
41 * Returns true if hostname is in dotted IP address format. Note that this
42 * function performs a syntax check only. For each octet it just checks that
43 * the octet is at most 3 digits.
44 */
45jboolean isDottedIPAddress(const char *hostname, unsigned int *addrp) {
46 char *c = (char *)hostname;
47 int octets = 0;
48 unsigned int cur = 0;
49 int digit_cnt = 0;
50
51 while (*c) {
52 if (*c == '.') {
53 if (digit_cnt == 0) {
54 return JNI_FALSE;
55 } else {
56 if (octets < 4) {
57 addrp[octets++] = cur;
58 cur = 0;
59 digit_cnt = 0;
60 } else {
61 return JNI_FALSE;
62 }
63 }
64 c++;
65 continue;
66 }
67
68 if ((*c < '0') || (*c > '9')) {
69 return JNI_FALSE;
70 }
71
72 digit_cnt++;
73 if (digit_cnt > 3) {
74 return JNI_FALSE;
75 }
76
77 /* don't check if current octet > 255 */
78 cur = cur*10 + (*c - '0');
79
80 /* Move onto next character and check for EOF */
81 c++;
82 if (*c == '\0') {
83 if (octets < 4) {
84 addrp[octets++] = cur;
85 } else {
86 return JNI_FALSE;
87 }
88 }
89 }
90
91 return (jboolean)(octets == 4);
92}
93
94/*
95 * Inet4AddressImpl
96 */
97
98/*
99 * Class: java_net_Inet4AddressImpl
100 * Method: getLocalHostName
101 * Signature: ()Ljava/lang/String;
102 */
103JNIEXPORT jstring JNICALL
104Java_java_net_Inet4AddressImpl_getLocalHostName (JNIEnv *env, jobject this) {
105 char hostname[256];
106
107 if (gethostname(hostname, sizeof hostname) == -1) {
108 strcpy(hostname, "localhost");
109 }
110 return JNU_NewStringPlatform(env, hostname);
111}
112
113static jclass ni_iacls;
114static jclass ni_ia4cls;
115static jmethodID ni_ia4ctrID;
116static jfieldID ni_iaaddressID;
117static jfieldID ni_iahostID;
118static jfieldID ni_iafamilyID;
119static int initialized = 0;
120
121/*
122 * Find an internet address for a given hostname. Not this this
123 * code only works for addresses of type INET. The translation
124 * of %d.%d.%d.%d to an address (int) occurs in java now, so the
125 * String "host" shouldn't be a %d.%d.%d.%d string. The only
126 * exception should be when any of the %d are out of range and
127 * we fallback to a lookup.
128 *
129 * Class: java_net_Inet4AddressImpl
130 * Method: lookupAllHostAddr
131 * Signature: (Ljava/lang/String;)[[B
132 *
133 * This is almost shared code
134 */
135
136JNIEXPORT jobjectArray JNICALL
137Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
138 jstring host) {
139 const char *hostname;
140 jobject name;
141 struct hostent *hp;
142 unsigned int addr[4];
143
144 jobjectArray ret = NULL;
145 jclass byteArrayCls;
146
147 if (!initialized) {
148 ni_iacls = (*env)->FindClass(env, "java/net/InetAddress");
149 ni_iacls = (*env)->NewGlobalRef(env, ni_iacls);
150 ni_ia4cls = (*env)->FindClass(env, "java/net/Inet4Address");
151 ni_ia4cls = (*env)->NewGlobalRef(env, ni_ia4cls);
152 ni_ia4ctrID = (*env)->GetMethodID(env, ni_ia4cls, "<init>", "()V");
153 ni_iaaddressID = (*env)->GetFieldID(env, ni_iacls, "address", "I");
154 ni_iafamilyID = (*env)->GetFieldID(env, ni_iacls, "family", "I");
155 ni_iahostID = (*env)->GetFieldID(env, ni_iacls, "hostName", "Ljava/lang/String;");
156 initialized = 1;
157 }
158
159 if (IS_NULL(host)) {
160 JNU_ThrowNullPointerException(env, "host argument");
161 return NULL;
162 }
163 hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
164 CHECK_NULL_RETURN(hostname, NULL);
165
166 /*
167 * The NT/2000 resolver tolerates a space in front of localhost. This
168 * is not consistent with other implementations of gethostbyname.
169 * In addition we must do a white space check on Solaris to avoid a
170 * bug whereby 0.0.0.0 is returned if any host name has a white space.
171 */
172 if (isspace(hostname[0])) {
173 JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", hostname);
174 goto cleanupAndReturn;
175 }
176
177 /*
178 * If the format is x.x.x.x then don't use gethostbyname as Windows
179 * is unable to handle octets which are out of range.
180 */
181 if (isDottedIPAddress(hostname, &addr[0])) {
182 unsigned int address;
183 jobject iaObj;
184
185 /*
186 * Are any of the octets out of range?
187 */
188 if (addr[0] > 255 || addr[1] > 255 || addr[2] > 255 || addr[3] > 255) {
189 JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", hostname);
190 goto cleanupAndReturn;
191 }
192
193 /*
194 * Return an byte array with the populated address.
195 */
196 address = (addr[3]<<24) & 0xff000000;
197 address |= (addr[2]<<16) & 0xff0000;
198 address |= (addr[1]<<8) & 0xff00;
199 address |= addr[0];
200
201 ret = (*env)->NewObjectArray(env, 1, ni_iacls, NULL);
202
203 if (IS_NULL(ret)) {
204 goto cleanupAndReturn;
205 }
206
207 iaObj = (*env)->NewObject(env, ni_ia4cls, ni_ia4ctrID);
208 if (IS_NULL(iaObj)) {
209 ret = NULL;
210 goto cleanupAndReturn;
211 }
212 (*env)->SetIntField(env, iaObj, ni_iaaddressID,
213 ntohl(address));
214 (*env)->SetObjectArrayElement(env, ret, 0, iaObj);
215 JNU_ReleaseStringPlatformChars(env, host, hostname);
216 return ret;
217 }
218
219 /*
220 * Perform the lookup
221 */
222 if ((hp = gethostbyname((char*)hostname)) != NULL) {
223 struct in_addr **addrp = (struct in_addr **) hp->h_addr_list;
224 int len = sizeof(struct in_addr);
225 int i = 0;
226
227 while (*addrp != (struct in_addr *) 0) {
228 i++;
229 addrp++;
230 }
231
232 name = (*env)->NewStringUTF(env, hostname);
233 if (IS_NULL(name)) {
234 goto cleanupAndReturn;
235 }
236 ret = (*env)->NewObjectArray(env, i, ni_iacls, NULL);
237
238 if (IS_NULL(ret)) {
239 goto cleanupAndReturn;
240 }
241
242 addrp = (struct in_addr **) hp->h_addr_list;
243 i = 0;
244 while (*addrp != (struct in_addr *) 0) {
245 jobject iaObj = (*env)->NewObject(env, ni_ia4cls, ni_ia4ctrID);
246 if (IS_NULL(iaObj)) {
247 ret = NULL;
248 goto cleanupAndReturn;
249 }
250 (*env)->SetIntField(env, iaObj, ni_iaaddressID,
251 ntohl((*addrp)->s_addr));
252 (*env)->SetObjectField(env, iaObj, ni_iahostID, name);
253 (*env)->SetObjectArrayElement(env, ret, i, iaObj);
254 addrp++;
255 i++;
256 }
257 } else {
258 JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", hostname);
259 }
260
261cleanupAndReturn:
262 JNU_ReleaseStringPlatformChars(env, host, hostname);
263 return ret;
264}
265
266/*
267 * Class: java_net_Inet4AddressImpl
268 * Method: getHostByAddr
269 * Signature: (I)Ljava/lang/String;
270 */
271JNIEXPORT jstring JNICALL
272Java_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this,
273 jbyteArray addrArray) {
274 struct hostent *hp;
275 jbyte caddr[4];
276 jint addr;
277 (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
278 addr = ((caddr[0]<<24) & 0xff000000);
279 addr |= ((caddr[1] <<16) & 0xff0000);
280 addr |= ((caddr[2] <<8) & 0xff00);
281 addr |= (caddr[3] & 0xff);
282 addr = htonl(addr);
283
284 hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
285 if (hp == NULL) {
286 JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", 0);
287 return NULL;
288 }
289 if (hp->h_name == NULL) { /* Deal with bug in Windows XP */
290 JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", 0);
291 return NULL;
292 }
293 return JNU_NewStringPlatform(env, hp->h_name);
294}
295
296
297/**
298 * ping implementation.
299 * Send a ICMP_ECHO_REQUEST packet every second until either the timeout
300 * expires or a answer is received.
301 * Returns true is an ECHO_REPLY is received, otherwise, false.
302 */
303static jboolean
304ping4(JNIEnv *env, jint fd, struct sockaddr_in* him, jint timeout,
305 struct sockaddr_in* netif, jint ttl) {
306 jint size;
307 jint n, len, hlen1, icmplen;
308 char sendbuf[1500];
309 char recvbuf[1500];
310 struct icmp *icmp;
311 struct ip *ip;
312 WSAEVENT hEvent;
313 struct sockaddr sa_recv;
314 jint tmout2;
315 u_short pid, seq;
316 int read_rv = 0;
317
318 /* Initialize the sequence number to a suitable random number and
319 shift right one place to allow sufficient room for increamenting. */
320 seq = ((unsigned short)rand()) >> 1;
321
322 /* icmp_id is a 16 bit data type, therefore down cast the pid */
323 pid = (u_short) getpid();
324 size = 60*1024;
325 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char *) &size, sizeof(size));
326 /**
327 * A TTL was specified, let's set the socket option.
328 */
329 if (ttl > 0) {
330 setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *) &ttl, sizeof(ttl));
331 }
332
333 /**
334 * A network interface was specified, let's bind to it.
335 */
336 if (netif != NULL) {
337 if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
338 NET_ThrowNew(env, WSAGetLastError(), "Can't bind socket");
339 closesocket(fd);
340 return JNI_FALSE;
341 }
342 }
343
344 /**
345 * Let's make the socket non blocking
346 */
347 hEvent = WSACreateEvent();
348 WSAEventSelect(fd, hEvent, FD_READ|FD_CONNECT|FD_CLOSE);
349
350 /**
351 * send 1 ICMP REQUEST every second until either we get a valid reply
352 * or the timeout expired.
353 */
354 do {
355 /**
356 * construct the ICMP header
357 */
358 memset(sendbuf, 0, 1500);
359 icmp = (struct icmp *) sendbuf;
360 icmp->icmp_type = ICMP_ECHO;
361 icmp->icmp_code = 0;
362 icmp->icmp_id = htons(pid);
363 icmp->icmp_seq = htons(seq);
364 /**
365 * checksum has to be set to zero before we can calculate the
366 * real checksum!
367 */
368 icmp->icmp_cksum = 0;
369 icmp->icmp_cksum = in_cksum((u_short *)icmp, 64);
370 /**
371 * Ping!
372 */
373 n = sendto(fd, sendbuf, 64, 0, (struct sockaddr *)him,
374 sizeof(struct sockaddr));
375 if (n < 0 && WSAGetLastError() != WSAEWOULDBLOCK) {
376 NET_ThrowNew(env, WSAGetLastError(), "Can't send ICMP packet");
377 closesocket(fd);
378 WSACloseEvent(hEvent);
379 return JNI_FALSE;
380 }
381
382 /*
383 * wait for 1 second at most
384 */
385 tmout2 = timeout > 1000 ? 1000 : timeout;
386 do {
387 tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2);
388 if (tmout2 >= 0) {
389 len = sizeof(sa_recv);
390 n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, &sa_recv, &len);
391 ip = (struct ip*) recvbuf;
392 hlen1 = (ip->ip_hl) << 2;
393 icmp = (struct icmp *) (recvbuf + hlen1);
394 icmplen = n - hlen1;
395 /**
396 * Is that a proper ICMP reply?
397 */
398 if (icmplen >= 8 && icmp->icmp_type == ICMP_ECHOREPLY &&
399 (ntohs(icmp->icmp_seq) == seq) && (ntohs(icmp->icmp_id) == pid)) {
400 closesocket(fd);
401 WSACloseEvent(hEvent);
402 return JNI_TRUE;
403 }
404 }
405 } while (tmout2 > 0);
406 timeout -= 1000;
407 seq++;
408 } while (timeout > 0);
409 closesocket(fd);
410 WSACloseEvent(hEvent);
411 return JNI_FALSE;
412}
413
414/*
415 * Class: java_net_Inet4AddressImpl
416 * Method: isReachable0
417 * Signature: ([bI[bI)Z
418 */
419JNIEXPORT jboolean JNICALL
420Java_java_net_Inet4AddressImpl_isReachable0(JNIEnv *env, jobject this,
421 jbyteArray addrArray,
422 jint timeout,
423 jbyteArray ifArray,
424 jint ttl) {
425 jint addr;
426 jbyte caddr[4];
427 jint fd;
428 struct sockaddr_in him;
429 struct sockaddr_in* netif = NULL;
430 struct sockaddr_in inf;
431 int len = 0;
432 WSAEVENT hEvent;
433 int connect_rv = -1;
434 int sz;
435
436 /**
437 * Convert IP address from byte array to integer
438 */
439 sz = (*env)->GetArrayLength(env, addrArray);
440 if (sz != 4) {
441 return JNI_FALSE;
442 }
443 memset((char *) &him, 0, sizeof(him));
444 memset((char *) caddr, 0, sizeof(caddr));
445 (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
446 addr = ((caddr[0]<<24) & 0xff000000);
447 addr |= ((caddr[1] <<16) & 0xff0000);
448 addr |= ((caddr[2] <<8) & 0xff00);
449 addr |= (caddr[3] & 0xff);
450 addr = htonl(addr);
451 /**
452 * Socket address
453 */
454 him.sin_addr.s_addr = addr;
455 him.sin_family = AF_INET;
456 len = sizeof(him);
457
458 /**
459 * If a network interface was specified, let's convert its address
460 * as well.
461 */
462 if (!(IS_NULL(ifArray))) {
463 memset((char *) caddr, 0, sizeof(caddr));
464 (*env)->GetByteArrayRegion(env, ifArray, 0, 4, caddr);
465 addr = ((caddr[0]<<24) & 0xff000000);
466 addr |= ((caddr[1] <<16) & 0xff0000);
467 addr |= ((caddr[2] <<8) & 0xff00);
468 addr |= (caddr[3] & 0xff);
469 addr = htonl(addr);
470 inf.sin_addr.s_addr = addr;
471 inf.sin_family = AF_INET;
472 inf.sin_port = 0;
473 netif = &inf;
474 }
475
476#if 0
477 /*
478 * Windows implementation of ICMP & RAW sockets is too unreliable for now.
479 * Therefore it's best not to try it at all and rely only on TCP
480 * We may revisit and enable this code in the future.
481 */
482
483 /*
484 * Let's try to create a RAW socket to send ICMP packets
485 * This usually requires "root" privileges, so it's likely to fail.
486 */
487 fd = NET_Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
488 if (fd != -1) {
489 /*
490 * It didn't fail, so we can use ICMP_ECHO requests.
491 */
492 return ping4(env, fd, &him, timeout, netif, ttl);
493 }
494#endif
495
496 /*
497 * Can't create a raw socket, so let's try a TCP socket
498 */
499 fd = NET_Socket(AF_INET, SOCK_STREAM, 0);
500 if (fd == JVM_IO_ERR) {
501 /* note: if you run out of fds, you may not be able to load
502 * the exception class, and get a NoClassDefFoundError
503 * instead.
504 */
505 NET_ThrowNew(env, WSAGetLastError(), "Can't create socket");
506 return JNI_FALSE;
507 }
508 if (ttl > 0) {
509 setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *)&ttl, sizeof(ttl));
510 }
511 /*
512 * A network interface was specified, so let's bind to it.
513 */
514 if (netif != NULL) {
515 if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
516 NET_ThrowNew(env, WSAGetLastError(), "Can't bind socket");
517 closesocket(fd);
518 return JNI_FALSE;
519 }
520 }
521
522 /*
523 * Make the socket non blocking so we can use select/poll.
524 */
525 hEvent = WSACreateEvent();
526 WSAEventSelect(fd, hEvent, FD_READ|FD_CONNECT|FD_CLOSE);
527
528 /* no need to use NET_Connect as non-blocking */
529 him.sin_port = htons(7); /* Echo */
530 connect_rv = connect(fd, (struct sockaddr *)&him, len);
531
532 /**
533 * connection established or refused immediately, either way it means
534 * we were able to reach the host!
535 */
536 if (connect_rv == 0 || WSAGetLastError() == WSAECONNREFUSED) {
537 WSACloseEvent(hEvent);
538 closesocket(fd);
539 return JNI_TRUE;
540 } else {
541 int optlen;
542
543 switch (WSAGetLastError()) {
544 case WSAEHOSTUNREACH: /* Host Unreachable */
545 case WSAENETUNREACH: /* Network Unreachable */
546 case WSAENETDOWN: /* Network is down */
547 case WSAEPFNOSUPPORT: /* Protocol Family unsupported */
548 WSACloseEvent(hEvent);
549 closesocket(fd);
550 return JNI_FALSE;
551 }
552
553 if (WSAGetLastError() != WSAEWOULDBLOCK) {
554 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
555 "connect failed");
556 WSACloseEvent(hEvent);
557 closesocket(fd);
558 return JNI_FALSE;
559 }
560
561 timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);
562
563 /* has connection been established */
564
565 if (timeout >= 0) {
566 optlen = sizeof(connect_rv);
567 if (JVM_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
568 &optlen) <0) {
569 connect_rv = WSAGetLastError();
570 }
571
572 if (connect_rv == 0 || connect_rv == WSAECONNREFUSED) {
573 WSACloseEvent(hEvent);
574 closesocket(fd);
575 return JNI_TRUE;
576 }
577 }
578 }
579 WSACloseEvent(hEvent);
580 closesocket(fd);
581 return JNI_FALSE;
582}