Python的网络编程[4] -> DHCP 协议[1] -> DHCP 的 Python 实现
DHCP实现 / DHCP Implement
目录
下面介绍建立一个简单的DHCP服务器,主要用于对基本的DHCP请求进行响应,目前只提供一个IP为客户端使用,实现最基本的通信示例。理论内容可参考 DHCP 理论部分。
1 DHCP 服务器建立过程
首先是基本服务器的建立,这个服务器实现了最基本的对DISCOVER和REQUEST报文的响应,在验证时会对魔术字进行验证,此处未对BOOTP进行处理,验证通过后会对Options字段进行验证,此处利用生成器来对字段进行获取,最终处理完所有信息后结束。
import socket
import struct
import binascii
import logging
from threading import Thread
from dhcp_offer import Offer logging.basicConfig(level=logging.DEBUG, format='[%(asctime)s] %(threadName)s: %(message)s') class DHCPServer():
"""
This class implements parts of RFC-2131
Only DHCPDISCOVER and DHCPREQUEST allowed
"""
def __init__(self, boot_file=None, server_ip=None, offer_ip=None, tftp_ip=None):
Thread.__init__(self)
self._port = 67
self._boot_file = boot_file
self._file_index = 0
self._offer_ip = offer_ip
self._tftp_ip = tftp_ip
self.server_ip = server_ip @property
def server_ip(self):
return self._server_ip @server_ip.setter
def server_ip(self, server_ip):
self._server_ip = server_ip
self.send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
self.send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
self.send_socket.bind((self._server_ip, self._port)) @property
def boot_file(self):
return self._boot_file @boot_file.setter
def boot_file(self, boot_file):
if not isinstance(boot_file, list):
boot_file = [boot_file]
self._boot_file = boot_file
self._file_index = 0 @property
def offer_ip(self):
return self._offer_ip @offer_ip.setter
def offer_ip(self, offer_ip):
self._offer_ip = offer_ip def check_msg(self, m):
is_valid, msg_type, select_ip = False, None, None
if (m[0] == b'\x01' and
m[1] == b'\x01' and
m[2] == b'\x06' and
m[3] == b'\x00' and
m[10:12] == [b'\x00', b'\x00'] and
m[12:16] == [b'\x00', b'\x00', b'\x00', b'\x00'] and
m[16:20] == [b'\x00', b'\x00', b'\x00', b'\x00'] and
m[20:24] == [b'\x00', b'\x00', b'\x00', b'\x00'] and
m[236:240] == [b'\x63', b'\x82', b'\x53', b'\x63']
):
logging.warning('Valid DHCP message')
# Valid DHCPDISCOVER
opt = (x for x in m[240:])
while opt:
try:
func_code = next(opt)
if func_code == b'\x00':
break
length = next(opt)
items = b''
for i in range(ord(length)):
items += next(opt)
except StopIteration:
break
else:
if func_code == b'\x35' and length == b'\x01':
if items == b'\x01':
logging.warning('DHCP Discover')
msg_type = 'DSCV'
is_valid = True
if items == b'\x03':
logging.warning('DHCP Request')
msg_type = 'RQST' # Assure DHCP server selected
if func_code == b'\x36' and msg_type == 'RQST':
logging.warning('DHCP Server Identifier check')
select_ip = socket.inet_ntoa(items) # Double check DHCP offer ip
if func_code == b'\x32' and select_ip == self._server_ip:
offer_ip = socket.inet_ntoa(items)
if offer_ip == self._offer_ip:
is_valid = True
else:
logging.warning('Offer ip double check failed') return is_valid, msg_type def serve_forever(self): self.recv_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.recv_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
self.recv_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
self.recv_socket.bind(('', self._port)) if self._boot_file:
if self._file_index >= len(self._boot_file):
self._file_index = 0 def handle_msg(msg, addr):
send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
send_socket.bind((self._server_ip, self._port))
m = list(struct.unpack('c'*len(msg), msg))
is_valid, msg_type = self.check_msg(m)
if is_valid:
logging.warning('Valid %s message, try to response' % msg_type)
pass_sec = ord(m[8]) * 256 + ord(m[9])
transaction_id = ''.join(['%02x' % ord(x) for x in m[4:8]])
client_mac_id = ':'.join(['%02x' % ord(x) for x in m[28:34]])
if msg_type:
offer = Offer(transaction_id=transaction_id,
client_ip_offer=self._offer_ip,
server_ip=self._server_ip,
client_mac_id=client_mac_id,
file_path=self._boot_file,
pass_sec=pass_sec,
msg_type=msg_type,
tftp_ip = self._tftp_ip)
send_socket.sendto(offer.packet, ('<broadcast>', 68))
self._file_index += 1
logging.warning('Respone done')
else:
logging.warning('Invalid message, discard...')
send_socket.close() while True:
logging.warning('Waiting discovery...')
msg, addr = self.recv_socket.recvfrom(8192)
logging.warning('Receive message from %s, port %s' % addr)
handler = Thread(target=handle_msg, args=(msg, addr))
handler.start() if __name__ == '__main__':
dhcp = DHCPServer(server_ip='127.0.0.1', offer_ip='127.0.0.10')
dhcp.serve_forever()
2 DHCP 报文加码实现过程
Note: 此处为做示例,对网关等信息都设置为固定值,匹配服务器ip。
import binascii
import struct
import socket class Offer():
def __init__(self, transaction_id, client_ip_offer, server_ip, client_mac_id, file_path, pass_sec, msg_type, tftp_ip=None, lease_time=172800):
SERVER_NAME = ''
self._server_ip = server_ip
self._offer_ip = client_ip_offer
self._lease_time = lease_time
if not tftp_ip:
tftp_ip = server_ip
self._tftp_ip = tftp_ip
if not file_path:
file_path = ''
pass_sec = struct.pack('!H', pass_sec)
client_mac_id = binascii.unhexlify(client_mac_id.replace(':', ''))
transaction_id = binascii.unhexlify(transaction_id) self.packet = b''
self.packet += b'\x02' # op
self.packet += b'\x01' # htype
self.packet += b'\x06' # hlen
self.packet += b'\x00' # hops
self.packet += transaction_id
self.packet += pass_sec # secs
self.packet += b'\x00\x00' # flags
self.packet += b'\x00\x00\x00\x00' # current client ip
self.packet += socket.inet_aton(client_ip_offer) # offer ip
self.packet += socket.inet_aton(server_ip) # server ip
self.packet += b'\x00\x00\x00\x00' # gateway ip
self.packet += client_mac_id # client mac id
self.packet += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # client mac id padding
self.packet += SERVER_NAME.encode('utf-8')
self.packet += b'\x00'*(64-len(SERVER_NAME))
self.packet += file_path.encode('utf-8')
self.packet += b'\x00'*(128-len(file_path))
self.packet += self.optional(msg_type) def optional(self, msg_type):
magic = b'\x63\x82\x53\x63'
opt = b''
# Message type
if msg_type == 'DSCV':
opt += self.encode_int(53, 1, 2)
if msg_type == 'RQST':
opt += self.encode_int(53, 1, 5)
# Server identifier
opt += self.encode_ip(54, 4, self._server_ip)
# Subnet mask
opt += self.encode_ip(1, 4, '255.255.255.0')
# Router
opt += self.encode_ip(3, 4, '127.0.0.1')
# DNS server
opt += self.encode_ip(6, 4, '127.0.0.1')
# NetBios server
opt += self.encode_ip(44, 4, '127.0.0.1')
# IP address lease time
opt += self.encode_int(51, 4, self._lease_time)
# Extend lease time T1
opt += self.encode_int(58, 4, int(self._lease_time*0.5))
# Extend lease time T2
opt += self.encode_int(59, 4, int(self._lease_time*0.8))
# Log server
opt += self.encode_ip(7, 4, '127.0.0.1')
# TFTP server name
opt += bytes([66, 11]) + self._tftp_ip.encode()
# Tail
# TODO: find out why a b'\xff' for end
opt += b'\xff'
return magic+opt def encode_int(self, func, length, item):
m = {1: '!B', 2: '!H', 4: '!I'}
s = b''
s += (bytes([func, length]) + struct.pack(m[length], item))
return s def encode_ip(self, func, length, item):
s = b''
s += bytes([func, length])
s += socket.inet_aton(item)
return s
相关阅读
1. DHCP 理论
2. 生成器
Python的网络编程[4] -> DHCP 协议[1] -> DHCP 的 Python 实现的更多相关文章
- Python的网络编程[3] -> BOOTP 协议[1] -> BOOTP 的 Python 实现
BOOTP实现 / BOOTP Implement 目录 BOOTP 的服务器建立过程 BOOTP 的客户端建立过程 Note: 理论部分请参考文末相关阅读链接 1 BOOTP 的服务器建立过程 服务 ...
- Python的网络编程[2] -> TFTP 协议[1] -> TFTP 的 Python 实现
TFTP实现 / TFTP Implement 目录 TFTP 的服务器建立过程 TFTP 的客户端建立过程 1 TFTP 的服务器建立过程 服务器建立步骤主要有: (1) 设定服务器IP和 ...
- Python的网络编程[3] -> BOOTP 协议[0] -> BOOTP 的基本理论
BOOTP协议 / BOOTP Protocol 目录 基本理论 BOOTP 与 DHCP 通信流程 数据报文格式 报文加解码实现 1. 基本理论 / Basic Theory BOOTP(Boots ...
- Python的网络编程[6] -> Modbus 协议 -> Modbus 的基本理论与 Python 实现
Modbus协议 / Modbus Protocol 目录 Modbus 协议简介 Modbus RTU协议 Modbus TCP协议与 Python 实现 Modbus 功能码 Modbus TCP ...
- Python的网络编程[1] -> FTP 协议[0] -> FTP 的基本理论
FTP协议 / FTP Protocol FTP全称为File Transfer Protocol(文件传输协议),常用于Internet上控制文件的双向传输,常用的操作有上传和下载.基于TCP/IP ...
- Python的网络编程[2] -> TFTP 协议[0] -> TFTP 的基本理论
TFTP 的基本理论 目录 通信流程 数据报文格式 传输终结 异常处理 数据丢失和超时 TFTP(Trivial File Transfer Protocol,简单文件传输协议)是UDP协议族中的一个 ...
- Python的网络编程[1] -> FTP 协议[2] -> 使用 ftplib 建立 FTP 客户端
使用 ftplib 建立 FTP 客户端 用于建立FTP Client,与 pyftplib 建立的 Server 进行通信. 快速导航 1. 模块信息 2. 建立 FTP 客户端 1. 模块信息 1 ...
- Python的网络编程[1] -> FTP 协议[1] -> 使用 pyftplib 建立 FTP 服务器
使用 pyftplib 建立 FTP 服务器 pyftplib 主要用于建立 FTP Server,与 ftplib 建立的 Client 进行通信. 快速导航 1. 模块信息 2. 建立 FTP 服 ...
- python之网络编程
本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的和具名的) 远程过程调用 ...
- Python高级网络编程系列之第一篇
在上一篇中我们简单的说了一下Python中网络编程的基础知识(相关API就不解释了),其中还有什么细节的知识点没有进行说明,如什么是TCP/IP协议有几种状态,什么是TCP三次握手,什么是TCP四次握 ...
随机推荐
- 【UVA10655】 Contemplation! Algebra
题目 给定 \(p = a + b\) 和 \(q = ab\) 和 \(n\),求 \(a ^ n + b ^ n\). $0\le n\lt 2^{63} $ 分析 大水题. 先考虑 \(n\) ...
- es6实现简单模板编译
现在有各种框架,其中一个主要模块就是关于template.最火的vue.react等框架,在这一块上也是是下足了功夫.我也想写一个自己的模板编译工具,所以就做了个简单的实现,主要是使用es6的反引号编 ...
- appium+python的APP自动化(2)
上节说到安卓上的测试环境都安装好了,这个时候要安装python了 1python的安装 https://www.python.org/15官网下载python2.7(3.0以上也行,个人爱好),安装也 ...
- selenium获取浏览器控制台日志
public void logsTest(){ WebDriver driver = null; try { System.setProperty("webdriver.chrome.dri ...
- python-使用pip安装第三方库报UnicodeDecodeError: 'utf8' codec can't decode byte 0xcb in position 7: invalid continuation byte 错误解决方案
在python 的安装目录下找到Lib\ntpath.py文件,找到def join(path, *paths):方法,添加如下两行语句: reload(sys) sys.setdefaultenco ...
- 一个初学者的辛酸路程-依旧Django
回顾: 1.Django的请求声明周期? 请求过来,先到URL,URL这里写了一大堆路由关系映射,如果匹配成功,执行对应的函数,或者执行类里面对应的方法,FBV和CBV,本质上返回的内容都是字符串 ...
- (原)UE4.20 自定义编辑器 - 基础(一)创建编辑器模块
@author:白袍小道 前言: 本小文参考了UnrealC++,游戏编辑器(应该都找不到了嘿嘿)等书籍. 引擎基于UnrealEngine4.20版本(由于UnrealC++ 用的是 ...
- Mini-MBA记录
最近学完了Mini-MBA的课程,对课程讲述的人力资源,创新,财务,战略,领导力等方面有了更深一些的了解,在此之上也做了一些笔记,如果课程信息披露是被允许的,后续把这些笔记贴出来,作为自己以后的参考.
- Oracle设置用户密码永不过期
1.查看用户的profile是那个,一般是default: select username, profile from dba_users; 2.查看指定概要文件(如default)的密码有效期设置: ...
- Statement [倍增+线段树]
题面 思路 首先,可以确定的是,本题因为每个点只有一条入边,所以整个图肯定是一个基环外向树森林 那么我们首先考虑树上的情况: 我们考虑一个真点,它会对它的子树里面的所有假点产生贡献 一个真点对一个假点 ...