From c59b4cd966eb71b2fa7c4d7fdb1f8d6f1df600bd Mon Sep 17 00:00:00 2001 From: Thomas White Date: Wed, 28 Apr 2010 17:46:06 +0000 Subject: [PATCH] HADOOP-6722. NetUtils.connect should check that it hasn't connected a socket to itself. Contributed by Todd Lipcon. git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@939026 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + src/java/org/apache/hadoop/net/NetUtils.java | 16 ++++++ .../org/apache/hadoop/net/TestNetUtils.java | 56 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/test/core/org/apache/hadoop/net/TestNetUtils.java diff --git a/CHANGES.txt b/CHANGES.txt index 00d8599dd4..d4df079eae 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -376,6 +376,9 @@ Trunk (unreleased changes) HADOOP-6724. IPC doesn't properly handle IOEs thrown by socket factory. (Todd Lipcon via tomwhite) + HADOOP-6722. NetUtils.connect should check that it hasn't connected a socket + to itself. (Todd Lipcon via tomwhite) + Release 0.21.0 - Unreleased INCOMPATIBLE CHANGES diff --git a/src/java/org/apache/hadoop/net/NetUtils.java b/src/java/org/apache/hadoop/net/NetUtils.java index 7cebfd60ff..ffe4982415 100644 --- a/src/java/org/apache/hadoop/net/NetUtils.java +++ b/src/java/org/apache/hadoop/net/NetUtils.java @@ -26,6 +26,7 @@ import java.net.SocketAddress; import java.net.URI; import java.net.UnknownHostException; +import java.net.ConnectException; import java.nio.channels.SocketChannel; import java.util.Map.Entry; import java.util.regex.Pattern; @@ -367,6 +368,21 @@ public static void connect(Socket socket, } else { SocketIOWithTimeout.connect(ch, endpoint, timeout); } + + // There is a very rare case allowed by the TCP specification, such that + // if we are trying to connect to an endpoint on the local machine, + // and we end up choosing an ephemeral port equal to the destination port, + // we will actually end up getting connected to ourself (ie any data we + // send just comes right back). This is only possible if the target + // daemon is down, so we'll treat it like connection refused. + if (socket.getLocalPort() == socket.getPort() && + socket.getLocalAddress().equals(socket.getInetAddress())) { + LOG.info("Detected a loopback TCP socket, disconnecting it"); + socket.close(); + throw new ConnectException( + "Localhost targeted connection resulted in a loopback. " + + "No daemon is listening on the target port."); + } } /** diff --git a/src/test/core/org/apache/hadoop/net/TestNetUtils.java b/src/test/core/org/apache/hadoop/net/TestNetUtils.java new file mode 100644 index 0000000000..52fb2090d5 --- /dev/null +++ b/src/test/core/org/apache/hadoop/net/TestNetUtils.java @@ -0,0 +1,56 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.net; + +import org.junit.Test; +import static org.junit.Assert.*; + +import java.net.Socket; +import java.net.ConnectException; +import java.net.InetSocketAddress; +import org.apache.hadoop.conf.Configuration; + +public class TestNetUtils { + + /** + * Test that we can't accidentally connect back to the connecting socket due + * to a quirk in the TCP spec. + * + * This is a regression test for HADOOP-6722. + */ + @Test + public void testAvoidLoopbackTcpSockets() throws Exception { + Configuration conf = new Configuration(); + + Socket socket = NetUtils.getDefaultSocketFactory(conf) + .createSocket(); + socket.bind(new InetSocketAddress("127.0.0.1", 0)); + System.err.println("local address: " + socket.getLocalAddress()); + System.err.println("local port: " + socket.getLocalPort()); + try { + NetUtils.connect(socket, + new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort()), + 20000); + socket.close(); + fail("Should not have connected"); + } catch (ConnectException ce) { + System.err.println("Got exception: " + ce); + assertTrue(ce.getMessage().contains("resulted in a loopback")); + } + } +}