Skip to content
This repository has been archived by the owner on Sep 9, 2021. It is now read-only.

Shelepov Vladislav. \203 lab2s #27

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 170 additions & 0 deletions lab2s/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import socket
import struct
from random import randint
from uuid import getnode as get_mac
from time import sleep

UDP_PORT_OUT = 67
UDP_PORT_IN = 68
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.bind(('', UDP_PORT_IN))


def get_mac_address():
mac = ':'.join(("%012X" % get_mac())[i:i + 2] for i in range(0, 12, 2)) # create string with : separated mac values
byte_mac = b''
for element in mac.split(":"):
byte_mac += struct.pack('!B', int(element,
16)) # create byte package with mac address bytes.fromhex(hex(get_mac())[2:])
print(byte_mac)
return byte_mac


def generate_transaction_id():
id = b''
for i in range(4):
id += struct.pack('!B', randint(0, 255))
return id


class Client:
def __init__(self):
self.operation = "Unknown"
self.requests = {'SubnetMask': False, 'DomainName': False, 'Router': False, 'DNS': False, 'StaticRoute': False}
self.options = {}
self.data = 0
self.end_byte = b'\xff'
self.transaction_id = generate_transaction_id()
self.magic_cookie = b'\x63\x82\x53\x63'
self.subnetMask = b''
self.offerIP = 0
self.nextServerIP = 0
self.router = 0
self.DNS = 0
self.leaseTime = 0
self.DHCPServerIdentifier = 0

def set_data(self, data):

self.data = data
self.options_analyzer()
self.options_handler()
if self.operation == "DHCPOFFER":
self.unpack()
pack = self.build_request_pack()
print(f"Sending REQUEST responding to {self.operation} in broadcast")
sock.sendto(pack, ("255.255.255.255", UDP_PORT_OUT))
elif self.operation == "DHCPACK":
print(f"Got DHCPACK, now {self.offerIP} is mine for {self.leaseTime} second(s)")

def build_discover_pack(self):
mac_address = get_mac_address()
package = b''
package += b'\x01' # Message type: Boot Request (1)
package += b'\x01' # Hardware type: Ethernet
package += b'\x06' # Hardware address length: 6
package += b'\x00' # Hops: 0
package += self.transaction_id # Transaction ID
package += b'\x00\x00' # Seconds elapsed: 0
package += b'\x80\x00' # Bootp flags: 0x8000 (Broadcast) + reserved flags
package += b'\x00\x00\x00\x00' # Client IP address: 0.0.0.0
package += b'\x00\x00\x00\x00' # Your (client) IP address: 0.0.0.0
package += b'\x00\x00\x00\x00' # Next server IP address: 0.0.0.0
package += b'\x00\x00\x00\x00' # Relay agent IP address: 0.0.0.0
package += mac_address # may be 0 here
package += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # Client hardware address padding: 00000000000000000000
package += b'\x00' * 64 # Server host name not given
package += b'\x00' * 128 # Boot file name not given

package += b'\x63\x82\x53\x63' # Magic cookie: DHCP
package += b'\x35\x01\x01' # Option: (t=53,l=1) DHCP Message Type = DHCP Discover
package += b'\x3d\x06' + mac_address # Option (t=61 , l=6) Client identifier
package += b'\x37\x03\x03\x01\x06' # Option: (t=55,l=3) Parameter Request List
package += b'\xff' # End Option
return package

def build_request_pack(self):
mac_address = get_mac_address()
package = b''
package += b'\x01' # Message type: Boot Request (1)
package += b'\x01' # Hardware type: Ethernet
package += b'\x06' # Hardware address length: 6
package += b'\x00' # Hops: 0
package += self.transaction_id # Transaction ID
package += b'\x00\x00' # Seconds elapsed: 0
package += b'\x80\x00' # Bootp flags: 0x8000 (Broadcast) + reserved flags
package += b'\x00\x00\x00\x00' # Client IP address: 0.0.0.0
package += socket.inet_pton(socket.AF_INET, self.offerIP) # Your (client) IP address: 0.0.0.0
# package += b'\x00\x00\x00\x00' # Your (client) IP address: 0.0.0.0
package += b'\x00\x00\x00\x00' # Next server IP address: 0.0.0.0
package += b'\x00\x00\x00\x00' # Relay agent IP address: 0.0.0.0
package += mac_address # may be 0 here
package += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # Client hardware address padding: 00000000000000000000
package += b'\x00' * 64 # Server host name not given
package += b'\x00' * 128 # Boot file name not given

