1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
#!/usr/bin/python3
import struct
import eventlet
from eventlet.green import socket
from eventlet import wsgi
def simpleDHCP(s_serverip, s_clientip, s_netmask, s_binddev):
print("DHCP Start dumb server.")
dhcpOp = [None, \
'DISCOVER','OFFER','REQUEST','DECLINE','ACK','NACK','RELEASE','INFORM']
serverip = socket.inet_aton(s_serverip)
clientip = socket.inet_aton(s_clientip)
netmask = socket.inet_aton(s_netmask)
routerip = dnsip = serverip
iptime = struct.pack("!I", 3600) # Lease time 1 hour
binddev = s_binddev.encode()
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, binddev+b'\0')
sock.bind(('', 67))
frame_fmt = "!4BI2H4s4s4s4s16s64s128sI"
fbase_len = struct.calcsize(frame_fmt)
fmin_len = 300 # Min BOOTP Packet size
fmax_len = 534 # Max BOOTP Packet size
roptions = None # Response options
nullip = socket.inet_aton('0.0.0.0')
while True:
try:
# Receive and decode BOOTP base frame (without options)
nf, addr = sock.recvfrom(fmax_len)
if(len(nf) < fbase_len): continue # Not BOOTP
op, htype, hlen, hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr, \
chaddr, sname, bfile, magic = struct.unpack_from(frame_fmt, nf)
if(magic != 0x63825363): continue # Not BOOTP
if(op != 1): continue # Not a BOOTP REQUEST
s_chaddr="%02X:%02X:%02X:%02X:%02X:%02X"%struct.unpack("6B",chaddr[0:hlen])
# Decode options
i = fbase_len
while(i < len(nf)):
# BOOTP: Option type
ot = nf[i]
# BOOTP: No more option
if(ot == 0xFF): break
# BOOTP: Option length
olen = nf[i+1]
# BOOTP: Option data
odta = nf[i+2:i+2+olen]
# Seek to next option
i += 2+olen
# BOOTP: DHCP Request IP
if (ot == 0x32 and olen == 4):
if(odta != clientip):
roptions = b'\x36\x04'+serverip # Server ID (IP)
roptions += b'\x35\x01\x06' # DHCP: NACK
print(" \-> Wrong client IP NACK")
break
# BOOTP: DHCP Msg option
elif(ot == 0x35 and olen == 1):
# Print DHCP Operation
if(odta[0] < len(dhcpOp)):
print("DHCP",dhcpOp[odta[0]],"xid:",hex(xid),"from:",s_chaddr)
# DHCP: DISCOVER
if (odta[0] == 1):
roptions = b'\x35\x01\x02' # DHCP: OFFER
roptions += b'\x36\x04'+serverip # Server ID (IP)
print(" \-> OFFER")
# DHCP: REQUEST
elif(odta[0] == 3):
if(ciaddr == clientip or ciaddr == nullip):
roptions = b'\x01\x04'+netmask # Subnet mask
roptions += b'\x03\x04'+routerip # Router IP
roptions += b'\x06\x04'+dnsip # DNS Server IP
roptions += b'\x33\x04'+iptime # IP Lease time
roptions += b'\x35\x01\x05' # DHCP: ACK
print(" \-> ACK")
else:
roptions = b'\x35\x01\x06' # DHCP: NACK
print(" \-> NACK")
# BOOTP: DHCP Server ID
elif(ot == 0x36 and olen == 4):
if(odta != serverip):
roptions = b'\x36\x04'+serverip # Server ID (IP)
roptions += b'\x35\x01\x06' # DHCP: NACK
print(" \-> Wrong server IP NACK")
break
# Send answer if needed
if(not roptions): continue # Nothings to answer
op = 2 # BOOTP REPLY
yiaddr = clientip
siaddr = serverip
nf = struct.pack(frame_fmt, op, htype, hlen, hops, xid, secs, flags, \
ciaddr, yiaddr, siaddr, giaddr, chaddr, sname, bfile, magic)
nf += roptions # Add response options
nf += b'\xFF' # End of options
nf += bytes(fmin_len-len(nf)) # Padding
if(ciaddr == nullip): sock.sendto(nf, ('255.255.255.255', 68))
else: sock.sendto(nf, (addr[0], 68))
# Reset variable
roptions = None # Response options
except Exception as e:
print("DHCP Error:", repr(e))
def simpleDNS(s_bindip, s_redirectip):
print("DNS Start dumb server.")
redirectip = socket.inet_aton(s_redirectip)
ttl = 300 # 5 min
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((s_bindip, 53))
frame_fmt = "!6H"
fbase_len = struct.calcsize(frame_fmt)
fmax_len = 1458 # Max DNS Packet size
while True:
try:
# Receive and decode DNS base frame (without queries)
nf, addr = sock.recvfrom(fmax_len)
if(len(nf) < fbase_len): continue # Not DNS
did, flags, qcount, ancount, nscount, arcount = \
struct.unpack_from(frame_fmt, nf)
# Ignore non-standard query
if(flags & 0xF800 != 0): continue
# Answer only if there is not already an answer
if(ancount>0 or nscount>0 or arcount>0): continue
# Decode queries
answers = bytes()
i = fbase_len
iqcount = 0
while(i < len(nf) and iqcount < qcount):
names = []
pointer = i
while(nf[i] != 0):
names.append(nf[i+1:i+1+nf[i]].decode())
i += nf[i]+1
i += 1
qtype, qclass = struct.unpack_from("!HH", nf, i)
i += 4
iqcount += 1
print("DNS Query",names,"type:",hex(qtype),"class:",hex(qclass))
# Answer to A IN (IPv4) always with redirectip
if(qtype == 1 and qclass == 1):
answers += struct.pack("!HHHIH4s", 0xC000+pointer, \
qtype, qclass, ttl,len(redirectip), redirectip)
ancount += 1
print(" \-> Redirect")
queries = nf[fbase_len:i]
# Send an answer in all cases
flags = 0x8400 # Answer flag
nf = struct.pack(frame_fmt, did, flags, qcount, ancount, nscount, arcount)
nf += queries # Add queries
nf += answers # Add answers
sock.sendto(nf, addr)
except Exception as e:
print("DNS Error:", repr(e))
def dispatch(env, start_response):
start_response('200 OK', [('content-type', 'text/html')])
return '<html><body><h1>You are mine!</h1><body></html>'
if __name__ == "__main__":
eventlet.spawn_n(simpleDHCP, \
'192.168.254.1', '192.168.254.20', '255.255.255.0', 'eth0')
eventlet.spawn_n(simpleDNS, '192.168.254.1', '192.168.254.1')
listener = eventlet.listen(('', 80))
wsgi.server(listener, dispatch) |
Partager