Skip to content

Commit 1ba531b

Browse files
Make sure Address.parseAddress can handle quoted IPv6 addresses
Closes #385. [#159499428] (cherry picked from commit d282928)
1 parent 2e042e5 commit 1ba531b

File tree

2 files changed

+184
-7
lines changed

2 files changed

+184
-7
lines changed

src/main/java/com/rabbitmq/client/Address.java

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,28 @@
1616

1717
package com.rabbitmq.client;
1818

19+
import org.slf4j.Logger;
20+
import org.slf4j.LoggerFactory;
21+
1922
/**
2023
* A representation of network addresses, i.e. host/port pairs,
2124
* with some utility functions for parsing address strings.
2225
*/
2326
public class Address {
24-
/** host name **/
27+
private static final Logger LOGGER = LoggerFactory.getLogger(Address.class);
28+
29+
/**
30+
* host name
31+
**/
2532
private final String _host;
26-
/** port number **/
33+
/**
34+
* port number
35+
**/
2736
private final int _port;
2837

2938
/**
3039
* Construct an address from a host name and port number.
40+
*
3141
* @param host the host name
3242
* @param port the port number
3343
*/
@@ -38,6 +48,7 @@ public Address(String host, int port) {
3848

3949
/**
4050
* Construct an address from a host.
51+
*
4152
* @param host the host name
4253
*/
4354
public Address(String host) {
@@ -47,6 +58,7 @@ public Address(String host) {
4758

4859
/**
4960
* Get the host name
61+
*
5062
* @return the host name
5163
*/
5264
public String getHost() {
@@ -55,23 +67,98 @@ public String getHost() {
5567

5668
/**
5769
* Get the port number
70+
*
5871
* @return the port number
5972
*/
6073
public int getPort() {
6174
return _port;
6275
}
6376

77+
/**
78+
* Extracts hostname or IP address from a string containing a hostname, IP address,
79+
* hostname:port pair or IP address:port pair.
80+
* Note that IPv6 addresses must be quoted with square brackets, e.g. [2001:db8:85a3:8d3:1319:8a2e:370:7348].
81+
*
82+
* @param addressString the string to extract hostname from
83+
* @return the hostname or IP address
84+
*/
85+
public static String parseHost(String addressString) {
86+
// we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
87+
int lastColon = addressString.lastIndexOf(":");
88+
int lastClosingSquareBracket = addressString.lastIndexOf("]");
89+
if (lastClosingSquareBracket == -1) {
90+
String[] parts = addressString.split(":");
91+
if (parts.length > 2) {
92+
String msg = "Address " +
93+
addressString +
94+
" seems to contain an unquoted IPv6 address. Make sure you quote IPv6 addresses like so: [2001:db8:85a3:8d3:1319:8a2e:370:7348]";
95+
LOGGER.error(msg);
96+
throw new IllegalArgumentException(msg);
97+
}
98+
99+
return parts[0];
100+
}
101+
102+
if (lastClosingSquareBracket < lastColon) {
103+
// there is a port
104+
return addressString.substring(0, lastColon);
105+
} else {
106+
return addressString;
107+
}
108+
}
109+
110+
public static int parsePort(String addressString) {
111+
// we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
112+
int lastColon = addressString.lastIndexOf(":");
113+
int lastClosingSquareBracket = addressString.lastIndexOf("]");
114+
if (lastClosingSquareBracket == -1) {
115+
String[] parts = addressString.split(":");
116+
if (parts.length > 2) {
117+
String msg = "Address " +
118+
addressString +
119+
" seems to contain an unquoted IPv6 address. Make sure you quote IPv6 addresses like so: [2001:db8:85a3:8d3:1319:8a2e:370:7348]";
120+
LOGGER.error(msg);
121+
throw new IllegalArgumentException(msg);
122+
}
123+
124+
if (parts.length == 2) {
125+
return Integer.parseInt(parts[1]);
126+
}
127+
128+
return ConnectionFactory.USE_DEFAULT_PORT;
129+
}
130+
131+
if (lastClosingSquareBracket < lastColon) {
132+
// there is a port
133+
return Integer.parseInt(addressString.substring(lastColon + 1));
134+
}
135+
136+
return ConnectionFactory.USE_DEFAULT_PORT;
137+
}
138+
139+
public static boolean isHostWithPort(String addressString) {
140+
// we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
141+
int lastColon = addressString.lastIndexOf(":");
142+
int lastClosingSquareBracket = addressString.lastIndexOf("]");
143+
144+
if (lastClosingSquareBracket == -1) {
145+
return addressString.contains(":");
146+
} else {
147+
return lastClosingSquareBracket < lastColon;
148+
}
149+
}
150+
64151
/**
65152
* Factory method: takes a formatted addressString string as construction parameter
66153
* @param addressString an addressString of the form "host[:port]".
67154
* @return an {@link Address} from the given data
68155
*/
69156
public static Address parseAddress(String addressString) {
70-
int idx = addressString.indexOf(':');
71-
return (idx == -1) ?
72-
new Address(addressString) :
73-
new Address(addressString.substring(0, idx),
74-
Integer.parseInt(addressString.substring(idx+1)));
157+
if (isHostWithPort(addressString)) {
158+
return new Address(parseHost(addressString), parsePort(addressString));
159+
} else {
160+
return new Address(addressString);
161+
}
75162
}
76163

77164
/**
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright (c) 2018 Pivotal Software, Inc. All rights reserved.
2+
//
3+
// This software, the RabbitMQ Java client library, is triple-licensed under the
4+
// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2
5+
// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see
6+
// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL,
7+
// please see LICENSE-APACHE2.
8+
//
9+
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
10+
// either express or implied. See the LICENSE file for specific language governing
11+
// rights and limitations of this software.
12+
//
13+
// If you have any questions regarding licensing, please contact us at
14+
// info@rabbitmq.com.
15+
16+
package com.rabbitmq.client.test;
17+
18+
import com.rabbitmq.client.Address;
19+
import org.junit.Test;
20+
21+
import static org.junit.Assert.assertEquals;
22+
import static org.junit.Assert.assertFalse;
23+
import static org.junit.Assert.assertTrue;
24+
25+
/**
26+
*
27+
*/
28+
public class AddressTest {
29+
30+
@Test public void isHostWithPort() {
31+
assertTrue(Address.isHostWithPort("127.0.0.1:5672"));
32+
assertTrue(Address.isHostWithPort("[1080:0:0:0:8:800:200C:417A]:5672"));
33+
assertTrue(Address.isHostWithPort("[::1]:5672"));
34+
35+
assertFalse(Address.isHostWithPort("127.0.0.1"));
36+
assertFalse(Address.isHostWithPort("[1080:0:0:0:8:800:200C:417A]"));
37+
assertFalse(Address.isHostWithPort("[::1]"));
38+
}
39+
40+
@Test public void parseHost() {
41+
assertEquals("127.0.0.1", Address.parseHost("127.0.0.1:5672"));
42+
assertEquals("[1080:0:0:0:8:800:200C:417A]", Address.parseHost("[1080:0:0:0:8:800:200C:417A]:5673"));
43+
assertEquals("[::1]", Address.parseHost("[::1]:5672"));
44+
45+
assertEquals("127.0.0.1", Address.parseHost("127.0.0.1"));
46+
assertEquals("[1080:0:0:0:8:800:200C:417A]", Address.parseHost("[1080:0:0:0:8:800:200C:417A]"));
47+
assertEquals("[::1]", Address.parseHost("[::1]"));
48+
}
49+
50+
@Test public void parsePort() {
51+
assertEquals(5672, Address.parsePort("127.0.0.1:5672"));
52+
assertEquals(5673, Address.parsePort("[1080:0:0:0:8:800:200C:417A]:5673"));
53+
assertEquals(5672, Address.parsePort("[::1]:5672"));
54+
55+
// "use default port" value
56+
assertEquals(-1, Address.parsePort("127.0.0.1"));
57+
assertEquals(-1, Address.parsePort("[1080:0:0:0:8:800:200C:417A]"));
58+
assertEquals(-1, Address.parsePort("[::1]"));
59+
}
60+
61+
@Test public void parseIPv4() {
62+
assertEquals(addr("192.168.1.10"), Address.parseAddress("192.168.1.10"));
63+
assertEquals(addr("192.168.1.10", 5682), Address.parseAddress("192.168.1.10:5682"));
64+
}
65+
66+
@Test public void parseIPv6() {
67+
// quoted IPv6 addresses without a port
68+
assertEquals(addr("[1080:0:0:0:8:800:200C:417A]"), Address.parseAddress("[1080:0:0:0:8:800:200C:417A]"));
69+
assertEquals(addr("[::1]"), Address.parseAddress("[::1]"));
70+
71+
// quoted IPv6 addresses with a port
72+
assertEquals(addr("[1080:0:0:0:8:800:200C:417A]", 5673), Address.parseAddress("[1080:0:0:0:8:800:200C:417A]:5673"));
73+
assertEquals(addr("[::1]", 5673), Address.parseAddress("[::1]:5673"));
74+
}
75+
76+
@Test(expected = IllegalArgumentException.class)
77+
public void parseUnquotedIPv6() {
78+
// using a non-quoted IPv6 addresses with a port
79+
Address.parseAddress("::1:5673");
80+
}
81+
82+
private Address addr(String addr) {
83+
return new Address(addr);
84+
}
85+
86+
private Address addr(String addr, int port) {
87+
return new Address(addr, port);
88+
}
89+
90+
}

0 commit comments

Comments
 (0)