Home > Software design >  Python socket hangs on connect/accept
Python socket hangs on connect/accept

Time:02-16

I'm trying to send packets using sockets, and was able to do so just fine until this morning. I'm not sure what's going on. The packets are showing up in tcpdump but the server and the client cannot connect to each other.

netcat.py

import socket
import argparse
import sys
import os
import re
import threading


def convertContent(content: str = "") -> bytes:
    byteContent = []
    # grab the hex from the content
    for i in range(len(content)):
        if content[i] == "\\" and content[i 1] == "x":
            byteContent.append(f"{content[i 2]}{content[i 3]}")

    # grab the non hex from the content, split it on the hex
    stringContent = re.split(r"\\x.{2}", content)

    byteIndex = 0
    newContent = b""
    # Re add the non-hex content, and the hex content
    for word in stringContent:
        newContent  = word.encode()
        if byteIndex < len(byteContent):
            newContent  = bytes.fromhex(byteContent[byteIndex])
            byteIndex  = 1
    newContent = newContent.replace(b"\\n", b"\n").replace(b"\\r", b"\r")
    return newContent


class Netcat():
    '''
    Netcat class that can be used to send/receive TCP packets
    '''
    BUFFER_SIZE = 1024

    def __init__(self):
        pass

    @classmethod
    def createSocket(cls):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # Address might be in a TIME_WAIT status, ignore this
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # Port might be in a TIME_WAIT status, ignore this
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
        return sock

    @classmethod
    def send(cls, hostname: str = "127.0.0.1", srcPort: int = 0, destPort: int = 9999, content: str = "", buffer_size: int = 1024):
        srcPort = int(srcPort)
        destPort = int(destPort)
        try:
            content = convertContent(content=content)
        except:
            pass
        sock = cls.createSocket()

        # Set the source port before sending
        sock.connect((hostname, destPort))
        sock.sendall(content)
        # shutdown might be redundant/unnecessary (tells connected host that we're done sending data)
        sock.shutdown(socket.SHUT_WR)
        while True:
            data = sock.recv(buffer_size)
            if len(data) == 0:
                break
        sock.close()

    @classmethod
    def receive(cls, port: int = 9999, buffer_size: int = 1024):
        if port <= 1024 and os.geteuid() != 0:
            print(f"Listening on port {port} requires superuser privileges!")
            return
        host = ""
        sock = cls.createSocket()
        sock.bind((host, port))
        sock.listen(10)
        conn, addr = sock.accept()
        while True:
            data = conn.recv(buffer_size)
            if not data:
                break
        conn.close()

threading.Thread(target=Netcat.receive,daemon=True).start()
Netcat.send(content="test")

Note: I am sending the packets from one VM to another, rather than sending to myself, but it would be a lot to ask people to spin up a bunch of VMs to reproduce this. The hostname param in the send method should be the actual IP of the receiving machine

I've thrown some print statements, and the server stops on sock.accept(), while the client hangs on sock.connect((hostname, destPort))

I checked the hostname for the server, and it's listening on (0.0.0.0, 8888) (assuming 8888 is the port param), which means its listening on all interfaces on that port, so I dont know why its refusing to connect

I tcpdumped on the server, and its getting the packets, it gets a SYN, then sends out a SYN, ACK, but the rest of the packets are marked as re-transmissions.

I've tried looping the accept & connect lines, thinking maybe some sort of race condition was occurring, but no matter what I do the client can't connect to the server.

Edit: This works on my local machine, but still breaks when I try to send packets over the network. The first 2 steps of the handshake go through SYN & SYN, ACK, but not the third ACK

CodePudding user response:

Don't bind in the client. Working example below, but minor changes to make a standalone script:

import socket
import threading

def receive(port: int = 9999, buffer_size: int = 1024):
    host = ""
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # Address might be in a TIME_WAIT status, ignore this
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind((host, port))
    sock.listen()
    conn, addr = sock.accept()
    while True:
        data = conn.recv(buffer_size)
        if not data:
            break
        print(data)
    conn.close()

def send(hostname: str = "127.0.0.1", destPort: int = 9999, content: str = b"test", buffer_size: int = 1024):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # Address might be in a TIME_WAIT status, ignore this
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # Removed bind
    sock.connect((hostname, destPort))
    sock.sendall(content)
    # shutdown might be redundant/unnecessary (tells connected host that we're done sending data)
    sock.shutdown(socket.SHUT_WR)
    while True:
        data = sock.recv(buffer_size)
        if len(data) == 0:
            break
    sock.close()
    
threading.Thread(target=receive,daemon=True).start()
send()

Output:

b'test'
  • Related