Anorov / PySocks

A SOCKS proxy client and wrapper for Python.
Other
1.23k stars 258 forks source link

SOCKS5 and UDP error #14

Closed duncanwebb closed 9 years ago

duncanwebb commented 10 years ago

I'm trying to write a small test application to test if a Proxy supports UDP over SOCKS5.

Currently the application seems to be sending messages correctly but receiving is falling over when receiving the echoed data. File "E:\pacs\socks.py", line 330, in recvfrom peerhost, peerport = self.proxy_peername TypeError: 'NoneType' object is not iterable

I cannot see how self.proxy_peername is being initialized with socks5 proxy. My test environment is using proxy plus (free edition) with SOCKS activated and the following code

#! /usr/bin/env python

# Client and server for udp (datagram) echo.
#
# Usage: udpecho -s [port]            (to start a server)
# or:    udpecho -c host [port] <file (client)

import sys
import socks
from socket import *

ECHO_PORT = 50000 + 7
BUFSIZE = 1024

def main():
    if len(sys.argv) < 2:
        usage()
    if sys.argv[1] == '-s':
        server()
    elif sys.argv[1] == '-c':
        client()
    elif sys.argv[1] == '-ss':
        sockserver()
    elif sys.argv[1] == '-sc':
        sockclient()
    else:
        usage()

def usage():
    sys.stdout = sys.stderr
    print 'Usage: udpecho -s [port]            (server)'
    print 'or:    udpecho -c host [port] <file (client)'
    sys.exit(2)

def server():
    if len(sys.argv) > 2:
        port = eval(sys.argv[2])
    else:
        port = ECHO_PORT
    s = socket(AF_INET, SOCK_DGRAM)
    s.bind(('', port))
    print 'udp echo server ready'
    while 1:
        data, addr = s.recvfrom(BUFSIZE)
        print 'server received', `data`, 'from', `addr`
        s.sendto(data, addr)

def client():
    if len(sys.argv) < 3:
        usage()
    host = sys.argv[2]
    if len(sys.argv) > 3:
        port = eval(sys.argv[3])
    else:
        port = ECHO_PORT
    addr = host, port
    s = socket(AF_INET, SOCK_DGRAM)
    s.settimeout(100)
    s.bind(('', 0))
    print 'udp echo client ready, reading stdin'
    while 1:
        line = sys.stdin.readline()
        if not line:
            break
        s.sendto(line, addr)
        data, fromaddr = s.recvfrom(BUFSIZE)
        print 'client received', `data`, 'from', `fromaddr`

def sockserver():
    if len(sys.argv) > 2:
        port = eval(sys.argv[2])
    else:
        port = ECHO_PORT
    socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 1080)
    s = socks.socksocket(AF_INET, SOCK_DGRAM)
    s.bind(('', port))
    print 'udp echo server ready'
    while 1:
        data, addr = s.recvfrom(BUFSIZE)
        print 'server received', `data`, 'from', `addr`
        s.sendto(data, addr)

def sockclient():
    if len(sys.argv) < 3:
        usage()
    host = sys.argv[2]
    if len(sys.argv) > 3:
        port = eval(sys.argv[3])
    else:
        port = ECHO_PORT
    addr = host, port
    s = socks.socksocket(AF_INET, SOCK_DGRAM)
    s.set_proxy(socks.SOCKS5, "localhost")
    s.settimeout(100)
    s.bind(('', 0))

    print 'udp echo client ready, reading stdin'
    try:
        while 1:
            line = sys.stdin.readline()
            if not line:
                break
            print 'sending', line, 'to', addr
            s.sendto(line, addr)
            res = s.recvfrom(BUFSIZE)
            print '%r' % res
            print 'client received', `data`, 'from', `fromaddr`
    except KeyboardInterrupt:
        print 'Bye'
        pass

main()

In one command window I run: udpecho.py -s

In the another window: udpecho.py -sc localhost test

The send is working as the server sees udp echo server ready server received 'test\n' from ('127.0.0.1', 49165)

But the receive is failing.

duncanwebb commented 10 years ago

Sorry I don't know the markup of github.

vadmium commented 10 years ago

Stick a line of three back-ticks before and after the code to escape the markup.

It looks like the code I used when I introduced the UDP support calls sock.connect() before calling send() or recvfrom(). That may be a quick workaround for you, but I will try and make a proper fix that receives packets when the socket is not “connected”.

duncanwebb commented 9 years ago

Thanks for the fix, this works (I'd already commented out this block of code).

I wasn't able to find any code examples of using the pysocks with UDP. It looks like bind calls connect() but it may be exiting before with no authentication.

vadmium commented 9 years ago

FYI I added the UDP support fairly recently. Here is a generic script that I originally used it for: https://gist.github.com/vadmium/675e87a148cba14b9e78. It depends on a couple of my personal library routines, but you should be able see the general idea.

Calling bind() on the high-level UDP Py Socks object does invoke a low-level connect() to set the address of the UDP relay. However I thought it best for the high-level connect() to work differently: it sets the remote address and port that the UDP relay forwards packets to, as well as filters packets received according to the remote address that the relay indicates.

duncanwebb commented 9 years ago

Thanks again for the explanation. Using bind works a treat, I can use proxy+ running on one machine and the server and the client on another and packet tracing the conversation there is a nice trace: socksclient -> proxy (socks) proxy -> server (udp) server -> proxy (udp) this is an echo server proxy -> socksclient (socks)

So changing the code to use connect reveals a small python 2 problem

def sockclient():
    if len(sys.argv) < 3:
        usage()
    host = sys.argv[2]
    if len(sys.argv) > 3:
        port = eval(sys.argv[3])
    else:
        port = ECHO_PORT
    addr = host, port
    s = socks.socksocket(AF_INET, SOCK_DGRAM)
    s.set_proxy(socks.SOCKS5, PROXY_HOST)
    s.connect(addr)
    #s.bind(('', 0))
    s.settimeout(100)

    print 'udp echo client ready, reading stdin'
    try:
        while 1:
            line = sys.stdin.readline()
            if not line:
                break
            print 'sending', line, 'to', addr
            s.sendto(line, addr)
            res = s.recvfrom(BUFSIZE)
            #print '%r' % (res,)
            data, fromaddr = res
            print 'client received', `data`, 'from', `fromaddr`
    except KeyboardInterrupt:
        print 'Bye'

Traceback (most recent call last): File "E:\pacs\udpecho.py", line 116, in main() File "E:\pacs\udpecho.py", line 27, in main sockclient() File "E:\pacs\udpecho.py", line 108, in sockclient res = s.recvfrom(BUFSIZE) File "E:\pacs\socks.py", line 333, in recvfrom filterhost = socket.inet_pton(self.family, peerhost).strip(b"\x00") AttributeError: 'module' object has no attribute 'inet_pton'

vadmium commented 9 years ago

Ah crap. I suspect you are using Windows, where inet_pton() is not available. I was trying to check if the host is a null address like 0.0.0.0, but without being IPv4 specific. Maybe it’s simplest to just use the equivalent IPv4 function instead, since IPv6 isn’t supported anyway. Will update PR #15 when I get a chance.

duncanwebb commented 9 years ago

Indeed I'm using Windows, E:\ gives you your confirmation.

Thanks for the package, it has helped me prove that I proxy is not working.