Path: blob/master/src/java.base/windows/native/libnet/Inet4AddressImpl.c
41149 views
/*1* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24#include <malloc.h>2526#include "net_util.h"2728#include "java_net_InetAddress.h"29#include "java_net_Inet4AddressImpl.h"3031/*32* Inet4AddressImpl33*/3435/*36* Class: java_net_Inet4AddressImpl37* Method: getLocalHostName38* Signature: ()Ljava/lang/String;39*/40JNIEXPORT jstring JNICALL41Java_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {42char hostname[256];4344if (gethostname(hostname, sizeof(hostname)) == -1) {45strcpy(hostname, "localhost");46}47return JNU_NewStringPlatform(env, hostname);48}4950/*51* Find an internet address for a given hostname. Note that this52* code only works for addresses of type INET. The translation53* of %d.%d.%d.%d to an address (int) occurs in java now, so the54* String "host" shouldn't be a %d.%d.%d.%d string. The only55* exception should be when any of the %d are out of range and56* we fallback to a lookup.57*58* Class: java_net_Inet4AddressImpl59* Method: lookupAllHostAddr60* Signature: (Ljava/lang/String;)[[B61*/62JNIEXPORT jobjectArray JNICALL63Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,64jstring host) {65jobjectArray ret = NULL;66const char *hostname;67int error = 0;68struct addrinfo hints, *res = NULL, *resNew = NULL, *last = NULL,69*iterator;7071initInetAddressIDs(env);72JNU_CHECK_EXCEPTION_RETURN(env, NULL);7374if (IS_NULL(host)) {75JNU_ThrowNullPointerException(env, "host argument is null");76return NULL;77}78hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);79CHECK_NULL_RETURN(hostname, NULL);8081// try once, with our static buffer82memset(&hints, 0, sizeof(hints));83hints.ai_flags = AI_CANONNAME;84hints.ai_family = AF_INET;8586error = getaddrinfo(hostname, NULL, &hints, &res);8788if (error) {89// report error90NET_ThrowByNameWithLastError(env, "java/net/UnknownHostException",91hostname);92goto cleanupAndReturn;93} else {94int i = 0;95iterator = res;96while (iterator != NULL) {97// skip duplicates98int skip = 0;99struct addrinfo *iteratorNew = resNew;100while (iteratorNew != NULL) {101struct sockaddr_in *addr1, *addr2;102addr1 = (struct sockaddr_in *)iterator->ai_addr;103addr2 = (struct sockaddr_in *)iteratorNew->ai_addr;104if (addr1->sin_addr.s_addr == addr2->sin_addr.s_addr) {105skip = 1;106break;107}108iteratorNew = iteratorNew->ai_next;109}110111if (!skip) {112struct addrinfo *next113= (struct addrinfo *)malloc(sizeof(struct addrinfo));114if (!next) {115JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");116ret = NULL;117goto cleanupAndReturn;118}119memcpy(next, iterator, sizeof(struct addrinfo));120next->ai_next = NULL;121if (resNew == NULL) {122resNew = next;123} else {124last->ai_next = next;125}126last = next;127i++;128}129iterator = iterator->ai_next;130}131132// allocate array - at this point i contains the number of addresses133ret = (*env)->NewObjectArray(env, i, ia_class, NULL);134if (IS_NULL(ret)) {135goto cleanupAndReturn;136}137138i = 0;139iterator = resNew;140while (iterator != NULL) {141jobject iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);142if (IS_NULL(iaObj)) {143ret = NULL;144goto cleanupAndReturn;145}146setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in *)147(iterator->ai_addr))->sin_addr.s_addr));148if ((*env)->ExceptionCheck(env))149goto cleanupAndReturn;150setInetAddress_hostName(env, iaObj, host);151if ((*env)->ExceptionCheck(env))152goto cleanupAndReturn;153(*env)->SetObjectArrayElement(env, ret, i++, iaObj);154iterator = iterator->ai_next;155}156}157cleanupAndReturn:158JNU_ReleaseStringPlatformChars(env, host, hostname);159while (resNew != NULL) {160last = resNew;161resNew = resNew->ai_next;162free(last);163}164if (res != NULL) {165freeaddrinfo(res);166}167return ret;168}169170/*171* Class: java_net_Inet4AddressImpl172* Method: getHostByAddr173* Signature: ([B)Ljava/lang/String;174*175* Theoretically the UnknownHostException could be enriched with gai error176* information. But as it is silently ignored anyway, there's no need for this.177* It's only important that either a valid hostname is returned or an178* UnknownHostException is thrown.179*/180JNIEXPORT jstring JNICALL181Java_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this,182jbyteArray addrArray) {183jstring ret = NULL;184char host[NI_MAXHOST + 1];185jbyte caddr[4];186jint addr;187struct sockaddr_in sa;188189// construct a sockaddr_in structure190memset((char *)&sa, 0, sizeof(struct sockaddr_in));191(*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);192addr = ((caddr[0] << 24) & 0xff000000);193addr |= ((caddr[1] << 16) & 0xff0000);194addr |= ((caddr[2] << 8) & 0xff00);195addr |= (caddr[3] & 0xff);196sa.sin_addr.s_addr = htonl(addr);197sa.sin_family = AF_INET;198199if (getnameinfo((struct sockaddr *)&sa, sizeof(struct sockaddr_in),200host, NI_MAXHOST, NULL, 0, NI_NAMEREQD)) {201JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);202} else {203ret = (*env)->NewStringUTF(env, host);204if (ret == NULL) {205JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);206}207}208209return ret;210}211212/**213* ping implementation using tcp port 7 (echo)214*/215static jboolean216tcp_ping4(JNIEnv *env, SOCKETADDRESS *sa, SOCKETADDRESS *netif, jint timeout,217jint ttl)218{219jint fd;220int connect_rv = -1;221WSAEVENT hEvent;222223// open a TCP socket224fd = NET_Socket(AF_INET, SOCK_STREAM, 0);225if (fd == SOCKET_ERROR) {226// note: if you run out of fds, you may not be able to load227// the exception class, and get a NoClassDefFoundError instead.228NET_ThrowNew(env, WSAGetLastError(), "Can't create socket");229return JNI_FALSE;230}231232// set TTL233if (ttl > 0) {234setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *)&ttl, sizeof(ttl));235}236237// A network interface was specified, so let's bind to it.238if (netif != NULL) {239if (bind(fd, &netif->sa, sizeof(struct sockaddr_in)) < 0) {240NET_ThrowNew(env, WSAGetLastError(), "Can't bind socket");241closesocket(fd);242return JNI_FALSE;243}244}245246// Make the socket non blocking so we can use select/poll.247hEvent = WSACreateEvent();248WSAEventSelect(fd, hEvent, FD_READ|FD_CONNECT|FD_CLOSE);249250sa->sa4.sin_port = htons(7); // echo port251connect_rv = connect(fd, &sa->sa, sizeof(struct sockaddr_in));252253// connection established or refused immediately, either way it means254// we were able to reach the host!255if (connect_rv == 0 || WSAGetLastError() == WSAECONNREFUSED) {256WSACloseEvent(hEvent);257closesocket(fd);258return JNI_TRUE;259}260261switch (WSAGetLastError()) {262case WSAEHOSTUNREACH: // Host Unreachable263case WSAENETUNREACH: // Network Unreachable264case WSAENETDOWN: // Network is down265case WSAEPFNOSUPPORT: // Protocol Family unsupported266WSACloseEvent(hEvent);267closesocket(fd);268return JNI_FALSE;269case WSAEWOULDBLOCK: // this is expected as we'll probably have to wait270break;271default:272NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",273"connect failed");274WSACloseEvent(hEvent);275closesocket(fd);276return JNI_FALSE;277}278279timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);280if (timeout >= 0) {281// connection has been established, check for error condition282int optlen = sizeof(connect_rv);283if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&connect_rv,284&optlen) < 0)285{286connect_rv = WSAGetLastError();287}288if (connect_rv == 0 || connect_rv == WSAECONNREFUSED) {289WSACloseEvent(hEvent);290closesocket(fd);291return JNI_TRUE;292}293}294WSACloseEvent(hEvent);295closesocket(fd);296return JNI_FALSE;297}298299/**300* ping implementation.301* Send a ICMP_ECHO_REQUEST packet every second until either the timeout302* expires or a answer is received.303* Returns true is an ECHO_REPLY is received, otherwise, false.304*/305static jboolean306ping4(JNIEnv *env, HANDLE hIcmpFile, SOCKETADDRESS *sa,307SOCKETADDRESS *netif, jint timeout)308{309DWORD dwRetVal = 0;310char SendData[32] = {0};311LPVOID ReplyBuffer = NULL;312DWORD ReplySize = 0;313jboolean ret = JNI_FALSE;314315// See https://msdn.microsoft.com/en-us/library/aa366050%28VS.85%29.aspx316// or https://msdn.microsoft.com/en-us/library/windows/desktop/aa366051%28v=vs.85%29.aspx317ReplySize = sizeof(ICMP_ECHO_REPLY) // The buffer should be large enough318// to hold at least one ICMP_ECHO_REPLY319// structure320+ sizeof(SendData) // plus RequestSize bytes of data.321+ 8; // This buffer should also be large enough322// to also hold 8 more bytes of data323// (the size of an ICMP error message)324325ReplyBuffer = (VOID *)malloc(ReplySize);326if (ReplyBuffer == NULL) {327IcmpCloseHandle(hIcmpFile);328NET_ThrowNew(env, -1, "Unable to allocate memory");329return JNI_FALSE;330}331332if (netif == NULL) {333dwRetVal = IcmpSendEcho(hIcmpFile, // HANDLE IcmpHandle,334sa->sa4.sin_addr.s_addr, // IPAddr DestinationAddress,335SendData, // LPVOID RequestData,336sizeof(SendData), // WORD RequestSize,337NULL, // PIP_OPTION_INFORMATION RequestOptions,338ReplyBuffer,// LPVOID ReplyBuffer,339ReplySize, // DWORD ReplySize,340// Note: IcmpSendEcho and its derivatives341// seem to have an undocumented minimum342// timeout of 1000ms below which the343// api behaves inconsistently.344(timeout < 1000) ? 1000 : timeout); // DWORD Timeout345} else {346dwRetVal = IcmpSendEcho2Ex(hIcmpFile, // HANDLE IcmpHandle,347NULL, // HANDLE Event348NULL, // PIO_APC_ROUTINE ApcRoutine349NULL, // ApcContext350netif->sa4.sin_addr.s_addr, // IPAddr SourceAddress,351sa->sa4.sin_addr.s_addr, // IPAddr DestinationAddress,352SendData, // LPVOID RequestData,353sizeof(SendData), // WORD RequestSize,354NULL, // PIP_OPTION_INFORMATION RequestOptions,355ReplyBuffer,// LPVOID ReplyBuffer,356ReplySize, // DWORD ReplySize,357(timeout < 1000) ? 1000 : timeout); // DWORD Timeout358}359360if (dwRetVal == 0) { // if the call failed361TCHAR *buf = NULL;362DWORD n;363DWORD err = WSAGetLastError();364switch (err) {365case ERROR_NO_NETWORK:366case ERROR_NETWORK_UNREACHABLE:367case ERROR_HOST_UNREACHABLE:368case ERROR_PROTOCOL_UNREACHABLE:369case ERROR_PORT_UNREACHABLE:370case ERROR_REQUEST_ABORTED:371case ERROR_INCORRECT_ADDRESS:372case ERROR_HOST_DOWN:373case ERROR_INVALID_COMPUTERNAME:374case ERROR_INVALID_NETNAME:375case WSAEHOSTUNREACH: /* Host Unreachable */376case WSAENETUNREACH: /* Network Unreachable */377case WSAENETDOWN: /* Network is down */378case WSAEPFNOSUPPORT: /* Protocol Family unsupported */379case IP_REQ_TIMED_OUT:380break;381default:382n = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |383FORMAT_MESSAGE_FROM_SYSTEM,384NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),385(LPTSTR)&buf, 0, NULL);386if (n > 3) {387// Drop final '.', CR, LF388if (buf[n - 1] == TEXT('\n')) n--;389if (buf[n - 1] == TEXT('\r')) n--;390if (buf[n - 1] == TEXT('.')) n--;391buf[n] = TEXT('\0');392}393NET_ThrowNew(env, err, buf);394LocalFree(buf);395break;396}397} else {398PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;399400// This is to take into account the undocumented minimum401// timeout mentioned in the IcmpSendEcho call above.402// We perform an extra check to make sure that our403// roundtrip time was less than our desired timeout404// for cases where that timeout is < 1000ms.405if (pEchoReply->Status == IP_SUCCESS &&406(int)pEchoReply->RoundTripTime <= timeout)407{408ret = JNI_TRUE;409}410}411412free(ReplyBuffer);413IcmpCloseHandle(hIcmpFile);414415return ret;416}417418/*419* Class: java_net_Inet4AddressImpl420* Method: isReachable0421* Signature: ([bI[bI)Z422*/423JNIEXPORT jboolean JNICALL424Java_java_net_Inet4AddressImpl_isReachable0(JNIEnv *env, jobject this,425jbyteArray addrArray, jint timeout,426jbyteArray ifArray, jint ttl)427{428jbyte caddr[4];429jint addr = 0, sz;430SOCKETADDRESS sa, inf, *netif = NULL;431HANDLE hIcmpFile;432433// check if address array size is 4 (IPv4 address)434sz = (*env)->GetArrayLength(env, addrArray);435if (sz != 4) {436return JNI_FALSE;437}438439// convert IP address from byte array to integer440memset((char *)caddr, 0, sizeof(caddr));441(*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);442addr = ((caddr[0] << 24) & 0xff000000);443addr |= ((caddr[1] << 16) & 0xff0000);444addr |= ((caddr[2] << 8) & 0xff00);445addr |= (caddr[3] & 0xff);446memset((char *)&sa, 0, sizeof(SOCKETADDRESS));447sa.sa4.sin_addr.s_addr = htonl(addr);448sa.sa4.sin_family = AF_INET;449450// If a network interface was specified, let's convert its address as well.451if (!(IS_NULL(ifArray))) {452memset((char *)caddr, 0, sizeof(caddr));453(*env)->GetByteArrayRegion(env, ifArray, 0, 4, caddr);454addr = ((caddr[0] << 24) & 0xff000000);455addr |= ((caddr[1] << 16) & 0xff0000);456addr |= ((caddr[2] << 8) & 0xff00);457addr |= (caddr[3] & 0xff);458memset((char *)&inf, 0, sizeof(SOCKETADDRESS));459inf.sa4.sin_addr.s_addr = htonl(addr);460inf.sa4.sin_family = AF_INET;461netif = &inf;462}463464// Let's try to create an ICMP handle.465hIcmpFile = IcmpCreateFile();466if (hIcmpFile == INVALID_HANDLE_VALUE) {467int err = WSAGetLastError();468if (err == ERROR_ACCESS_DENIED) {469// fall back to TCP echo if access is denied to ICMP470return tcp_ping4(env, &sa, netif, timeout, ttl);471} else {472NET_ThrowNew(env, err, "Unable to create ICMP file handle");473return JNI_FALSE;474}475} else {476// It didn't fail, so we can use ICMP.477return ping4(env, hIcmpFile, &sa, netif, timeout);478}479}480481482