Skip to content

Commit fcc3dbb

Browse files
committed
Add opt-in to enable server hostname verification
Hostname verification isn't performed by default in the TLS handshake, this commit makes it easier to enable server hostname verification for both blocking and non-blocking IO modes. References #394
1 parent 149b6c7 commit fcc3dbb

15 files changed

+610
-20
lines changed

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

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515

1616
package com.rabbitmq.client;
1717

18-
import static java.util.concurrent.TimeUnit.*;
19-
2018
import com.rabbitmq.client.impl.AMQConnection;
2119
import com.rabbitmq.client.impl.ConnectionParams;
2220
import com.rabbitmq.client.impl.CredentialsProvider;
@@ -32,6 +30,10 @@
3230
import com.rabbitmq.client.impl.recovery.RetryHandler;
3331
import com.rabbitmq.client.impl.recovery.TopologyRecoveryFilter;
3432

33+
import javax.net.SocketFactory;
34+
import javax.net.ssl.SSLContext;
35+
import javax.net.ssl.SSLSocketFactory;
36+
import javax.net.ssl.TrustManager;
3537
import java.io.IOException;
3638
import java.net.URI;
3739
import java.net.URISyntaxException;
@@ -50,10 +52,8 @@
5052
import java.util.concurrent.ThreadFactory;
5153
import java.util.concurrent.TimeoutException;
5254
import java.util.function.Predicate;
53-
import javax.net.SocketFactory;
54-
import javax.net.ssl.SSLContext;
55-
import javax.net.ssl.SSLSocketFactory;
56-
import javax.net.ssl.TrustManager;
55+
56+
import static java.util.concurrent.TimeUnit.MINUTES;
5757

