Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can a socket connect() to its own ephemeral port?

I can reliably get a Winsock socket to connect() to itself if I connect to localhost with a port in the range of automatically assigned ephemeral ports (5000–65534). Specifically, Windows appears to have a system-wide rolling port number which is the next port that it will try to assign as a local port number for a client socket. If I create sockets until the assigned number is just below my target port number, and then repeatedly create a socket and attempt to connect to that port number, I can usually get the socket to connect to itself.

I first got it to happen in an application that repeatedly tries to connect to a certain port on localhost, and when the service is not listening it very rarely successfully establishes a connection and receives the message that it initially sent (which happens to be a Redis PING command).

An example, in Python (run with nothing listening to the target port):

import socket

TARGET_PORT = 49400

def mksocket():
    return socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)

while True:
    sock = mksocket()
    sock.bind(('127.0.0.1', 0))
    host, port = sock.getsockname()
    if port > TARGET_PORT - 10 and port < TARGET_PORT:
        break
    print port

while port < TARGET_PORT:
    sock = mksocket()
    err = None
    try:
        sock.connect(('127.0.0.1', TARGET_PORT))
    except socket.error, e:
        err = e
    host, port = sock.getsockname()
    if err:
        print 'Unable to connect to port %d, used local port %d: %s' % (TARGET_PORT, port, err)
    else:
        print 'Connected to port %d, used local port %d' (TARGET_PORT, port)

On my Mac machine, this eventually terminates with Unable to connect to port 49400, used local port 49400. On my Windows 7 machine, a connection is successfully established and it prints Connected to port 49400, used local port 49400. The resulting socket receives any data that is sent to it.

Is this a bug in Winsock? Is this a bug in my code?

Edit: Here is a screenshot of TcpView with the offending connection shown:

python.exe 8108 TCP (my HOSTNAME) 49400 localhost 49400 ESTABLISHED

like image 505
John Calsbeek Avatar asked Jul 11 '13 03:07

John Calsbeek


1 Answers

This appears to be a 'simultaneous initiation' as described in #3.4 of RFC 793. See Figure 8. Note that neither side is in state LISTEN at any stage. In your case, both ends are the same: that would cause it to work exactly as described in the RFC.

like image 121
user207421 Avatar answered Sep 23 '22 05:09

user207421