1 /* 2 * Copyright 2000-2009 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 <errno.h> 27 #include <sys/time.h> 28 #include <sys/types.h> 29 #include <sys/socket.h> 30 #include <netinet/in_systm.h> 31 #include <netinet/in.h> 32 #include <netinet/ip.h> 33 #include <netinet/ip_icmp.h> 34 #include <netdb.h> 35 #include <string.h> 36 #include <stdlib.h> 37 #include <ctype.h> 38 39 #include "jvm.h" 40 #include "jni_util.h" 41 #include "net_util.h" 42 43 #include "java_net_Inet4AddressImpl.h" 44 45 /* the initial size of our hostent buffers */ 46 #define HENT_BUF_SIZE 1024 47 #define BIG_HENT_BUF_SIZE 10240 /* a jumbo-sized one */ 48 49 /************************************************************************ 50 * Inet4AddressImpl 51 */ 52 53 /* 54 * Class: java_net_Inet4AddressImpl 55 * Method: getLocalHostName 56 * Signature: ()Ljava/lang/String; 57 */ 58 JNIEXPORT jstring JNICALL 59 Java_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) { 60 char hostname[MAXHOSTNAMELEN+1]; 61 62 hostname[0] = '\0'; 63 if (JVM_GetHostName(hostname, MAXHOSTNAMELEN)) { 64 /* Something went wrong, maybe networking is not setup? */ 65 strcpy(hostname, "localhost"); 66 } else { 67 #ifdef __linux__ 68 /* On Linux gethostname() says "host.domain.sun.com". On 69 * Solaris gethostname() says "host", so extra work is needed. 70 */ 71 #else 72 /* Solaris doesn't want to give us a fully qualified domain name. 73 * We do a reverse lookup to try and get one. This works 74 * if DNS occurs before NIS in /etc/resolv.conf, but fails 75 * if NIS comes first (it still gets only a partial name). 76 * We use thread-safe system calls. 77 */ 78 #endif /* __linux__ */ 79 struct hostent res, res2, *hp; 80 char buf[HENT_BUF_SIZE]; 81 char buf2[HENT_BUF_SIZE]; 82 int h_error=0; 83 84 #ifdef __GLIBC__ 85 gethostbyname_r(hostname, &res, buf, sizeof(buf), &hp, &h_error); 86 #else 87 hp = gethostbyname_r(hostname, &res, buf, sizeof(buf), &h_error); 88 #endif 89 if (hp) { 90 #ifdef __GLIBC__ 91 gethostbyaddr_r(hp->h_addr, hp->h_length, AF_INET, 92 &res2, buf2, sizeof(buf2), &hp, &h_error); 93 #else 94 hp = gethostbyaddr_r(hp->h_addr, hp->h_length, AF_INET, 95 &res2, buf2, sizeof(buf2), &h_error); 96 #endif 97 if (hp) { 98 /* 99 * If gethostbyaddr_r() found a fully qualified host name, 100 * returns that name. Otherwise, returns the hostname 101 * found by gethostname(). 102 */ 103 char *p = hp->h_name; 104 if ((strlen(hp->h_name) > strlen(hostname)) 105 && (strncmp(hostname, hp->h_name, strlen(hostname)) == 0) 106 && (*(p + strlen(hostname)) == '.')) 107 strcpy(hostname, hp->h_name); 108 } 109 } 110 } 111 return (*env)->NewStringUTF(env, hostname); 112 } 113 114 /* 115 * Find an internet address for a given hostname. Note that this 116 * code only works for addresses of type INET. The translation 117 * of %d.%d.%d.%d to an address (int) occurs in java now, so the 118 * String "host" shouldn't *ever* be a %d.%d.%d.%d string 119 * 120 * Class: java_net_Inet4AddressImpl 121 * Method: lookupAllHostAddr 122 * Signature: (Ljava/lang/String;)[[B 123 */ 124 125 JNIEXPORT jobjectArray JNICALL 126 Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this, 127 jstring host) { 128 const char *hostname; 129 jobjectArray ret = 0; 130 struct hostent res, *hp = 0; 131 char buf[HENT_BUF_SIZE]; 132 133 /* temporary buffer, on the off chance we need to expand */ 134 char *tmp = NULL; 135 int h_error=0; 136 137 init(env); 138 139 if (IS_NULL(host)) { 140 JNU_ThrowNullPointerException(env, "host is null"); 141 return 0; 142 } 143 hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE); 144 CHECK_NULL_RETURN(hostname, NULL); 145 146 #ifdef __solaris__ 147 /* 148 * Workaround for Solaris bug 4160367 - if a hostname contains a 149 * white space then 0.0.0.0 is returned 150 */ 151 if (isspace((unsigned char)hostname[0])) { 152 JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", 153 (char *)hostname); 154 JNU_ReleaseStringPlatformChars(env, host, hostname); 155 return NULL; 156 } 157 #endif 158 159 /* Try once, with our static buffer. */ 160 #ifdef __GLIBC__ 161 gethostbyname_r(hostname, &res, buf, sizeof(buf), &hp, &h_error); 162 #else 163 hp = gethostbyname_r(hostname, &res, buf, sizeof(buf), &h_error); 164 #endif 165 166 /* With the re-entrant system calls, it's possible that the buffer 167 * we pass to it is not large enough to hold an exceptionally 168 * large DNS entry. This is signaled by errno->ERANGE. We try once 169 * more, with a very big size. 170 */ 171 if (hp == NULL && errno == ERANGE) { 172 if ((tmp = (char*)malloc(BIG_HENT_BUF_SIZE))) { 173 #ifdef __GLIBC__ 174 gethostbyname_r(hostname, &res, tmp, BIG_HENT_BUF_SIZE, 175 &hp, &h_error); 176 #else 177 hp = gethostbyname_r(hostname, &res, tmp, BIG_HENT_BUF_SIZE, 178 &h_error); 179 #endif 180 } 181 } 182 if (hp != NULL) { 183 struct in_addr **addrp = (struct in_addr **) hp->h_addr_list; 184 int i = 0; 185 186 while (*addrp != (struct in_addr *) 0) { 187 i++; 188 addrp++; 189 } 190 191 ret = (*env)->NewObjectArray(env, i, ia_class, NULL); 192 if (IS_NULL(ret)) { 193 /* we may have memory to free at the end of this */ 194 goto cleanupAndReturn; 195 } 196 addrp = (struct in_addr **) hp->h_addr_list; 197 i = 0; 198 while (*addrp) { 199 jobject iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID); 200 if (IS_NULL(iaObj)) { 201 ret = NULL; 202 goto cleanupAndReturn; 203 } 204 (*env)->SetIntField(env, iaObj, ia_addressID, 205 ntohl((*addrp)->s_addr)); 206 (*env)->SetObjectField(env, iaObj, ia_hostNameID, host); 207 (*env)->SetObjectArrayElement(env, ret, i, iaObj); 208 addrp++; 209 i++; 210 } 211 } else { 212 JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", 213 (char *)hostname); 214 ret = NULL; 215 } 216 217 cleanupAndReturn: 218 JNU_ReleaseStringPlatformChars(env, host, hostname); 219 if (tmp != NULL) { 220 free(tmp); 221 } 222 return ret; 223 } 224 225 /* 226 * Class: java_net_Inet4AddressImpl 227 * Method: getHostByAddr 228 * Signature: (I)Ljava/lang/String; 229 */ 230 JNIEXPORT jstring JNICALL 231 Java_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this, 232 jbyteArray addrArray) { 233 jstring ret = NULL; 234 jint addr; 235 struct hostent hent, *hp = 0; 236 char buf[HENT_BUF_SIZE]; 237 int h_error = 0; 238 char *tmp = NULL; 239 240 /* 241 * We are careful here to use the reentrant version of 242 * gethostbyname because at the Java level this routine is not 243 * protected by any synchronization. 244 * 245 * Still keeping the reentrant platform dependent calls temporarily 246 * We should probably conform to one interface later. 247 * 248 */ 249 jbyte caddr[4]; 250 (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr); 251 addr = ((caddr[0]<<24) & 0xff000000); 252 addr |= ((caddr[1] <<16) & 0xff0000); 253 addr |= ((caddr[2] <<8) & 0xff00); 254 addr |= (caddr[3] & 0xff); 255 addr = htonl(addr); 256 #ifdef __GLIBC__ 257 gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, &hent, 258 buf, sizeof(buf), &hp, &h_error); 259 #else 260 hp = gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, &hent, 261 buf, sizeof(buf), &h_error); 262 #endif 263 /* With the re-entrant system calls, it's possible that the buffer 264 * we pass to it is not large enough to hold an exceptionally 265 * large DNS entry. This is signaled by errno->ERANGE. We try once 266 * more, with a very big size. 267 */ 268 if (hp == NULL && errno == ERANGE) { 269 if ((tmp = (char*)malloc(BIG_HENT_BUF_SIZE))) { 270 #ifdef __GLIBC__ 271 gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, 272 &hent, tmp, BIG_HENT_BUF_SIZE, &hp, &h_error); 273 #else 274 hp = gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, 275 &hent, tmp, BIG_HENT_BUF_SIZE, &h_error); 276 #endif 277 } else { 278 JNU_ThrowOutOfMemoryError(env, "getHostByAddr"); 279 } 280 } 281 if (hp == NULL) { 282 JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", NULL); 283 } else { 284 ret = (*env)->NewStringUTF(env, hp->h_name); 285 } 286 if (tmp) { 287 free(tmp); 288 } 289 return ret; 290 } 291 292 #define SET_NONBLOCKING(fd) { \ 293 int flags = fcntl(fd, F_GETFL); \ 294 flags |= O_NONBLOCK; \ 295 fcntl(fd, F_SETFL, flags); \ 296 } 297 298 /** 299 * ping implementation. 300 * Send a ICMP_ECHO_REQUEST packet every second until either the timeout 301 * expires or a answer is received. 302 * Returns true is an ECHO_REPLY is received, otherwise, false. 303 */ 304 static jboolean 305 ping4(JNIEnv *env, jint fd, struct sockaddr_in* him, jint timeout, 306 struct sockaddr_in* netif, jint ttl) { 307 jint size; 308 jint n, hlen1, icmplen; 309 socklen_t len; 310 char sendbuf[1500]; 311 char recvbuf[1500]; 312 struct icmp *icmp; 313 struct ip *ip; 314 struct sockaddr_in sa_recv; 315 jchar pid; 316 jint tmout2, seq = 1; 317 struct timeval tv; 318 size_t plen; 319 320 /* icmp_id is a 16 bit data type, therefore down cast the pid */ 321 pid = (jchar)getpid(); 322 size = 60*1024; 323 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); 324 /* 325 * sets the ttl (max number of hops) 326 */ 327 if (ttl > 0) { 328 setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); 329 } 330 /* 331 * a specific interface was specified, so let's bind the socket 332 * to that interface to ensure the requests are sent only through it. 333 */ 334 if (netif != NULL) { 335 if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) { 336 NET_ThrowNew(env, errno, "Can't bind socket"); 337 close(fd); 338 return JNI_FALSE; 339 } 340 } 341 /* 342 * Make the socket non blocking so we can use select 343 */ 344 SET_NONBLOCKING(fd); 345 do { 346 /* 347 * create the ICMP request 348 */ 349 icmp = (struct icmp *) sendbuf; 350 icmp->icmp_type = ICMP_ECHO; 351 icmp->icmp_code = 0; 352 icmp->icmp_id = htons(pid); 353 icmp->icmp_seq = htons(seq); 354 seq++; 355 gettimeofday(&tv, NULL); 356 memcpy(icmp->icmp_data, &tv, sizeof(tv)); 357 plen = ICMP_ADVLENMIN + sizeof(tv); 358 icmp->icmp_cksum = 0; 359 icmp->icmp_cksum = in_cksum((u_short *)icmp, plen); 360 /* 361 * send it 362 */ 363 n = sendto(fd, sendbuf, plen, 0, (struct sockaddr *)him, 364 sizeof(struct sockaddr)); 365 if (n < 0 && errno != EINPROGRESS ) { 366 NET_ThrowNew(env, errno, "Can't send ICMP packet"); 367 close(fd); 368 return JNI_FALSE; 369 } 370 371 tmout2 = timeout > 1000 ? 1000 : timeout; 372 do { 373 tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2); 374 if (tmout2 >= 0) { 375 len = sizeof(sa_recv); 376 n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&sa_recv, &len); 377 ip = (struct ip*) recvbuf; 378 hlen1 = (ip->ip_hl) << 2; 379 icmp = (struct icmp *) (recvbuf + hlen1); 380 icmplen = n - hlen1; 381 /* 382 * We did receive something, but is it what we were expecting? 383 * I.E.: A ICMP_ECHOREPLY packet with the proper PID. 384 */ 385 if (icmplen >= 8 && icmp->icmp_type == ICMP_ECHOREPLY && 386 (ntohs(icmp->icmp_id) == pid) && 387 (him->sin_addr.s_addr == sa_recv.sin_addr.s_addr)) { 388 close(fd); 389 return JNI_TRUE; 390 } 391 } 392 } while (tmout2 > 0); 393 timeout -= 1000; 394 } while (timeout >0); 395 close(fd); 396 return JNI_FALSE; 397 } 398 399 /* 400 * Class: java_net_Inet4AddressImpl 401 * Method: isReachable0 402 * Signature: ([bI[bI)Z 403 */ 404 JNIEXPORT jboolean JNICALL 405 Java_java_net_Inet4AddressImpl_isReachable0(JNIEnv *env, jobject this, 406 jbyteArray addrArray, 407 jint timeout, 408 jbyteArray ifArray, 409 jint ttl) { 410 jint addr; 411 jbyte caddr[4]; 412 jint fd; 413 struct sockaddr_in him; 414 struct sockaddr_in* netif = NULL; 415 struct sockaddr_in inf; 416 int len = 0; 417 int connect_rv = -1; 418 int sz; 419 420 memset((char *) caddr, 0, sizeof(caddr)); 421 memset((char *) &him, 0, sizeof(him)); 422 sz = (*env)->GetArrayLength(env, addrArray); 423 if (sz != 4) { 424 return JNI_FALSE; 425 } 426 (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr); 427 addr = ((caddr[0]<<24) & 0xff000000); 428 addr |= ((caddr[1] <<16) & 0xff0000); 429 addr |= ((caddr[2] <<8) & 0xff00); 430 addr |= (caddr[3] & 0xff); 431 addr = htonl(addr); 432 him.sin_addr.s_addr = addr; 433 him.sin_family = AF_INET; 434 len = sizeof(him); 435 /* 436 * If a network interface was specified, let's create the address 437 * for it. 438 */ 439 if (!(IS_NULL(ifArray))) { 440 memset((char *) caddr, 0, sizeof(caddr)); 441 (*env)->GetByteArrayRegion(env, ifArray, 0, 4, caddr); 442 addr = ((caddr[0]<<24) & 0xff000000); 443 addr |= ((caddr[1] <<16) & 0xff0000); 444 addr |= ((caddr[2] <<8) & 0xff00); 445 addr |= (caddr[3] & 0xff); 446 addr = htonl(addr); 447 inf.sin_addr.s_addr = addr; 448 inf.sin_family = AF_INET; 449 inf.sin_port = 0; 450 netif = &inf; 451 } 452 453 /* 454 * Let's try to create a RAW socket to send ICMP packets 455 * This usually requires "root" privileges, so it's likely to fail. 456 */ 457 fd = JVM_Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 458 if (fd != -1) { 459 /* 460 * It didn't fail, so we can use ICMP_ECHO requests. 461 */ 462 return ping4(env, fd, &him, timeout, netif, ttl); 463 } 464 465 /* 466 * Can't create a raw socket, so let's try a TCP socket 467 */ 468 fd = JVM_Socket(AF_INET, SOCK_STREAM, 0); 469 if (fd == JVM_IO_ERR) { 470 /* note: if you run out of fds, you may not be able to load 471 * the exception class, and get a NoClassDefFoundError 472 * instead. 473 */ 474 NET_ThrowNew(env, errno, "Can't create socket"); 475 return JNI_FALSE; 476 } 477 if (ttl > 0) { 478 setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); 479 } 480 481 /* 482 * A network interface was specified, so let's bind to it. 483 */ 484 if (netif != NULL) { 485 if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) { 486 NET_ThrowNew(env, errno, "Can't bind socket"); 487 close(fd); 488 return JNI_FALSE; 489 } 490 } 491 492 /* 493 * Make the socket non blocking so we can use select/poll. 494 */ 495 SET_NONBLOCKING(fd); 496 497 /* no need to use NET_Connect as non-blocking */ 498 him.sin_port = htons(7); /* Echo */ 499 connect_rv = JVM_Connect(fd, (struct sockaddr *)&him, len); 500 501 /** 502 * connection established or refused immediately, either way it means 503 * we were able to reach the host! 504 */ 505 if (connect_rv == 0 || errno == ECONNREFUSED) { 506 close(fd); 507 return JNI_TRUE; 508 } else { 509 int optlen; 510 511 switch (errno) { 512 case ENETUNREACH: /* Network Unreachable */ 513 case EAFNOSUPPORT: /* Address Family not supported */ 514 case EADDRNOTAVAIL: /* address is not available on the remote machine */ 515 #ifdef __linux__ 516 case EINVAL: 517 /* 518 * On some Linuxes, when bound to the loopback interface, connect 519 * will fail and errno will be set to EINVAL. When that happens, 520 * don't throw an exception, just return false. 521 */ 522 #endif /* __linux__ */ 523 close(fd); 524 return JNI_FALSE; 525 } 526 527 if (errno != EINPROGRESS) { 528 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException", 529 "connect failed"); 530 close(fd); 531 return JNI_FALSE; 532 } 533 534 timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout); 535 if (timeout >= 0) { 536 /* has connection been established? */ 537 optlen = sizeof(connect_rv); 538 if (JVM_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv, 539 &optlen) <0) { 540 connect_rv = errno; 541 } 542 if (connect_rv == 0 || connect_rv == ECONNREFUSED) { 543 close(fd); 544 return JNI_TRUE; 545 } 546 } 547 close(fd); 548 return JNI_FALSE; 549 } 550 }