Skip to content

Commit 906d989

Browse files
committed
[GR-28432] Emulated version of inet_addr, inet_aton, inet_ntoa, inet_pton and inet_ntop
PullRequest: graalpython/1856
2 parents b6ef949 + dd53dfc commit 906d989

File tree

2 files changed

+156
-22
lines changed

2 files changed

+156
-22
lines changed

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/SocketTests.java

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,13 @@
6565
import static com.oracle.graal.python.runtime.PosixConstants.SO_PROTOCOL;
6666
import static com.oracle.graal.python.runtime.PosixConstants.SO_TYPE;
6767
import static com.oracle.graal.python.runtime.PosixConstants.TCP_USER_TIMEOUT;
68+
import static org.hamcrest.CoreMatchers.anyOf;
69+
import static org.hamcrest.CoreMatchers.equalTo;
6870
import static org.junit.Assert.assertArrayEquals;
6971
import static org.junit.Assert.assertEquals;
7072
import static org.junit.Assert.assertFalse;
7173
import static org.junit.Assert.assertNull;
74+
import static org.junit.Assert.assertThat;
7275
import static org.junit.Assert.assertTrue;
7376
import static org.junit.Assert.fail;
7477
import static org.junit.Assume.assumeTrue;
@@ -663,8 +666,16 @@ public void inet4Address() {
663666
static {
664667
ip4Addresses.put("text", null);
665668
ip4Addresses.put("1.2.3.", null);
669+
ip4Addresses.put(".1.2.3", null);
666670
ip4Addresses.put("1.2.65536", null);
667671
ip4Addresses.put("1.2.3.4.5", null);
672+
ip4Addresses.put("1.2.3.4.5.6", null);
673+
ip4Addresses.put("1.2.-3.4", null);
674+
ip4Addresses.put("1.2. 3.4", null);
675+
ip4Addresses.put(" 1.2.3.4", null);
676+
ip4Addresses.put("1.2.3.4@", null);
677+
ip4Addresses.put("1.2.3.4a", null);
678+
ip4Addresses.put("1.2..4", null);
668679
ip4Addresses.put("1.2.3.4", 0x01020304);
669680
ip4Addresses.put("1.2.0x3456", 0x01023456);
670681
ip4Addresses.put("1.2.0xffff", 0x0102ffff);
@@ -674,11 +685,11 @@ public void inet4Address() {
674685
ip4Addresses.put("0xff.0377.65535", 0xffffffff);
675686
ip4Addresses.put("0xa.012.10.0", 0x0a0a0a00);
676687
ip4Addresses.put("00.0x00000.0", 0x00000000);
688+
ip4Addresses.put("00.0x100.0", null);
677689
}
678690

679691
@Test
680692
public void inet_addr() {
681-
assumeTrue("native".equals(backendName));
682693
for (Map.Entry<String, Integer> a : ip4Addresses.entrySet()) {
683694
String src = a.getKey();
684695
Integer expected = a.getValue();
@@ -689,7 +700,6 @@ public void inet_addr() {
689700

690701
@Test
691702
public void inet_aton() {
692-
assumeTrue("native".equals(backendName));
693703
for (Map.Entry<String, Integer> a : ip4Addresses.entrySet()) {
694704
String src = a.getKey();
695705
Integer expected = a.getValue();
@@ -705,7 +715,6 @@ public void inet_aton() {
705715

706716
@Test
707717
public void inet_ntoa() {
708-
assumeTrue("native".equals(backendName));
709718
assertEquals("0.0.0.0", p2s(lib.inet_ntoa(posixSupport, 0x00000000)));
710719
assertEquals("1.2.3.4", p2s(lib.inet_ntoa(posixSupport, 0x01020304)));
711720
assertEquals("18.52.86.120", p2s(lib.inet_ntoa(posixSupport, 0x12345678)));
@@ -714,42 +723,71 @@ public void inet_ntoa() {
714723

715724
@Test
716725
public void inet_pton() throws PosixException, InvalidAddressException {
717-
assumeTrue("native".equals(backendName));
718726
assertArrayEquals(new byte[]{1, 2, -2, -1}, lib.inet_pton(posixSupport, AF_INET.value, s2p("1.2.254.255")));
719727
assertArrayEquals(new byte[]{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, lib.inet_pton(posixSupport, AF_INET6.value, s2p("1::FF")));
728+
assertArrayEquals(MAPPED_LOOPBACK, lib.inet_pton(posixSupport, AF_INET6.value, s2p("::ffff:127.0.0.1")));
720729
}
721730

722731
@Test
723732
public void inet_pton_eafnosupport() throws PosixException, InvalidAddressException {
724-
assumeTrue("native".equals(backendName));
725733
expectErrno(OSErrorEnum.EAFNOSUPPORT);
726734
lib.inet_pton(posixSupport, AF_UNSPEC.value, s2p(""));
727735
}
728736

729737
@Test
730-
public void inet_pton_invalid() throws PosixException, InvalidAddressException {
731-
assumeTrue("native".equals(backendName));
738+
public void inet_pton_invalid_inet6() throws PosixException, InvalidAddressException {
732739
expectedException.expect(InvalidAddressException.class);
733740
lib.inet_pton(posixSupport, AF_INET6.value, s2p(":"));
734741
}
735742

743+
@Test
744+
public void inet_pton_invalid_inet4_as_inet6() throws PosixException, InvalidAddressException {
745+
expectedException.expect(InvalidAddressException.class);
746+
lib.inet_pton(posixSupport, AF_INET6.value, s2p("127.0.0.1"));
747+
}
748+
749+
@Test
750+
public void inet_pton_invalid_inet4() throws PosixException {
751+
String[] addresses = {
752+
"1.2.3.4.5", // too many bytes
753+
"1.2.65535", // unlike inet_aton, inet_pton requires exactly four bytes
754+
"1.2.0x10.4", // hexadecimal is not allowed
755+
"1::FF", // IPv6 address is not allowed
756+
};
757+
for (String src : addresses) {
758+
try {
759+
lib.inet_pton(posixSupport, AF_INET.value, s2p(src));
760+
fail("inet_pton(AF_INET, \"" + src + "\") was expected to fail");
761+
} catch (InvalidAddressException e) {
762+
// expected
763+
}
764+
}
765+
}
766+
767+
@Test
768+
public void inet_pton_inet4_octal() throws PosixException, InvalidAddressException {
769+
// native inet_pton on darwin accepts leading zeroes (but handles them as decimal)
770+
assumeTrue("java".equals(backendName) || runsOnLinux());
771+
expectedException.expect(InvalidAddressException.class);
772+
lib.inet_pton(posixSupport, AF_INET.value, s2p("1.2.010.4"));
773+
}
774+
736775
@Test
737776
public void inet_ntop() throws PosixException {
738-
assumeTrue("native".equals(backendName));
739777
assertEquals("1.0.255.254", p2s(lib.inet_ntop(posixSupport, AF_INET.value, new byte[]{1, 0, -1, -2, -3})));
740-
assertEquals("fdfe:0:ff00::1:203", p2s(lib.inet_ntop(posixSupport, AF_INET6.value, new byte[]{-3, -2, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4})));
778+
assertThat(p2s(lib.inet_ntop(posixSupport, AF_INET6.value, new byte[]{-3, -2, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4})),
779+
anyOf(equalTo("fdfe:0:ff00::1:203"), equalTo("fdfe:0:ff00:0:0:0:1:203")));
780+
assertEquals("::ffff:127.0.0.1", p2s(lib.inet_ntop(posixSupport, AF_INET6.value, MAPPED_LOOPBACK)));
741781
}
742782

743783
@Test
744784
public void inet_ntop_eafnosupport() throws PosixException {
745-
assumeTrue("native".equals(backendName));
746785
expectErrno(OSErrorEnum.EAFNOSUPPORT);
747786
lib.inet_ntop(posixSupport, AF_UNSPEC.value, new byte[16]);
748787
}
749788

750789
@Test
751790
public void inet_ntop_len() throws PosixException {
752-
assumeTrue("native".equals(backendName));
753791
expectedException.expect(IllegalArgumentException.class);
754792
lib.inet_ntop(posixSupport, AF_INET6.value, new byte[15]);
755793
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java

Lines changed: 107 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import static com.oracle.graal.python.runtime.PosixConstants.DT_UNKNOWN;
5151
import static com.oracle.graal.python.runtime.PosixConstants.F_OK;
5252
import static com.oracle.graal.python.runtime.PosixConstants.IN6ADDR_ANY;
53+
import static com.oracle.graal.python.runtime.PosixConstants.INADDR_NONE;
5354
import static com.oracle.graal.python.runtime.PosixConstants.IPPROTO_TCP;
5455
import static com.oracle.graal.python.runtime.PosixConstants.IPPROTO_UDP;
5556
import static com.oracle.graal.python.runtime.PosixConstants.LOCK_EX;
@@ -194,6 +195,7 @@
194195
import com.oracle.graal.python.runtime.PosixSupportLibrary.GetAddrInfoException;
195196
import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet4SockAddr;
196197
import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet6SockAddr;
198+
import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidAddressException;
197199
import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException;
198200
import com.oracle.graal.python.runtime.PosixSupportLibrary.RecvfromResult;
199201
import com.oracle.graal.python.runtime.PosixSupportLibrary.SelectResult;
@@ -204,6 +206,7 @@
204206
import com.oracle.graal.python.runtime.exception.PythonExitException;
205207
import com.oracle.graal.python.runtime.sequence.storage.ByteSequenceStorage;
206208
import com.oracle.graal.python.util.FileDeleteShutdownHook;
209+
import com.oracle.graal.python.util.IPAddressUtil;
207210
import com.oracle.graal.python.util.OverflowException;
208211
import com.oracle.graal.python.util.PythonUtils;
209212
import com.oracle.truffle.api.CompilerDirectives;
@@ -2537,31 +2540,97 @@ public void setsockopt(int sockfd, int level, int optname, byte[] optval, int op
25372540
@ExportMessage
25382541
@SuppressWarnings("static-method")
25392542
public int inet_addr(Object src) {
2540-
throw shouldNotReachHere("Not implemented");
2543+
try {
2544+
return inet_aton(src);
2545+
} catch (InvalidAddressException e) {
2546+
return INADDR_NONE.value;
2547+
}
25412548
}
25422549

25432550
@ExportMessage
25442551
@SuppressWarnings("static-method")
2545-
public int inet_aton(Object src) {
2546-
throw shouldNotReachHere("Not implemented");
2552+
@TruffleBoundary
2553+
public int inet_aton(Object src) throws InvalidAddressException {
2554+
String s = (String) src;
2555+
if (s.charAt(0) == '.' || s.charAt(s.length() - 1) == '.') {
2556+
throw new InvalidAddressException();
2557+
}
2558+
String[] parts = s.split("\\.");
2559+
switch (parts.length) {
2560+
case 1:
2561+
return parseUnsigned(parts[0], 0xFFFFFFFFL);
2562+
case 2:
2563+
return (parseUnsigned(parts[0], 0xFF) << 24) | parseUnsigned(parts[1], 0xFFFFFF);
2564+
case 3:
2565+
return (parseUnsigned(parts[0], 0xFF) << 24) | (parseUnsigned(parts[1], 0xFF) << 16) | parseUnsigned(parts[2], 0xFFFF);
2566+
case 4:
2567+
return (parseUnsigned(parts[0], 0xFF) << 24) | (parseUnsigned(parts[1], 0xFF) << 16) | (parseUnsigned(parts[2], 0xFF) << 8) | parseUnsigned(parts[3], 0xFF);
2568+
default:
2569+
throw new InvalidAddressException();
2570+
}
25472571
}
25482572

25492573
@ExportMessage
25502574
@SuppressWarnings("static-method")
2575+
@TruffleBoundary
25512576
public Object inet_ntoa(int address) {
2552-
throw shouldNotReachHere("Not implemented");
2577+
return String.format("%d.%d.%d.%d", (address >> 24) & 0xFF, (address >> 16) & 0xFF, (address >> 8) & 0xFF, address & 0xFF);
25532578
}
25542579

25552580
@ExportMessage
25562581
@SuppressWarnings("static-method")
2557-
public byte[] inet_pton(int family, Object src) throws PosixException {
2558-
throw shouldNotReachHere("Not implemented");
2582+
@TruffleBoundary
2583+
public byte[] inet_pton(int family, Object src) throws PosixException, InvalidAddressException {
2584+
byte[] bytes;
2585+
if (family == AF_INET.value) {
2586+
String s = (String) src;
2587+
String[] parts = s.split("\\.");
2588+
if (s.charAt(0) == '.' || s.charAt(s.length() - 1) == '.' || parts.length != 4) {
2589+
throw new InvalidAddressException();
2590+
}
2591+
bytes = new byte[4];
2592+
for (int i = 0; i < 4; ++i) {
2593+
if (parts[i].charAt(0) == '0') {
2594+
throw new InvalidAddressException();
2595+
}
2596+
bytes[i] = (byte) parseUnsigned(parts[i], 10, 0xFF);
2597+
}
2598+
return bytes;
2599+
}
2600+
if (family == AF_INET6.value) {
2601+
bytes = IPAddressUtil.textToNumericFormatV6((String) src);
2602+
if (bytes == null) {
2603+
throw new InvalidAddressException();
2604+
}
2605+
if (bytes.length == 4) {
2606+
return mapIPv4toIPv6(bytes);
2607+
}
2608+
return bytes;
2609+
}
2610+
throw posixException(OSErrorEnum.EAFNOSUPPORT);
25592611
}
25602612

25612613
@ExportMessage
25622614
@SuppressWarnings("static-method")
2615+
@TruffleBoundary
25632616
public Object inet_ntop(int family, byte[] src) throws PosixException {
2564-
throw shouldNotReachHere("Not implemented");
2617+
if (family != AF_INET.value && family != AF_INET6.value) {
2618+
throw posixException(OSErrorEnum.EAFNOSUPPORT);
2619+
}
2620+
int len = family == AF_INET.value ? 4 : 16;
2621+
if (src.length < len) {
2622+
throw new IllegalArgumentException();
2623+
}
2624+
byte[] bytes = src.length > len ? PythonUtils.arrayCopyOf(src, len) : src;
2625+
try {
2626+
InetAddress addr = InetAddress.getByAddress(bytes);
2627+
if (family == AF_INET6.value && addr instanceof Inet4Address) {
2628+
return "::ffff:" + addr.getHostAddress();
2629+
}
2630+
return addr.getHostAddress();
2631+
} catch (UnknownHostException e) {
2632+
throw shouldNotReachHere();
2633+
}
25652634
}
25662635

25672636
@ExportMessage
@@ -2633,10 +2702,7 @@ Inet6SockAddr asInet6SockAddr() {
26332702
}
26342703
// IPv4 mapped to IPv6
26352704
byte[] ipv4 = ((Inet4Address) sa).getAddress();
2636-
byte[] ipv6 = new byte[16];
2637-
ipv6[10] = -1;
2638-
ipv6[11] = -1;
2639-
System.arraycopy(ipv4, 0, ipv6, 12, 4);
2705+
byte[] ipv6 = mapIPv4toIPv6(ipv4);
26402706
return new Inet6SockAddr(socketAddress.getPort(), ipv6, 0, 0);
26412707
}
26422708

@@ -3118,6 +3184,36 @@ private EmulatedSocket getEmulatedSocket(int fd) throws PosixException {
31183184
throw posixException(OSErrorEnum.ENOTSOCK);
31193185
}
31203186

3187+
private static int parseUnsigned(String value, long max) throws InvalidAddressException {
3188+
if (value.startsWith("0x") || value.startsWith("0X")) {
3189+
return parseUnsigned(value.substring(2), 16, max);
3190+
} else if (value.startsWith("0")) {
3191+
return parseUnsigned(value, 8, max);
3192+
} else {
3193+
return parseUnsigned(value, 10, max);
3194+
}
3195+
}
3196+
3197+
private static int parseUnsigned(String value, int radix, long max) throws InvalidAddressException {
3198+
try {
3199+
long l = Long.parseUnsignedLong(value, radix);
3200+
if (l < 0 || l > max) {
3201+
throw new InvalidAddressException();
3202+
}
3203+
return (int) l;
3204+
} catch (NumberFormatException e) {
3205+
throw new InvalidAddressException();
3206+
}
3207+
}
3208+
3209+
private static byte[] mapIPv4toIPv6(byte[] ipv4) {
3210+
byte[] ipv6 = new byte[16];
3211+
ipv6[10] = -1;
3212+
ipv6[11] = -1;
3213+
System.arraycopy(ipv4, 0, ipv6, 12, 4);
3214+
return ipv6;
3215+
}
3216+
31213217
// ------------------
31223218
// Path conversions
31233219

0 commit comments

Comments
 (0)