Skip to content

Commit 7f0a04c

Browse files
committed
HostAndPort IP-literal parsing should validate the in square bracketted content matches IPv6address or IPvFuture production rules.
Motivation: The HostAndPort IP-literal parser attempts to find content between square brackets, it should ensure that the enclosed content matches production rule. Changes: Implement IPv6address and IPvFuture parsing and use it when parsing IP-literal.
1 parent 0611769 commit 7f0a04c

File tree

2 files changed

+321
-3
lines changed

2 files changed

+321
-3
lines changed

vertx-core/src/main/java/io/vertx/core/http/impl/UriParser.java

Lines changed: 202 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,16 @@ public static int parseRegName(String s, int from, int to) {
8181
* @return the index after parsing or {@code -1} if the value is invalid
8282
*/
8383
public static int parseIPLiteral(String s, int from, int to) {
84-
return from + 2 < to && s.charAt(from) == '[' ? foo(s.indexOf(']', from + 2)) : -1;
84+
if (from < to && s.charAt(from++) == '[') {
85+
int idx = parseIPv6Address(s, from, to);
86+
if (idx == -1) {
87+
idx = parseIPvFuture(s, from, to);
88+
}
89+
if (idx != -1 && idx < to && s.charAt(idx++) == ']') {
90+
return idx;
91+
}
92+
}
93+
return -1;
8594
}
8695

8796
private static int foo(int v) {
@@ -127,6 +136,198 @@ public static int parseIPv4Address(String s, int from, int to) {
127136
return from;
128137
}
129138

139+
/**
140+
* Try to parse the <i>IPv6address</i> rule as per
141+
* <a href="https://datatracker.ietf.org/doc/html/rfc3986#appendix-A">RFC3986</a>.
142+
*
143+
* @param s a string containing the value to parse
144+
* @param from the index at which parsing begins
145+
* @param to the index at which parsing ends
146+
* @return the index after parsing or {@code -1} if the value is invalid
147+
*/
148+
public static int parseIPv6Address(String s, int from, int to) {
149+
int doubleColonIdx = s.indexOf("::", from);
150+
if (doubleColonIdx == -1 || (doubleColonIdx + 2) > to) {
151+
// Rule1
152+
for (int i = 0;i < 6;i++) {
153+
from = parseH16(s, from, to);
154+
if (from == -1) {
155+
return -1;
156+
}
157+
from = _parseColon(s, from, to);
158+
if (from == -1) {
159+
return -1;
160+
}
161+
}
162+
return parseLs32(s, from, to);
163+
}
164+
165+
// To ease parsing we use the first colon (:) or double colon (::)
166+
// e.g. "0::" = "0:" + ":"
167+
// "0:1:2:3::" = "0:1:2:3:" + ":"
168+
int a = doubleColonIdx + 1;
169+
int count1 = 0;
170+
while (true) {
171+
from = parseH16(s, from, a);
172+
if (from == -1) {
173+
break;
174+
}
175+
from = _parseColon(s, from, a);
176+
if (from == -1) {
177+
return -1;
178+
}
179+
count1++;
180+
}
181+
182+
int count2 = 0;
183+
from = doubleColonIdx + 2;
184+
while (true) {
185+
int idx = parseH16(s, from, to);
186+
if (idx == -1) {
187+
break;
188+
}
189+
idx = _parseColon(s, idx, to);
190+
if (idx == -1) {
191+
break;
192+
}
193+
from = idx;
194+
count2++;
195+
}
196+
int idx;
197+
if ((idx = parseIPv4Address(s, from, to)) != -1) {
198+
//
199+
} else if ((idx = parseH16(s, from, to)) != -1) {
200+
count2--;
201+
} else {
202+
// Rule 9
203+
return count1 <= 7 ? from : -1;
204+
}
205+
from = idx;
206+
207+
208+
switch (count2) {
209+
case 5:
210+
// Rule 2
211+
return count1 == 0 ? from : -1;
212+
case 4:
213+
// Rule 3
214+
return count1 <= 1 ? from : -1;
215+
case 3:
216+
// Rule 4
217+
return count1 <= 2 ? from : -1;
218+
case 2:
219+
// Rule 5
220+
return count1 <= 3 ? from : -1;
221+
case 1:
222+
// Rule 6
223+
return count1 <= 4 ? from : -1;
224+
case 0:
225+
// Rule 7
226+
return count1 <= 5 ? from : -1;
227+
case -1:
228+
// Rule 8
229+
return count1 <= 6 ? from : -1;
230+
default:
231+
throw new AssertionError();
232+
}
233+
}
234+
235+
public static int _parseColon(String s, int from, int to) {
236+
return from < to && s.charAt(from) == ':' ? from + 1 : -1;
237+
}
238+
239+
public static int _parseDoubleColon(String s, int from, int to) {
240+
return from + 1 < to && s.charAt(from) == ':' && s.charAt(from + 1) == ':' ? from + 2 : -1;
241+
}
242+
243+
/**
244+
* Try to parse the <i>ls32</i> rule as per
245+
* <a href="https://datatracker.ietf.org/doc/html/rfc3986#appendix-A">RFC3986</a>.
246+
*
247+
* @param s a string containing the value to parse
248+
* @param from the index at which parsing begins
249+
* @param to the index at which parsing ends
250+
* @return the index after parsing or {@code -1} if the value is invalid
251+
*/
252+
public static int parseLs32(String s, int from, int to) {
253+
int idx = _parseH16ColonH16(s, from, to);
254+
if (idx == -1) {
255+
idx = parseIPv4Address(s, from, to);
256+
}
257+
return idx;
258+
}
259+
260+
public static int _parseH16ColonH16(String s, int from, int to) {
261+
int idx = parseH16(s, from, to);
262+
if (idx == -1) {
263+
return -1;
264+
}
265+
from = idx;
266+
if (from >= to || s.charAt(from++) != ':') {
267+
return -1;
268+
}
269+
return parseH16(s, from, to);
270+
}
271+
272+
/**
273+
* Try to parse the <i>h16</i> rule as per
274+
* <a href="https://datatracker.ietf.org/doc/html/rfc3986#appendix-A">RFC3986</a>.
275+
*
276+
* @param s a string containing the value to parse
277+
* @param from the index at which parsing begins
278+
* @param to the index at which parsing ends
279+
* @return the index after parsing or {@code -1} if the value is invalid
280+
*/
281+
public static int parseH16(String s, int from, int to) {
282+
if (from >= to || !isHEXDIG(s.charAt(from++))) {
283+
return -1;
284+
}
285+
for (int i = 0;i < 3;i++) {
286+
if (from < to && isHEXDIG(s.charAt(from))) {
287+
from++;
288+
}
289+
}
290+
return from;
291+
}
292+
293+
/**
294+
* Try to parse the <i>IPvFuture</i> rule as per
295+
* <a href="https://datatracker.ietf.org/doc/html/rfc3986#appendix-A">RFC3986</a>.
296+
*
297+
* @param s a string containing the value to parse
298+
* @param from the index at which parsing begins
299+
* @param to the index at which parsing ends
300+
* @return the index after parsing or {@code -1} if the value is invalid
301+
*/
302+
public static int parseIPvFuture(String s, int from, int to) {
303+
if (from >= to || s.charAt(from++) != 'v') {
304+
return -1;
305+
}
306+
if (from >= to || !isHEXDIG(s.charAt(from++))) {
307+
return -1;
308+
}
309+
while (from < to) {
310+
if (isHEXDIG(s.charAt(from))) {
311+
// Continue
312+
from++;
313+
} else {
314+
break;
315+
}
316+
}
317+
if (from >= to || s.charAt(from++) != '.') {
318+
return -1;
319+
}
320+
int count = 0;
321+
char c;
322+
while ((from + count) < to && (isUnreserved(c = s.charAt(from + count)) || isSubDelims(c) || c == ':')) {
323+
count++;
324+
}
325+
if (count == 0) {
326+
return -1;
327+
}
328+
return from + count;
329+
}
330+
130331
/**
131332
* Try to parse the <i>dec-octet</i> rule as per
132333
* <a href="https://datatracker.ietf.org/doc/html/rfc3986#appendix-A">RFC3986</a>.

vertx-core/src/test/java/io/vertx/tests/net/HostAndPortTest.java

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ public void testParseIPLiteral() {
1616
Assert.assertEquals(-1, UriParser.parseIPLiteral("", 0, 0));
1717
assertEquals(-1, UriParser.parseIPLiteral("[", 0, 1));
1818
assertEquals(-1, UriParser.parseIPLiteral("[]", 0, 2));
19-
assertEquals(3, UriParser.parseIPLiteral("[0]", 0, 3));
20-
assertEquals(-1, UriParser.parseIPLiteral("[0", 0, 2));
19+
assertEquals(4, UriParser.parseIPLiteral("[::]", 0, 4));
20+
assertEquals(-1, UriParser.parseIPLiteral("[::", 0, 3));
2121
}
2222

2323
@Test
@@ -55,6 +55,90 @@ public void testParseRegName() {
5555
assertEquals(15, UriParser.parseRegName("10.0.0.1.nip.io", 0, 15));
5656
}
5757

58+
@Test
59+
public void testParseIPvFuture() {
60+
assertEquals(6, UriParser.parseIPvFuture("v123.a", 0, 6));
61+
assertEquals(6, UriParser.parseIPvFuture("v123.:", 0, 6));
62+
assertEquals(6, UriParser.parseIPvFuture("v123.!", 0, 6));
63+
}
64+
65+
@Test
66+
public void testParseIPv6Address() {
67+
// Rule 1
68+
assertIPv6Address("0:1:2:3:4:5:1.1.1.1");
69+
assertIPv6Address("0:1:2:3:4:5:7:8");
70+
// Rule 2
71+
assertIPv6Address("::0:1:2:3:4:1.1.1.1");
72+
assertIPv6Address("::0:1:2:3:4:5:6");
73+
// Rule 3
74+
assertIPv6Address("0::0:1:2:3:1.1.1.1");
75+
assertIPv6Address("0::0:1:2:3:4:5");
76+
assertIPv6Address("::0:1:2:3:1.1.1.1");
77+
assertIPv6Address("::0:1:2:3:4:5");
78+
// Rule 4
79+
assertIPv6Address("0:1::0:1:2:1.1.1.1");
80+
assertIPv6Address("0:1::0:1:2:3:4");
81+
assertIPv6Address("0::0:1:2:1.1.1.1");
82+
assertIPv6Address("0::0:1:2:3:4");
83+
assertIPv6Address("::0:1:2:1.1.1.1");
84+
assertIPv6Address("::0:1:2:3:4");
85+
// Rule 5
86+
assertIPv6Address("0:1:2::0:1:1.1.1.1");
87+
assertIPv6Address("0:1:2::0:1:2:3");
88+
assertIPv6Address("0:1::0:1:1.1.1.1");
89+
assertIPv6Address("0:1::0:1:2:3");
90+
assertIPv6Address("0::0:1:1.1.1.1");
91+
assertIPv6Address("0::0:1:2:3");
92+
assertIPv6Address("::0:1:1.1.1.1");
93+
assertIPv6Address("::0:1:2:3");
94+
// Rule 6
95+
assertIPv6Address("0:1:2:3::0:1.1.1.1");
96+
assertIPv6Address("0:1:2:3::0:1:2");
97+
assertIPv6Address("0:1:2::0:1.1.1.1");
98+
assertIPv6Address("0:1:2::0:1:2");
99+
assertIPv6Address("0:1::0:1.1.1.1");
100+
assertIPv6Address("0:1::0:1:2");
101+
assertIPv6Address("0::0:1.1.1.1");
102+
assertIPv6Address("0::0:1:2");
103+
assertIPv6Address("::0:1.1.1.1");
104+
assertIPv6Address("::0:1:2");
105+
// Rule 7
106+
assertIPv6Address("0:1:2:3:4::1.1.1.1");
107+
assertIPv6Address("0:1:2:3:4::0:1");
108+
assertIPv6Address("0:1:2:3::1.1.1.1");
109+
assertIPv6Address("0:1:2:3::0:1");
110+
assertIPv6Address("0:1:2::1.1.1.1");
111+
assertIPv6Address("0:1:2::0:1");
112+
assertIPv6Address("0:1::1.1.1.1");
113+
assertIPv6Address("0:1::0:1");
114+
assertIPv6Address("0::1.1.1.1");
115+
assertIPv6Address("0::0:1");
116+
assertIPv6Address("::1.1.1.1");
117+
assertIPv6Address("::0:1");
118+
// Rule 8
119+
assertIPv6Address("0:1:2:3:4:5::0");
120+
assertIPv6Address("0:1:2:3:4::0");
121+
assertIPv6Address("0:1:2:3::0");
122+
assertIPv6Address("0:1:2::0");
123+
assertIPv6Address("0:1::0");
124+
assertIPv6Address("0::0");
125+
assertIPv6Address("::0");
126+
// Rule 9
127+
assertIPv6Address("0:1:2:3:4:5:6::");
128+
assertIPv6Address("0:1:2:3:4:5::");
129+
assertIPv6Address("0:1:2:3:4::");
130+
assertIPv6Address("0:1:2:3::");
131+
assertIPv6Address("0:1:2::");
132+
assertIPv6Address("0:1::");
133+
assertIPv6Address("0::");
134+
assertIPv6Address("::");
135+
}
136+
137+
private void assertIPv6Address(String address) {
138+
int len = UriParser.parseIPv6Address(address, 0, address.length());
139+
assertEquals(len, address.length());
140+
}
141+
58142
@Test
59143
public void testParseHost() {
60144
assertEquals(14, UriParser.parseHost("example-fr.com", 0, 14));
@@ -65,6 +149,39 @@ public void testParseHost() {
65149
assertEquals(8, UriParser.parseHost("10.0.0.1:8080", 0, 15));
66150
}
67151

152+
@Test
153+
public void testParseH16() {
154+
assertEquals(-1, UriParser.parseH16("", 0, 0));
155+
assertEquals(-1, UriParser.parseH16("Z", 0, 1));
156+
for (int i = 0;i < 0xFFFF;i++) {
157+
String s = Integer.toHexString(i);
158+
assertEquals(s.length(), UriParser.parseH16(s, 0, s.length()));
159+
}
160+
assertEquals(4, UriParser.parseH16("FFFFF", 0, 5));
161+
}
162+
163+
@Test
164+
public void testParseDoubleColon() {
165+
assertEquals(2, UriParser._parseDoubleColon("::", 0, 2));
166+
assertEquals(-1, UriParser._parseDoubleColon(":", 0, 1));
167+
assertEquals(-1, UriParser._parseDoubleColon(":a", 0, 2));
168+
}
169+
170+
@Test
171+
public void testParseH16ColonH16() {
172+
assertEquals(3, UriParser._parseH16ColonH16("0:0", 0, 3));
173+
assertEquals(-1, UriParser._parseH16ColonH16("0:", 0, 2));
174+
assertEquals(6, UriParser._parseH16ColonH16("0:FFFF", 0, 6));
175+
}
176+
177+
@Test
178+
public void testParseLs32() {
179+
assertEquals(3, UriParser.parseLs32("0:0", 0, 3));
180+
assertEquals(6, UriParser.parseLs32("0:FFFF", 0, 6));
181+
assertEquals(7, UriParser.parseLs32("0.0.0.0", 0, 7));
182+
assertEquals(11, UriParser.parseLs32("192.168.0.1", 0, 11));
183+
}
184+
68185
@Test
69186
public void testParseHostAndPort() {
70187
assertHostAndPort("10.0.0.1.nip.io", -1, "10.0.0.1.nip.io");

0 commit comments

Comments
 (0)