From 47d9039b6969bebfb804c0d6208eddd7fc75bf4c Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 14 Jun 2010 16:09:56 -0600 Subject: [PATCH 1/2] switch from gethostbyname to getaddrinfo on POSIX systems gethostbyname may return any combination of IPv4 and IPv6 addresses, and it's not safe to assume the first address is IPv4, which is all our code is currently prepared to handle. In contrast, getaddrinfo allows us to specify whether we want IPv4, IPv6, or both. We should eventually make this switch on Windows as well, but the status of getaddrinfo in Windows 2000 is not clear, and MinGW's ws2tcpip.h only declares it for XP and above. This commit also adds InetAddress.getByName for explicit DNS lookups. --- classpath/java-net.cpp | 74 +++++++++++++++++++ classpath/java-nio.cpp | 40 ++++++---- classpath/java/net/InetAddress.java | 53 +++++++++++++ classpath/java/net/Socket.java | 4 + classpath/java/net/UnknownHostException.java | 21 ++++++ classpath/java/nio/channels/Selector.java | 2 +- .../nio/channels/ServerSocketChannel.java | 11 ++- .../java/nio/channels/SocketChannel.java | 4 +- .../java/nio/channels/SocketSelector.java | 5 +- 9 files changed, 194 insertions(+), 20 deletions(-) create mode 100644 classpath/java-net.cpp create mode 100644 classpath/java/net/InetAddress.java create mode 100644 classpath/java/net/UnknownHostException.java diff --git a/classpath/java-net.cpp b/classpath/java-net.cpp new file mode 100644 index 0000000000..851ceda7a4 --- /dev/null +++ b/classpath/java-net.cpp @@ -0,0 +1,74 @@ +/* Copyright (c) 2010, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "jni.h" +#include "jni-util.h" + +#ifdef PLATFORM_WINDOWS +# include +#else +# include +#endif + +extern "C" JNIEXPORT void JNICALL +Java_java_net_Socket_init(JNIEnv* e, jclass) +{ +#ifdef PLATFORM_WINDOWS + static bool wsaInitialized = false; + if (not wsaInitialized) { + WSADATA data; + int r = WSAStartup(MAKEWORD(2, 2), &data); + if (r or LOBYTE(data.wVersion) != 2 or HIBYTE(data.wVersion) != 2) { + throwNew(e, "java/io/IOException", "WSAStartup failed"); + } + } +#endif +} + +extern "C" JNIEXPORT jint JNICALL +Java_java_net_InetAddress_ipv4AddressForName(JNIEnv* e, + jclass, + jstring name) +{ + const char* chars = e->GetStringUTFChars(name, 0); + if (chars) { +#ifdef PLATFORM_WINDOWS + hostent* host = gethostbyname(chars); + e->ReleaseStringUTFChars(name, chars); + if (host) { + return htonl(reinterpret_cast(host->h_addr_list[0])->s_addr); + } else { + fprintf(stderr, "trouble %d\n", WSAGetLastError()); + } +#else + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + addrinfo* result; + int r = getaddrinfo(chars, 0, &hints, &result); + e->ReleaseStringUTFChars(name, chars); + + jint address; + if (r != 0) { + address = 0; + } else { + address = htonl + (reinterpret_cast(result->ai_addr)->sin_addr.s_addr); + + freeaddrinfo(result); + } + + return address; +#endif + } + return 0; +} diff --git a/classpath/java-nio.cpp b/classpath/java-nio.cpp index b8a2222bcb..25d2d673b0 100644 --- a/classpath/java-nio.cpp +++ b/classpath/java-nio.cpp @@ -18,6 +18,7 @@ #ifdef PLATFORM_WINDOWS # include +# include # include # ifdef _MSC_VER # define snprintf sprintf_s @@ -150,20 +151,40 @@ init(JNIEnv* e, sockaddr_in* address, jstring hostString, jint port) { const char* chars = e->GetStringUTFChars(hostString, 0); if (chars) { +#ifdef PLATFORM_WINDOWS hostent* host = gethostbyname(chars); e->ReleaseStringUTFChars(hostString, chars); if (host == 0) { -#ifdef PLATFORM_WINDOWS throwIOException(e); -#else - throwIOException(e, hstrerror(h_errno)); -#endif return; } + memset(address, 0, sizeof(sockaddr_in)); address->sin_family = AF_INET; address->sin_port = htons(port); address->sin_addr = *reinterpret_cast(host->h_addr_list[0]); +#else + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + addrinfo* result; + int r = getaddrinfo(chars, 0, &hints, &result); + e->ReleaseStringUTFChars(hostString, chars); + if (r != 0) { + throwIOException(e, gai_strerror(r)); + return; + } + + memset(address, 0, sizeof(sockaddr_in)); + address->sin_family = AF_INET; + address->sin_port = htons(port); + address->sin_addr = reinterpret_cast + (result->ai_addr)->sin_addr; + + freeaddrinfo(result); +#endif } } @@ -328,17 +349,6 @@ doWrite(int fd, const void* buffer, size_t count) int makeSocket(JNIEnv* e) { -#ifdef PLATFORM_WINDOWS - static bool wsaInitialized = false; - if (not wsaInitialized) { - WSADATA data; - int r = WSAStartup(MAKEWORD(2, 2), &data); - if (r or LOBYTE(data.wVersion) != 2 or HIBYTE(data.wVersion) != 2) { - throwIOException(e, "WSAStartup failed"); - } - } -#endif - int s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s < 0) { throwIOException(e); diff --git a/classpath/java/net/InetAddress.java b/classpath/java/net/InetAddress.java new file mode 100644 index 0000000000..3f49a9b739 --- /dev/null +++ b/classpath/java/net/InetAddress.java @@ -0,0 +1,53 @@ +/* Copyright (c) 2010, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; + +public class InetAddress { + private final String address; + + private InetAddress(String address) { + this.address = address; + } + + public String getHostAddress() { + return address; + } + + public static InetAddress getByName(String name) + throws UnknownHostException + { + try { + Socket.init(); + } catch (IOException e) { + UnknownHostException uhe = new UnknownHostException(name); + uhe.initCause(e); + throw uhe; + } + + int address = ipv4AddressForName(name); + if (address == 0) { + throw new UnknownHostException(name); + } else { + return new InetAddress(ipv4AddressToString(address)); + } + } + + private static String ipv4AddressToString(int address) { + return (((address >>> 24) ) + "." + + ((address >>> 16) & 0xFF) + "." + + ((address >>> 8 ) & 0xFF) + "." + + ((address ) & 0xFF)); + } + + private static native int ipv4AddressForName(String name); +} diff --git a/classpath/java/net/Socket.java b/classpath/java/net/Socket.java index 602b24a19e..9fbc1e4523 100644 --- a/classpath/java/net/Socket.java +++ b/classpath/java/net/Socket.java @@ -10,6 +10,10 @@ package java.net; +import java.io.IOException; + public abstract class Socket { + public static native void init() throws IOException; + public abstract void setTcpNoDelay(boolean on) throws SocketException; } diff --git a/classpath/java/net/UnknownHostException.java b/classpath/java/net/UnknownHostException.java new file mode 100644 index 0000000000..85e4da0eac --- /dev/null +++ b/classpath/java/net/UnknownHostException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2010, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.net; + +import java.io.IOException; + +public class UnknownHostException extends IOException { + public UnknownHostException(String host) { + super(host); + } + + public UnknownHostException() { } +} diff --git a/classpath/java/nio/channels/Selector.java b/classpath/java/nio/channels/Selector.java index 2354859cb0..942357c773 100644 --- a/classpath/java/nio/channels/Selector.java +++ b/classpath/java/nio/channels/Selector.java @@ -18,7 +18,7 @@ public abstract class Selector { protected final Set keys = new HashSet(); protected final Set selectedKeys = new HashSet(); - public static Selector open() { + public static Selector open() throws IOException { return new SocketSelector(); } diff --git a/classpath/java/nio/channels/ServerSocketChannel.java b/classpath/java/nio/channels/ServerSocketChannel.java index 0bb135c3e9..c8a691c5c5 100644 --- a/classpath/java/nio/channels/ServerSocketChannel.java +++ b/classpath/java/nio/channels/ServerSocketChannel.java @@ -15,11 +15,16 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.ServerSocket; +import java.net.Socket; public class ServerSocketChannel extends SelectableChannel { - private final SocketChannel channel = new SocketChannel(); + private final SocketChannel channel; - public static ServerSocketChannel open() { + private ServerSocketChannel() throws IOException { + channel = new SocketChannel(); + } + + public static ServerSocketChannel open() throws IOException { return new ServerSocketChannel(); } @@ -58,6 +63,8 @@ public class ServerSocketChannel extends SelectableChannel { } private int doListen(String host, int port) throws IOException { + Socket.init(); + return natDoListen(host, port); } diff --git a/classpath/java/nio/channels/SocketChannel.java b/classpath/java/nio/channels/SocketChannel.java index 25da62684c..db20896f1c 100644 --- a/classpath/java/nio/channels/SocketChannel.java +++ b/classpath/java/nio/channels/SocketChannel.java @@ -26,7 +26,9 @@ public class SocketChannel extends SelectableChannel boolean connected = false; boolean blocking = true; - public static SocketChannel open() { + public static SocketChannel open() throws IOException { + Socket.init(); + return new SocketChannel(); } diff --git a/classpath/java/nio/channels/SocketSelector.java b/classpath/java/nio/channels/SocketSelector.java index 7ad46b5355..8ad7bbafd8 100644 --- a/classpath/java/nio/channels/SocketSelector.java +++ b/classpath/java/nio/channels/SocketSelector.java @@ -12,13 +12,16 @@ package java.nio.channels; import java.io.IOException; import java.util.Iterator; +import java.net.Socket; class SocketSelector extends Selector { protected long state; protected final Object lock = new Object(); protected boolean woken = false; - public SocketSelector() { + public SocketSelector() throws IOException { + Socket.init(); + state = natInit(); } From 5e7e539c2a1adc9b4698c70c4dead5cac2ab86ae Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 2 Jul 2010 09:36:55 -0600 Subject: [PATCH 2/2] fix non-windows java-net.cpp build --- classpath/java-net.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/classpath/java-net.cpp b/classpath/java-net.cpp index 851ceda7a4..bd94f3e52c 100644 --- a/classpath/java-net.cpp +++ b/classpath/java-net.cpp @@ -13,12 +13,14 @@ #ifdef PLATFORM_WINDOWS # include +# define ONLY_ON_WINDOWS(x) x #else # include +# define ONLY_ON_WINDOWS(x) #endif extern "C" JNIEXPORT void JNICALL -Java_java_net_Socket_init(JNIEnv* e, jclass) +Java_java_net_Socket_init(JNIEnv* ONLY_ON_WINDOWS(e), jclass) { #ifdef PLATFORM_WINDOWS static bool wsaInitialized = false;