package += b'\x63\x82\x53\x63' # Magic cookie: DHCP
package += b'\x35\x01\x03' # Option: (t=53,l=1) DHCP Message Type = DHCP Request
package += b'\x3d\x06' + mac_address # Option (t=61 , l=6) Client identifier
package += b'\x32\x04' + socket.inet_pton(socket.AF_INET,
self.offerIP) # Option: (t=50,l=1) Requested IP Address
package += b'\x36\x04' + socket.inet_pton(socket.AF_INET,
self.DHCPServerIdentifier) # Option: (t=54,l=1) DHCP server identifer
package += b'\x0c\x0f' + bytes(socket.gethostname(), 'utf-8') # Option: (t=12,l=15) Host Name
package += b'\x37\x03\x03\x01\x06' # Option: (t=55,l=3) Parameter Request List
package += b'\xff' # End Option
return package

def options_analyzer(self):
pointer = 240
if self.magic_cookie == b'\x63\x82\x53\x63':
while self.data[pointer] != struct.unpack('!B', self.end_byte)[0]: # returns tuple
self.options[f"{self.data[pointer]}"] = list(
self.data[i] for i in range(pointer + 2, pointer + 2 + self.data[pointer + 1]))
pointer = pointer + 1 + self.data[pointer + 1] + 1
print(f"WE GET SUCH OPTIONS {self.options}")

def unpack(self):
if self.data[4:8] == self.transaction_id:
self.offerIP = '.'.join(map(lambda x: str(x), self.data[16:20]))
self.nextServerIP = '.'.join(map(lambda x: str(x), self.data[20:24]))

def options_handler(self):
for key, value in self.options.items():
# print(f" Key is {key} VALUES is {value}")
if int(key) == 1:
self.subnetMask = '.'.join(map(lambda x: str(x), value))
if int(key) == 3:
self.router = '.'.join(map(lambda x: str(x), value))
if int(key) == 6:
self.DNS = '.'.join(map(lambda x: str(x), value))
if int(key) == 51:
self.leaseTime = 0
for i, elem in enumerate(value):
self.leaseTime += 255 ** (3 - i) * elem
if int(key) == 53:
if value[0] == 1:
self.operation = "DHCPDISCOVER"
if value[0] == 2:
self.operation = "DHCPOFFER"
if value[0] == 3:
self.operation = "DHCPREQUEST"
if value[0] == 4:
self.operation = "DHCPDECLINE"
if value[0] == 5:
self.operation = "DHCPACK"
if int(key) == 54:
self.DHCPServerIdentifier = '.'.join(
map(lambda x: str(x), value)) # need to take it from options


if __name__ == '__main__':
first_client = Client()
sock.sendto(first_client.build_discover_pack(), ('255.255.255.255', UDP_PORT_OUT))
print(f"Sending DHCPDISCOVER in broadcast")
while True:
recv_data, addr = sock.recvfrom(1024)
print(f"Got the packet")
print(recv_data[236:240])
first_client.set_data(recv_data)
214 changes: 214 additions & 0 deletions lab2s/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import copy
import socket
import struct
from random import randint
from time import sleep

ip_range = ["255.255.125.13", "255.255.255.255"]
used_ip = []
UDP_PORT = 67
UDP_PORT_CLIENT = 68
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.bind(('', UDP_PORT))

dict1 = {'magic_cookie': 'None',
'transaction_id': 'None',
'requests': {'SubnetMask': False, 'DomainName': False, 'Router': False, 'DNS': False,
'StaticRoute': False},
'boot_req': 'None',
'hw_type': 'None',
'hw_addr_len': 'None',
'hops': 'None',
'sec_elapsed': 'None',
'boot_flags': 'None',
'client_ip': 'None',
'offered_ip': 'None',
'next_server_ip': 'None',
'relay_agent_ip': 'None',
'padding': 'None',
'server_host_name': 'None',
'boot_file_name': 'None',
}


def range_generation():
ans = ""
first = ip_range[0].split(".")
second = ip_range[1].split(".")
for i in range(len(first)):
ans += str(randint(int(first[i]), int(second[i])))
if i != len(first) - 1:
ans += "."
if ans in used_ip:
range_generation()
else:
return ans


