Skip to content

Commit 4c9c0cb

Browse files
author
Kamal Nayan
committed
Parse ip range bounds strictly without DNS resolution
Signed-off-by: Kamal Nayan <askkamal@amazon.com>
1 parent e12b568 commit 4c9c0cb

2 files changed

Lines changed: 39 additions & 19 deletions

File tree

sandbox/plugins/dsl-query-executor/src/main/java/org/opensearch/dsl/query/RangeQueryTranslator.java

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
import org.apache.calcite.sql.type.SqlTypeName;
1818
import org.opensearch.analytics.schema.IpType;
1919
import org.opensearch.common.geo.ShapeRelation;
20+
import org.opensearch.common.network.InetAddresses;
2021
import org.opensearch.dsl.converter.ConversionContext;
2122
import org.opensearch.dsl.converter.ConversionException;
2223
import org.opensearch.index.query.QueryBuilder;
2324
import org.opensearch.index.query.RangeQueryBuilder;
2425

2526
import java.math.BigDecimal;
2627
import java.net.InetAddress;
27-
import java.net.UnknownHostException;
2828
import java.util.ArrayList;
2929
import java.util.List;
3030

@@ -270,19 +270,13 @@ private RexNode convertIpRange(RangeQueryBuilder rangeQuery, RelDataTypeField fi
270270

271271
if (rangeQuery.from() != null) {
272272
byte[] fromBytes = encodeIpAsIpv6(String.valueOf(rangeQuery.from()));
273-
if (fromBytes == null) {
274-
throw new ConversionException("Failed to parse IP address value '" + rangeQuery.from() + "'");
275-
}
276273
RexNode literal = ctx.getRexBuilder().makeLiteral(new ByteString(fromBytes), varbinaryType, false);
277274
SqlOperator op = rangeQuery.includeLower() ? SqlStdOperatorTable.GREATER_THAN_OR_EQUAL : SqlStdOperatorTable.GREATER_THAN;
278275
conditions.add(ctx.getRexBuilder().makeCall(op, fieldRef, literal));
279276
}
280277

281278
if (rangeQuery.to() != null) {
282279
byte[] toBytes = encodeIpAsIpv6(String.valueOf(rangeQuery.to()));
283-
if (toBytes == null) {
284-
throw new ConversionException("Failed to parse IP address value '" + rangeQuery.to() + "'");
285-
}
286280
RexNode literal = ctx.getRexBuilder().makeLiteral(new ByteString(toBytes), varbinaryType, false);
287281
SqlOperator op = rangeQuery.includeUpper() ? SqlStdOperatorTable.LESS_THAN_OR_EQUAL : SqlStdOperatorTable.LESS_THAN;
288282
conditions.add(ctx.getRexBuilder().makeCall(op, fieldRef, literal));
@@ -301,21 +295,31 @@ private RexNode convertIpRange(RangeQueryBuilder rangeQuery, RelDataTypeField fi
301295
* encoded as 10 zero bytes + 0xff 0xff + 4 IPv4 bytes (RFC 4291 section 2.5.5.2).
302296
* IPv6 is its raw 16 bytes. Identical to {@code CidrMatchFunctionAdapter.encodeIpAsIpv6}
303297
* and {@code InetAddressPoint.encode} byte layout.
298+
*
299+
* <p>Uses {@code InetAddresses.forString} for strict textual IP parsing without DNS
300+
* resolution, matching legacy behavior (IpFieldMapper uses InetAddresses.forString;
301+
* hostname input is rejected).
302+
*
303+
* @param value textual IPv4 or IPv6 address (e.g. "192.168.0.1" or "::1")
304+
* @return 16-byte IPv6-mapped encoding
305+
* @throws ConversionException if the value is not a valid literal IP address
304306
*/
305-
static byte[] encodeIpAsIpv6(String value) {
307+
static byte[] encodeIpAsIpv6(String value) throws ConversionException {
308+
final InetAddress inetAddress;
306309
try {
307-
byte[] addr = InetAddress.getByName(value).getAddress();
308-
if (addr.length == 16) {
309-
return addr;
310-
}
311-
byte[] mapped = new byte[16];
312-
mapped[10] = (byte) 0xff;
313-
mapped[11] = (byte) 0xff;
314-
System.arraycopy(addr, 0, mapped, 12, 4);
315-
return mapped;
316-
} catch (UnknownHostException e) {
317-
return null;
310+
inetAddress = InetAddresses.forString(value);
311+
} catch (IllegalArgumentException e) {
312+
throw new ConversionException("Failed to parse IP address value '" + value + "': not a valid IPv4 or IPv6 literal");
313+
}
314+
byte[] addr = inetAddress.getAddress();
315+
if (addr.length == 16) {
316+
return addr;
318317
}
318+
byte[] mapped = new byte[16];
319+
mapped[10] = (byte) 0xff;
320+
mapped[11] = (byte) 0xff;
321+
System.arraycopy(addr, 0, mapped, 12, 4);
322+
return mapped;
319323
}
320324

321325
/**

sandbox/plugins/dsl-query-executor/src/test/java/org/opensearch/dsl/query/RangeQueryTranslatorTests.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,5 +981,21 @@ public void testIpRangeInvalidIpThrows() {
981981
expectThrows(ConversionException.class, () -> translator.convert(QueryBuilders.rangeQuery("ip_address").gte("not_an_ip"), ctx));
982982
}
983983

984+
/**
985+
* Hostname input (e.g. "localhost") must be rejected with ConversionException.
986+
* DNS resolution must never be attempted on user-supplied range bounds.
987+
*/
988+
public void testIpRangeHostnameLocalhostThrows() {
989+
expectThrows(ConversionException.class, () -> translator.convert(QueryBuilders.rangeQuery("ip_address").gte("localhost"), ctx));
990+
}
991+
992+
/**
993+
* Arbitrary hostname input must be rejected with ConversionException,
994+
* not passed to DNS resolution.
995+
*/
996+
public void testIpRangeHostnameArbitraryThrows() {
997+
expectThrows(ConversionException.class, () -> translator.convert(QueryBuilders.rangeQuery("ip_address").gte("evil.example"), ctx));
998+
}
999+
9841000
// ========== END OF TESTS ==========
9851001
}

0 commit comments

Comments
 (0)