5858
/**
5959
* Convenience factory class to facilitate opening a {@link Connection} to a RabbitMQ node.
@@ -132,7 +132,7 @@ public class ConnectionFactory implements Cloneable {
132132
// connections uses, see rabbitmq/rabbitmq-java-client#86
133133
private ExecutorService shutdownExecutor;
134134
private ScheduledExecutorService heartbeatExecutor;
135-
private SocketConfigurator socketConf = new DefaultSocketConfigurator();
135+
private SocketConfigurator socketConf = SocketConfigurators.defaultConfigurator();
136136
private ExceptionHandler exceptionHandler = new DefaultExceptionHandler();
137137
private CredentialsProvider credentialsProvider = new DefaultCredentialsProvider(DEFAULT_USER, DEFAULT_PASS);
138138

@@ -729,6 +729,44 @@ public void useSslProtocol(SSLContext context) {
729729
setSocketFactory(context.getSocketFactory());
730730
}
731731

732+
/**
733+
* Enable server hostname verification for TLS connections.
734+
* <p>
735+
* This enables hostname verification regardless of the IO mode
736+
* used (blocking or non-blocking IO).
737+
* <p>
738+
* This can be called typically after setting the {@link SSLContext}
739+
* with one of the <code>useSslProtocol</code> methods.
740+
*
741+
* @see NioParams#enableHostnameVerification()
742+
* @see NioParams#setSslEngineConfigurator(SslEngineConfigurator)
743+
* @see SslEngineConfigurators#ENABLE_HOSTNAME_VERIFICATION
744+
* @see SocketConfigurators#ENABLE_HOSTNAME_VERIFICATION
745+
* @see ConnectionFactory#useSslProtocol(String)
746+
* @see ConnectionFactory#useSslProtocol(SSLContext)
747+
* @see ConnectionFactory#useSslProtocol()
748+
* @see ConnectionFactory#useSslProtocol(String, TrustManager)
749+
*/
750+
public void enableHostnameVerification() {
751+
enableHostnameVerificationForNio();
752+
enableHostnameVerificationForBlockingIo();
753+
}
754+
755+
protected void enableHostnameVerificationForNio() {
756+
if (this.nioParams == null) {
757+
this.nioParams = new NioParams();
758+
}
759+
this.nioParams = this.nioParams.enableHostnameVerification();
760+
}
761+
762+
protected void enableHostnameVerificationForBlockingIo() {
763+
if (this.socketConf == null) {
764+
this.socketConf = SocketConfigurators.builder().defaultConfigurator().enableHostnameVerification().build();
765+
} else {
766+
this.socketConf = this.socketConf.andThen(SocketConfigurators.enableHostnameVerification());
767+
}
768+
}
769+
732770
public static String computeDefaultTlsProcotol(String[] supportedProtocols) {
733771
if(supportedProtocols != null) {
734772
for (String supportedProtocol : supportedProtocols) {

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
import java.io.IOException;
1919
import java.nio.channels.SocketChannel;
20+
import java.util.Objects;
2021

22+
@FunctionalInterface
2123
public interface SocketChannelConfigurator {
2224

2325
/**
@@ -26,4 +28,18 @@ public interface SocketChannelConfigurator {
2628
*/
2729
void configure(SocketChannel socketChannel) throws IOException;
2830

31+
/**
32+
* Returns a composed configurator that performs, in sequence, this
33+
* operation followed by the {@code after} operation.
34+
*
35+
* @param after the operation to perform after this operation
36+
* @return a composed configurator that performs in sequence this
37+
* operation followed by the {@code after} operation
38+
* @throws NullPointerException if {@code after} is null
39+
*/
40+
default SocketChannelConfigurator andThen(SocketChannelConfigurator after) {
41+
Objects.requireNonNull(after);
42+
return t -> { configure(t); after.configure(t); };
43+
}
44+
2945
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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+
15+
16+
package com.rabbitmq.client;
17+
18+
/**
19+
* Ready-to-use instances and builder for {@link SocketChannelConfigurator}.
20+
* <p>
21+
* Note {@link SocketChannelConfigurator}s can be combined with
22+
* {@link SocketChannelConfigurator#andThen(SocketChannelConfigurator)}.
23+
*
24+
* @since 5.5.0
25+
*/
26+
public abstract class SocketChannelConfigurators {
27+
28+
/**
29+
* Disable Nagle's algorithm.
30+
*/
31+
public static final SocketChannelConfigurator DISABLE_NAGLE_ALGORITHM =
32+
socketChannel -> SocketConfigurators.DISABLE_NAGLE_ALGORITHM.configure(socketChannel.socket());
33+
34+
/**
35+
* Default {@link SocketChannelConfigurator} that disables Nagle's algorithm.
36+
*/
37+
public static final SocketChannelConfigurator DEFAULT = DISABLE_NAGLE_ALGORITHM;
38+
39+
/**
40+
* The default {@link SocketChannelConfigurator} that disables Nagle's algorithm.
41+
*
42+
* @return
43+
*/
44+
public static SocketChannelConfigurator defaultConfigurator() {
45+
return DEFAULT;
46+
}
47+
48+
/**
49+
* {@link SocketChannelConfigurator} that disables Nagle's algorithm.
50+
*
51+
* @return
52+
*/
53+
public static SocketChannelConfigurator disableNagleAlgorithm() {
54+
return DISABLE_NAGLE_ALGORITHM;
55+
}
56+
57+
/**
58+
* Builder to configure and creates a {@link SocketChannelConfigurator} instance.
59+
*
60+
* @return
61+
*/
62+
public static SocketChannelConfigurators.Builder builder() {
63+
return new SocketChannelConfigurators.Builder();
64+
}
65+
66+
public static class Builder {
67+
68+
private SocketChannelConfigurator configurator = channel -> {
69+
};
70+
71+
/**
72+
* Set default configuration.
73+
*
74+
* @return
75+
*/
76+
public Builder defaultConfigurator() {
77+
configurator = configurator.andThen(DEFAULT);
78+
return this;
79+
}
80+
81+
/**
82+
* Disable Nagle's Algorithm.
83+
*
84+
* @return
85+
*/
86+
public Builder disableNagleAlgorithm() {
87+
configurator = configurator.andThen(DISABLE_NAGLE_ALGORITHM);
88+
return this;
89+
}
90+
91+
/**
92+
* Add an extra configuration step.
93+
*
94+
* @param extraConfiguration
95+
* @return
96+
*/
97+
public Builder add(SocketChannelConfigurator extraConfiguration) {
98+
configurator = configurator.andThen(extraConfiguration);
99+
return this;
100+
}
101+
102+
/**
103+
* Return the configured {@link SocketConfigurator}.
104+
*
105+
* @return
106+
*/
107+
public SocketChannelConfigurator build() {
108+
return configurator;
109+
}
110+
}
111+
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,31 @@
1717

1818
import java.io.IOException;
1919
import java.net.Socket;
20+
import java.util.Objects;
2021

22+
@FunctionalInterface
2123
public interface SocketConfigurator {
24+
2225
/**
2326
* Provides a hook to insert custom configuration of the sockets
2427
* used to connect to an AMQP server before they connect.
2528
*/
2629
void configure(Socket socket) throws IOException;
30+
31+
/**
32+
* Returns a composed configurator that performs, in sequence, this
33+
* operation followed by the {@code after} operation.
34+
*
35+
* @param after the operation to perform after this operation
36+
* @return a composed configurator that performs in sequence this
37+
* operation followed by the {@code after} operation
38+
* @throws NullPointerException if {@code after} is null
39+
*/
40+
default SocketConfigurator andThen(SocketConfigurator after) {
41+
Objects.requireNonNull(after);
42+
return t -> {
43+
configure(t);
44+
after.configure(t);
45+
};
46+
}
2747
}

0 commit comments

Comments
 (0)