Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WSL2 and Windows can bind listening TCP socket on 127.0.0.1 using same port without setting SO_REUSEADDR #7150

Closed
1 of 2 tasks
2pl opened this issue Jul 5, 2021 · 3 comments

Comments

@2pl
Copy link

2pl commented Jul 5, 2021

Windows Build Number

Microsoft Windows [Version 10.0.19042.1052]

WSL Version

  • WSL 2
  • WSL 1

Kernel Version

Linux version 5.4.72-microsoft-standard-WSL2 (oe-user@oe-host) (gcc version 8.2.0 (GCC)) #1 SMP Wed Oct 28 23:40:43 UTC 2020

Distro Version

Ubuntu 20.04

Other Software

No response

Repro Steps

Can be easily reproduced in two steps.

1/ using CMD or PowerShell start a process listening on 127.0.01:9999

PS C:\Users\XXX\tmp> python .\tcpserver.py --port 9999
Starting up echo server on 127.0.0.1 port 9999
Waiting to receive message from client

At this point from Windows you can see one listener on port 9999

PS C:\Users\XXXX> netstat -an  | Select-String 9999

  TCP    127.0.0.1:9999         0.0.0.0:0              LISTENING


PS C:\Users\XXXX>

2/ from ubuntu do the same

socat TCP4-LISTEN:9999,bind=127.0.0.1,fork -

Now from Windows you can see two listeners on port 9999

PS C:\Users\XXXX> netstat -an  | Select-String 9999

  TCP    127.0.0.1:9999         0.0.0.0:0              LISTENING
  TCP    127.0.0.1:9999         0.0.0.0:0              LISTENING


PS C:\Users\XXXX>

Note that the order of the steps matter a lot, as doing 2 before 1 would not trigger the issue.

For reference I used the python code snippet below :

#!/usr/bin/env python    
import argparse
import sys
import socket

host = '127.0.0.1'
data_payload = 2048
backlog = 5 

def echo_server(port):
    """ A simple echo server """
    socket.setdefaulttimeout(10)
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # DO NOT Enable reuse address/port 
    # sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_address = (host, port)
    print ("Starting up echo server on %s port %s" % server_address)
    sock.bind(server_address)
    sock.listen(backlog) 
    while True: 
        print ("Waiting to receive message from client")
        try:
            client, address = sock.accept() 
        except socket.timeout:
            pass
        except:
            raise
        else:
            data = client.recv(data_payload) 
            if data:
                print ("Data: %s" %data)
                client.send(data)
                print ("sent %s bytes back to %s" % (data, address))
            client.close() 

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Socket Server Example')
    parser.add_argument('--port', action="store", dest="port", type=int, required=True)
    given_args = parser.parse_args() 
    port = given_args.port
    echo_server(port)

Expected Behavior

At Step 2 expected behavior is a failure of the socat command because the address is already in use

socat TCP4-LISTEN:9999,bind=127.0.0.1,fork -
2021/07/05 12:14:52 socat[1901] E bind(5, {AF=2 127.0.0.1:9999}, 16): Address already in use

Actual Behavior

The socat command happily succeeds, resulting in:

  • the address being opened twice while the user is legitimately expecting to have the exclusive ownership of the socket address given SO_REUSEADDR is NOT set
  • unexpected consequences for both processes listening (and the end user)

Diagnostic Logs

No response

@2pl 2pl changed the title WSL2 and Windows can bind listening socket 127.0.0.1 using same port WSL2 and Windows can bind listening TCP socket on 127.0.0.1 using same port without using SO_REUSEADDR Jul 5, 2021
@2pl 2pl changed the title WSL2 and Windows can bind listening TCP socket on 127.0.0.1 using same port without using SO_REUSEADDR WSL2 and Windows can bind listening TCP socket on 127.0.0.1 using same port without setting SO_REUSEADDR Jul 5, 2021
@OneBlue
Copy link
Collaborator

OneBlue commented Jul 6, 2021

Thanks for reporting this @2pl.

There is currently no mechanism to make the bind() call fail inside WSL2 if the port is in use in Windows.
The second entry you're seeing in netstat's output is the localhost relay that relays traffic between WSL2 and Windows.
This relay is binding the Windows port with SO_REUSEADDR as well so that's why there are two entries.

We might not need that flag on the Windows though. I'll look into that.

@2pl
Copy link
Author

2pl commented Jul 7, 2021

Thank you @OneBlue for the details on how the relay works.
Are there any plan to improve this and make the bind call fail inside WSL2 ?
With the current behaviour it's just impossible to safely bind a port on 127.0.0.1 inside WSL2.
A successful bind with SO_REUSEADDR unset means you acquired exclusivity on the port, but WSL2 is breaking this.
You could be interfering with an other service inside Windows listening on same port without even being aware !
This happened to me in a real life scenario: a jupyter python kernel listener inside WSL2 started to randomly intercept traffic that should have been processed by an zscaler proxy agent inside Windows.
At best you waste time trying to figure out what's going and why things randomly fail.
At worst you are stuck because you cannot change the configuration.

Copy link
Contributor

This issue has been automatically closed since it has not had any activity for the past year. If you're still experiencing this issue please re-file this as a new issue or feature request.

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants