Skip to content

Commit db60351

Browse files
authored
Merge pull request #2369 from ClickHouse/fix_ip_v6
[jdbc-v2] Fixes IPv6 address conversion into IPv4
2 parents dfd5880 + d19eab5 commit db60351

File tree

6 files changed

+116
-12
lines changed

6 files changed

+116
-12
lines changed

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/AbstractBinaryFormatReader.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.math.BigInteger;
2929
import java.net.Inet4Address;
3030
import java.net.Inet6Address;
31+
import java.net.InetAddress;
3132
import java.time.Duration;
3233
import java.time.Instant;
3334
import java.time.LocalDate;
@@ -486,12 +487,12 @@ public TemporalAmount getTemporalAmount(String colName) {
486487

487488
@Override
488489
public Inet4Address getInet4Address(String colName) {
489-
return readValue(colName);
490+
return InetAddressConverter.convertToIpv4(readValue(colName));
490491
}
491492

492493
@Override
493494
public Inet6Address getInet6Address(String colName) {
494-
return readValue(colName);
495+
return InetAddressConverter.convertToIpv6(readValue(colName));
495496
}
496497

497498
@Override
@@ -651,12 +652,12 @@ public TemporalAmount getTemporalAmount(int index) {
651652

652653
@Override
653654
public Inet4Address getInet4Address(int index) {
654-
return readValue(index);
655+
return InetAddressConverter.convertToIpv4(readValue(index));
655656
}
656657

657658
@Override
658659
public Inet6Address getInet6Address(int index) {
659-
return readValue(index);
660+
return InetAddressConverter.convertToIpv6(readValue(index));
660661
}
661662

662663
@Override
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.clickhouse.client.api.data_formats.internal;
2+
3+
import java.net.Inet4Address;
4+
import java.net.Inet6Address;
5+
import java.net.InetAddress;
6+
import java.net.UnknownHostException;
7+
8+
public class InetAddressConverter {
9+
10+
/**
11+
* Converts IPv4 address to IPv6 address.
12+
*
13+
* @param value IPv4 address
14+
* @return IPv6 address
15+
* @throws IllegalArgumentException when failed to convert to IPv6 address
16+
*/
17+
public static Inet6Address convertToIpv6(InetAddress value) {
18+
if (value == null || value instanceof Inet6Address) {
19+
return (Inet6Address) value;
20+
}
21+
22+
// https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
23+
byte[] bytes = new byte[16];
24+
bytes[10] = (byte) 0xFF;
25+
bytes[11] = (byte) 0xFF;
26+
System.arraycopy(value.getAddress(), 0, bytes, 12, 4);
27+
28+
try {
29+
return Inet6Address.getByAddress(null, bytes, null);
30+
} catch (UnknownHostException e) {
31+
throw new IllegalArgumentException(e);
32+
}
33+
}
34+
35+
/**
36+
* Converts IPv6 address to IPv4 address if applicable.
37+
*
38+
* @param value IPv6 address
39+
* @return IPv4 address
40+
* @throws IllegalArgumentException when failed to convert to IPv4 address
41+
*/
42+
public static Inet4Address convertToIpv4(InetAddress value) {
43+
if (value == null || value instanceof Inet4Address) {
44+
return (Inet4Address) value;
45+
}
46+
47+
byte[] bytes = value.getAddress();
48+
boolean invalid = false;
49+
for (int i = 0; i < 10; i++) {
50+
if (bytes[i] != (byte) 0) {
51+
invalid = true;
52+
break;
53+
}
54+
}
55+
56+
if (!invalid) {
57+
invalid = bytes[10] != 0xFF || bytes[11] != 0xFF;
58+
}
59+
60+
if (invalid) {
61+
throw new IllegalArgumentException("Failed to convert IPv6 to IPv4");
62+
}
63+
64+
byte[] addr = new byte[4];
65+
System.arraycopy(bytes, 12, addr, 0, 4);
66+
try {
67+
return (Inet4Address) InetAddress.getByAddress(addr);
68+
} catch (UnknownHostException e) {
69+
throw new IllegalArgumentException(e);
70+
}
71+
}
72+
}

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/MapBackedRecord.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,12 @@ public TemporalAmount getTemporalAmount(String colName) {
162162

163163
@Override
164164
public Inet4Address getInet4Address(String colName) {
165-
return readValue(colName);
165+
return InetAddressConverter.convertToIpv4(readValue(colName));
166166
}
167167

168168
@Override
169169
public Inet6Address getInet6Address(String colName) {
170-
return readValue(colName);
170+
return InetAddressConverter.convertToIpv6(readValue(colName));
171171
}
172172

173173
@Override
@@ -323,12 +323,12 @@ public TemporalAmount getTemporalAmount(int index) {
323323

324324
@Override
325325
public Inet4Address getInet4Address(int index) {
326-
return readValue(index);
326+
return InetAddressConverter.convertToIpv4(readValue(index));
327327
}
328328

329329
@Override
330330
public Inet6Address getInet6Address(int index) {
331-
return readValue(index);
331+
return InetAddressConverter.convertToIpv6(readValue(index));
332332
}
333333

334334
@Override

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/SerializerUtils.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import java.math.BigInteger;
2929
import java.net.Inet4Address;
3030
import java.net.Inet6Address;
31+
import java.net.InetAddress;
32+
import java.net.UnknownHostException;
3133
import java.sql.Timestamp;
3234
import java.time.*;
3335
import java.time.temporal.TemporalUnit;

client-v2/src/test/java/com/clickhouse/client/query/QueryTests.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,34 @@ public void testIPAddresses() throws Exception {
781781
testDataTypes(columns, valueGenerators, verifiers);
782782
}
783783

784-
@Test
784+
@Test(groups = {"integration"})
785+
public void testConversionOfIpAddresses() throws Exception {
786+
787+
try (QueryResponse response = client.query("SELECT toIPv6('::ffff:90.176.75.97') ipv4, toIPv6('2001:db8:85a3::8a2e:370:7334') ipv6").get()) {
788+
ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);
789+
790+
reader.next();
791+
InetAddress ipv4 = reader.getInet4Address(1);
792+
Assert.assertNotNull(ipv4);
793+
InetAddress ipv6 = reader.getInet6Address(2);
794+
Assert.assertNotNull(ipv6);
795+
InetAddress ipv4_as_ipv6 = reader.getInet6Address(1);
796+
Assert.assertEquals(Inet4Address.getByAddress(ipv4_as_ipv6.getAddress()), ipv4);
797+
Assert.assertThrows(() -> reader.getInet4Address(2));
798+
}
799+
800+
List<GenericRecord> records = client.queryAll("SELECT toIPv6('::ffff:90.176.75.97') ipv4, toIPv6('2001:db8:85a3::8a2e:370:7334') ipv6");
801+
GenericRecord record = records.get(0);
802+
InetAddress ipv4 = record.getInet4Address(1);
803+
Assert.assertNotNull(ipv4);
804+
InetAddress ipv6 = record.getInet6Address(2);
805+
Assert.assertNotNull(ipv6);
806+
InetAddress ipv4_as_ipv6 = record.getInet6Address(1);
807+
Assert.assertEquals(Inet4Address.getByAddress(ipv4_as_ipv6.getAddress()), ipv4);
808+
Assert.assertThrows(() -> record.getInet4Address(2));
809+
}
810+
811+
@Test(groups = {"integration"})
785812
public void testDateTimeDataTypes() {
786813
final List<String> columns = Arrays.asList(
787814
"min_date Date",

jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import java.time.OffsetDateTime;
2929
import java.time.ZoneId;
3030
import java.time.ZonedDateTime;
31-
import java.time.format.DateTimeFormatter;
3231
import java.util.GregorianCalendar;
3332
import java.util.HashMap;
3433
import java.util.Map;
@@ -519,7 +518,7 @@ public void testStringTypes() throws SQLException {
519518
@Test(groups = { "integration" })
520519
public void testIpAddressTypes() throws SQLException, UnknownHostException {
521520
runQuery("CREATE TABLE test_ips (order Int8, "
522-
+ "ipv4_ip IPv4, ipv4_name IPv4, ipv6 IPv6"
521+
+ "ipv4_ip IPv4, ipv4_name IPv4, ipv6 IPv6, ipv4_as_ipv6 IPv6"
523522
+ ") ENGINE = MergeTree ORDER BY ()");
524523

525524
// Insert random (valid) values
@@ -529,12 +528,14 @@ public void testIpAddressTypes() throws SQLException, UnknownHostException {
529528
InetAddress ipv4AddressByIp = Inet4Address.getByName(rand.nextInt(256) + "." + rand.nextInt(256) + "." + rand.nextInt(256) + "." + rand.nextInt(256));
530529
InetAddress ipv4AddressByName = Inet4Address.getByName("www.example.com");
531530
InetAddress ipv6Address = Inet6Address.getByName("2001:adb8:85a3:1:2:8a2e:370:7334");
531+
InetAddress ipv4AsIpv6 = Inet4Address.getByName("90.176.75.97");
532532

533533
try (Connection conn = getConnection()) {
534-
try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO test_ips VALUES ( 1, ?, ?, ? )")) {
534+
try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO test_ips VALUES ( 1, ?, ?, ?, ? )")) {
535535
stmt.setObject(1, ipv4AddressByIp);
536536
stmt.setObject(2, ipv4AddressByName);
537537
stmt.setObject(3, ipv6Address);
538+
stmt.setObject(4, ipv4AsIpv6);
538539
stmt.executeUpdate();
539540
}
540541
}
@@ -549,6 +550,7 @@ public void testIpAddressTypes() throws SQLException, UnknownHostException {
549550
assertEquals(rs.getObject("ipv4_name"), ipv4AddressByName);
550551
assertEquals(rs.getObject("ipv6"), ipv6Address);
551552
assertEquals(rs.getString("ipv6"), ipv6Address.toString());
553+
assertEquals(rs.getObject("ipv4_as_ipv6"), ipv4AsIpv6);
552554
assertFalse(rs.next());
553555
}
554556
}

0 commit comments

Comments
 (0)