class Server:
def __init__(self):
self.all_clients = {} # {'client_mac': [options]}
self.options = {}
self.operation = "Unknown"
self.requests = {'SubnetMask': False, 'DomainName': False, 'Router': False, 'DNS': False,
'StaticRoute': False}
self.end_byte = b'\xff'
self.mac_address = b'\xd8\xcb\x8a\xee~\xa1' # may be change on random mac
self.magic_cookie = b''

def set_data(self, data):
self.magic_cookie = data[236:240]
print(f"MAGIC COOKIE IS {self.magic_cookie}")
self.options_analyzer(data)
self.options_handler()
pack = self.data_setter(data)
print(f"Responding to {self.operation} in broadcast")
sock.sendto(pack, ("255.255.255.255", UDP_PORT_CLIENT))

def options_analyzer(self, data):
pointer = 240
if data[236:240] == b'\x63\x82\x53\x63':
while data[pointer] != struct.unpack('!B', self.end_byte)[0]: # returns tuple
self.options[f"{data[pointer]}"] = list(
data[i] for i in range(pointer + 2, pointer + 2 + data[pointer + 1]))
pointer = pointer + 1 + data[pointer + 1] + 1
print(f"WE GET SUCH OPTIONS {self.options}")

def options_handler(self):
for key, value in self.options.items():
# print(f" Key is {key} VALUES is {value}")
if int(key) == 53:
if value[0] == 1:
self.operation = "DHCPDISCOVER"
if value[0] == 2:
self.operation = "DHCPOFFER"
if value[0] == 3:
self.operation = "DHCPREQUEST"
if value[0] == 4:
self.operation = "DHCPDECLINE"
if value[0] == 5:
self.operation = "DHCPACK"

if int(key) == 55:
for elem in value:
if elem == 1:
self.requests['SubnetMask'] = True
if elem == 15:
self.requests['DomainName'] = True
if elem == 3:
self.requests['Router'] = True
if elem == 6:
self.requests['DNS'] = True
if elem == 33:
self.requests['StaticRoute'] = True
if int(key) == 61:
self.mac_address = b''
for elem in value:
self.mac_address += struct.pack('!B', elem)
# self.mac_address = '.'.join(map(lambda x: str(x), value)) # set usual mac_address

def data_setter(self, data):
if self.mac_address not in self.all_clients:
self.all_clients[self.mac_address] = copy.deepcopy(dict1)
if self.operation == "DHCPDISCOVER" or self.operation == "DHCPREQUEST":
self.all_clients[self.mac_address]['magic_cookie'] = data[236:240]
self.all_clients[self.mac_address]['transaction_id'] = data[4:8]
self.all_clients[self.mac_address]['requests'] = self.requests
self.all_clients[self.mac_address]['boot_req'] = data[0]
self.all_clients[self.mac_address]['hw_type'] = data[1]
self.all_clients[self.mac_address]['hw_addr_len'] = data[2]
self.all_clients[self.mac_address]['hops'] = data[3]
self.all_clients[self.mac_address]['sec_elapsed'] = data[8:10]
self.all_clients[self.mac_address]['boot_flags'] = data[10:12]
self.all_clients[self.mac_address]['client_ip'] = data[12:16]
self.all_clients[self.mac_address]['offered_ip'] = data[16:20]
self.all_clients[self.mac_address]['next_server_ip'] = data[20:24]
self.all_clients[self.mac_address]['relay_agent_ip'] = data[24:28]
self.all_clients[self.mac_address]['padding'] = data[34:44] # Client hardware address padding
self.all_clients[self.mac_address]['server_host_name'] = data[44:108]
self.all_clients[self.mac_address]['boot_file_name'] = data[108:236]
if self.operation == "DHCPDISCOVER":
return self.create_dhcp_offer()
if self.operation == "DHCPREQUEST":
return self.create_dhcp_pack()

