Usando proxies con Python
Presento un pequeño código para usar proxies SOCKS 4 y 5 en python, en principio la idea era mostrarlo junto un servidor proxy en C, pero viendo que con esto se podía tumbar, tardará un poco más...
El código es este (para descargar completo más abajo): from socket import socket, AF_INET, SOCK_STREAM, inet_aton, getaddrinfo,\
SOL_TCP
from string import letters
from random import choice
import sys
Lee siempre una cantidad de un socket
def sock_read(sock, n):
b = "" # Inicia el buffer
i = 0 # Tamaño del buffer
while (i < n): # Mientas no lee todo
c = sock.recv(n - i) # Lee lo que falta
if (len(c) < 1): # Si no se leyó nada
# es que el socket está cerrado
raise Exception("Closed socket")
b += c # Añadir lo nuevo al buffer
i += len(c) # y actualizar su tamaño
return b # Devolver el buffer
Convertir sockaddr a una dirección de red
def str2host(addr, ipv = 4):
for i in letters: # Si hay letras
if i in addr[0]: # es un nombre de dominio
d = getaddrinfo(addr[0], addr[1], 0, 0, SOL_TCP)
# La primera dirección, el
# primer dato de la última tupla
for j in d:
if (len(j[-1]) == 2) and (ipv == 4): # A ipv4 tuple
return inet_aton(j[-1][0])
elif (len(j[-1]) == 4) and (ipv == 6): # A ipv6 tuple
return inet_aton(j[-1][0])
raise Exception("Host not found")
# Sino se traduce directamente
return inet_aton(addr[0])
Pasa un número de 2 bytes a str
Suponiendo little endian
def uint16str(n):
data = [] # Prepara el buffer
data.append( chr(n & 255) ) # Lee el byte menos significativo
n >>= 8 # Mueve todo un byte a la derecha
data.append( chr(n & 255) ) # Lee el nuevo lsb
data.reverse() # Le da la vuelta (little endian)
return ''.join(data) # Y lo convierte en una cadeba
Envía el código de excepción de un servidor SOCKS4
def SOCKS4_ex(ans):
# Relación código/respuesta
s4_ex = { 91 : "Request rejected or failed",
92 : "Request rejected becasue SOCKS server cannot connect to
identd on the client",
93 : "Request rejected because the client program and identd
report different user-ids"}
raise Exception(s4_ex[ord(ans)]) # Produce la excepción
Envía el código de excepción de un servidor SOCKS5
def SOCKS5_ex(ans):
# Relación código/respuesta
s5_ex = { 1 : "General SOCKS server failure",
2 : "Connection not allowed by ruleset",
3 : "Network unreachable",
4 : "Host unreachable",
5 : "Connection refused",
6 : "TTL expired",
7 : "Command not supported",
8 : "Address type not supported" }
raise Exception(s5_ex[ord(ans)]) # Produce la excepción
Especifica la dirección a un proxy SOCKS
def SOCKS_hop(sock, addr, proto = 4, ipv = 4):
if (proto == 4): # Protocolo SOCKS4
# Mensaje de conexión
sock.send(chr(4) + chr(1) + uint16str(addr[1]) + str2host(addr) +\
chr(0))
# Mensaje de confirmación/error
code = sock_read(sock, 8)[1]
# Si algo falló
if (code != chr(90)):
# Mandar una excepción
SOCKS4_ex(code)
elif (proto == 5): # Protocolo SOCKS5
atype = None
if (ipv == 4): # Hosts ipv4
atype = chr(1)
elif (ipv == 6): # Hosts ipv6
atype = chr(4)
else:
raise Exception("Unknown IP version")
sock.send(chr(5) + chr(1) + chr(0))
# Mensaje de confirmación/error
code = sock_read(sock, 2)
if (code != (chr(5) + chr(0))):
raise Exception("Requiere autenticación")
# Mensaje de conexión
l = sock.send(chr(5) + chr(1) + chr(0) + atype + str(str2host(addr)) +\
uint16str(addr[1]))
# Mensaje de confirmación/error
code = sock_read(sock, l)[1]
# Si algo falló
if (code != chr(00)):
# Mandar una excepción
SOCKS5_ex(code)
else: # Protocolo desconocido
raise Exception("Unknown SOCKS version")
Simplemente hay que levantar una conexión hacia un proxy y luego llamar a
SOCKS_hopcon los siguientes argumentos:
* El socket que se uso para la conexión
* Una tupla dirección/puerto como al conectar un socket
* Versión de SOCKS: 4 o 5(Opcional, por defecto 4)
* Versión del protocolo IP (Opcional, por defecto 4)
*
Si se produce un error en el proceso (no está conectado a un servidor SOCKS o le servidor no se pudo conectar al nuevo host, por ejemplo) manda una excepción.
Para este ejemplo, si se pasa algún parámetro, se leerá como una lista de proxies (ip:puerto en cada línea) y va saltando entre proxies al azar. Sino se le pasa ningún parámetro se conecta a localhost en el puerto 1080 y intenta usarlo como servidor SOCKS5 para ver cuantas veces se puede conectar a si mismo [Cuidado]: unos cuantos hilos haciendo esto provocaron algo parecido a un DOS en un pequeño servidor SSH funcionando como proxy =P if name == "main":
if (len(sys.argv) > 1):
plist = []
f = open(sys.argv[1], "rt")
while True:
txt = f.readline()
if (len(txt) < 1):
break
addr = txt.split(":")
plist.append((addr[0], int(addr[1])))
f.close()
last = choice(plist) # Selecciona un proxy al azar
next = last
i = 0
sys.stdout.write("[" + str(i) + "] " + str(next))
sys.stdout.flush()
sock = socket(AF_INET, SOCK_STREAM)
sock.connect(next)
sys.stdout.write("\n")
while True:
i += 1
while (next == last):
next = choice(plist)
last = next
sys.stdout.write("[" + str(i) + "] " + str(next))
sys.stdout.flush()
SOCKS_hop(sock, next)
sys.stdout.write("\n")
else:
sock = socket(AF_INET, SOCK_STREAM)
#sock.connect(('127.0.0.1', 4444))
sock.connect(('localhost', 1080))
i = 0
while True:
#SOCKS_hop(sock, ('localhost', 4444))
SOCKS_hop(sock, ('127.0.0.1', 1080), 5)
i += 1
print i
El código completo [pysocks.py] [Referencias] SOCKS_4 SOCKS_4A RFC1928_-_SOCKS5