def create_dhcp_offer(self):
package = b''
package += b'\x02' # Message type: OFFER
package += b'\x01' # Hardware type: Ethernet
package += b'\x06' # Hardware address length: 6
package += b'\x00' # Hops: 0
package += self.all_clients[self.mac_address]['transaction_id'] # Transaction ID
print(len(package)) #8
package += b'\x00\x00' # Seconds elapsed: 0
package += b'\x80\x00' # Bootp flags: 0x8000 (Broadcast) + reserved flags
package += b'\x00\x00\x00\x00' # Client IP address: 0.0.0.0 (sending to)
print(len(package)) #16
package += socket.inet_pton(socket.AF_INET,
range_generation()) # Your (client) IP address: 0.0.0.0 (offered IP)
package += socket.inet_pton(socket.AF_INET, "192.168.0.71") # Next server IP address: 0.0.0.0 (our IP)
package += socket.inet_pton(socket.AF_INET, "0.0.0.0") # Relay agent IP address: 0.0.0.0
print(len(package)) #28
package += self.mac_address
print(len(package)) #34
package += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # Client hardware address padding: 00000000000000000000
print(len(package)) # 46
package += b'\x00' * 64 # Server host name not given
package += b'\x00' * 128 # Boot file name not given
package += b'c\x82Sc' # Magic cookie: DHCP
print(len(package))
package += b'\x35\x01\x02' # Option: (t=53,l=1) DHCP Message Type = DHCP Offer
package += b'\x36\x04' + socket.inet_pton(socket.AF_INET,
"192.168.0.71") # Option (t=54 , l=6) Client identifier
package += b'\x33\x04\x00\x00\x01\xff' # # Option: (t=51,l=4) LeaseTime The time is in units of seconds
package += b'\x01\x04' + socket.inet_pton(socket.AF_INET, "255.255.255.0") # Option: (t=1,l=4) SubNetMask
package += b'\x03\x04' + socket.inet_pton(socket.AF_INET, "192.168.0.1") # Option: (t=3,l=4) Router
package += b'\x06\x04' + socket.inet_pton(socket.AF_INET, "8.8.8.8") # Option: (t=6,l=4) DNS
package += b'\xff' # End Option
return package

def create_dhcp_pack(self):
# answer on dhcp_request
package = b''
package += b'\x02' # Message type: OFFER
package += b'\x01' # Hardware type: Ethernet
package += b'\x06' # Hardware address length: 6
package += b'\x00' # Hops: 0
package += self.all_clients[self.mac_address]['transaction_id'] # Transaction ID
package += b'\x00\x00' # Seconds elapsed: 0
package += b'\x80\x00' # Bootp flags: 0x8000 (Broadcast) + reserved flags
package += b'\x00\x00\x00\x00' # Client IP address: 0.0.0.0 (sending to)

string_ip = '.'.join(map(lambda x: str(x), self.all_clients[self.mac_address]['offered_ip']))
print(f"Sending {string_ip} to client")
package += socket.inet_pton(socket.AF_INET, string_ip) # Your (client) IP address: 0.0.0.0 (offered IP)
used_ip.append(string_ip)
package += socket.inet_pton(socket.AF_INET, "192.168.0.71") # Next server IP address: 0.0.0.0 (our IP)
package += socket.inet_pton(socket.AF_INET, "0.0.0.0") # Relay agent IP address: 0.0.0.0
package += self.mac_address
package += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # Client hardware address padding: 00000000000000000000
package += b'\x00' * 64 # Server host name not given
package += b'\x00' * 128 # Boot file name not given

package += b'c\x82Sc' # Magic cookie: DHCP
print(len(package))
package += b'\x35\x01\x05' # Option: (t=53,l=1) DHCP Message Type = DHCP Pack
package += b'\x36\x04' + socket.inet_pton(socket.AF_INET,
"192.168.0.71") # Option (t=54 , l=6) Client identifier
package += b'\x33\x04\x00\x00\x01\xff' # # Option: (t=51,l=4) LeaseTime The time is in units of seconds
package += b'\x01\x04' + socket.inet_pton(socket.AF_INET, "255.255.255.0") # Option: (t=1,l=4) SubNetMask
package += b'\x03\x04' + socket.inet_pton(socket.AF_INET, "192.168.0.1") # Option: (t=3,l=4) Router
package += b'\x06\x04' + socket.inet_pton(socket.AF_INET, "8.8.8.8") # Option: (t=6,l=4) DNS
package += b'\xff' # End Option

return package


server_one = Server()

while True:
recv_data, addr = sock.recvfrom(1024)
print(f"Got new UDP packet")
# print(recv_data)
server_one.set_data(recv_data) #