65
65
DHCP_HLENETHERNET = const (0x06 )
66
66
DHCP_HOPS = const (0x00 )
67
67
68
- MAGIC_COOKIE = const ( 0x63825363 )
68
+ MAGIC_COOKIE = b"c \x82 Sc" # Four bytes 99.130.83.99
69
69
MAX_DHCP_OPT = const (0x10 )
70
70
71
71
# Default DHCP Server port
@@ -179,7 +179,7 @@ def send_dhcp_message(
179
179
# Transaction ID (xid)
180
180
self ._initial_xid = htonl (self ._transaction_id )
181
181
self ._initial_xid = self ._initial_xid .to_bytes (4 , "big" )
182
- _BUFF [4 :7 ] = self ._initial_xid
182
+ _BUFF [4 :8 ] = self ._initial_xid
183
183
184
184
# seconds elapsed
185
185
_BUFF [8 ] = (int (time_elapsed ) & 0xFF00 ) >> 8
@@ -195,18 +195,15 @@ def send_dhcp_message(
195
195
# as they're already set to 0.0.0.0
196
196
# Except when renewing, then fill in ciaddr
197
197
if renew :
198
- _BUFF [12 :15 ] = bytes (self .local_ip )
198
+ _BUFF [12 :16 ] = bytes (self .local_ip )
199
199
200
200
# chaddr
201
201
_BUFF [28 :34 ] = self ._mac_address
202
202
203
203
# NOTE: 192 octets of 0's, BOOTP legacy
204
204
205
205
# Magic Cookie
206
- _BUFF [236 ] = (MAGIC_COOKIE >> 24 ) & 0xFF
207
- _BUFF [237 ] = (MAGIC_COOKIE >> 16 ) & 0xFF
208
- _BUFF [238 ] = (MAGIC_COOKIE >> 8 ) & 0xFF
209
- _BUFF [239 ] = MAGIC_COOKIE & 0xFF
206
+ _BUFF [236 :240 ] = MAGIC_COOKIE
210
207
211
208
# Option - DHCP Message Type
212
209
_BUFF [240 ] = 53
@@ -262,10 +259,10 @@ def send_dhcp_message(
262
259
# pylint: disable=too-many-branches, too-many-statements
263
260
def parse_dhcp_response (
264
261
self ,
265
- ) -> Union [ Tuple [int , bytes ], Tuple [ int , int ] ]:
262
+ ) -> Tuple [int , bytearray ]:
266
263
"""Parse DHCP response from DHCP server.
267
264
268
- :return Union[ Tuple[int, bytes], Tuple[int, int]] : DHCP packet type.
265
+ :return Tuple[int, bytearray] : DHCP packet type and ID .
269
266
"""
270
267
# store packet in buffer
271
268
_BUFF = self ._sock .recv ()
@@ -281,16 +278,16 @@ def parse_dhcp_response(
281
278
)
282
279
283
280
xid = _BUFF [4 :8 ]
284
- if bytes (xid ) < self ._initial_xid :
285
- print ("f" )
286
- return 0 , 0
281
+ if bytes (xid ) != self ._initial_xid :
282
+ raise ValueError ("DHCP response ID mismatch." )
287
283
288
284
self .local_ip = tuple (_BUFF [16 :20 ])
289
- if _BUFF [28 :34 ] == 0 :
290
- return 0 , 0
285
+ # Check that there is a server ID.
286
+ if _BUFF [28 :34 ] == b"\x00 \x00 \x00 \x00 \x00 \x00 " :
287
+ raise ValueError ("No DHCP server ID in the response." )
291
288
292
- if int . from_bytes ( _BUFF [235 :240 ], "big" ) != MAGIC_COOKIE :
293
- return 0 , 0
289
+ if _BUFF [236 :240 ] != MAGIC_COOKIE :
290
+ raise ValueError ( "No DHCP Magic Cookie in the response." )
294
291
295
292
# -- Parse Packet, VARIABLE -- #
296
293
ptr = 240
@@ -323,8 +320,8 @@ def parse_dhcp_response(
323
320
ptr += 1
324
321
opt_len = _BUFF [ptr ]
325
322
ptr += 1
326
- self .gateway_ip = tuple (_BUFF [ptr : ptr + opt_len ])
327
- ptr += opt_len
323
+ self .gateway_ip = tuple (_BUFF [ptr : ptr + 4 ])
324
+ ptr += opt_len # still increment even though we only read 1 addr.
328
325
elif _BUFF [ptr ] == DNS_SERVERS :
329
326
ptr += 1
330
327
opt_len = _BUFF [ptr ]
@@ -427,65 +424,79 @@ def _dhcp_state_machine(self) -> None:
427
424
if self ._sock .available ():
428
425
if self ._debug :
429
426
print ("* DHCP: Parsing OFFER" )
430
- msg_type , xid = self .parse_dhcp_response ()
431
- if msg_type == DHCP_OFFER :
432
- # Check if transaction ID matches, otherwise it may be an offer
433
- # for another device
434
- if htonl (self ._transaction_id ) == int .from_bytes (xid , "big" ):
435
- if self ._debug :
436
- print (
437
- "* DHCP: Send request to {}" .format (self .dhcp_server_ip )
427
+ try :
428
+ msg_type , xid = self .parse_dhcp_response ()
429
+ except ValueError as error :
430
+ if self ._debug :
431
+ print (error )
432
+ else :
433
+ if msg_type == DHCP_OFFER :
434
+ # Check if transaction ID matches, otherwise it may be an offer
435
+ # for another device
436
+ if htonl (self ._transaction_id ) == int .from_bytes (xid , "big" ):
437
+ if self ._debug :
438
+ print (
439
+ "* DHCP: Send request to {}" .format (
440
+ self .dhcp_server_ip
441
+ )
442
+ )
443
+ self ._transaction_id = (
444
+ self ._transaction_id + 1
445
+ ) & 0x7FFFFFFF
446
+ self .send_dhcp_message (
447
+ DHCP_REQUEST , (time .monotonic () - self ._start_time )
438
448
)
439
- self ._transaction_id = (self ._transaction_id + 1 ) & 0x7FFFFFFF
440
- self .send_dhcp_message (
441
- DHCP_REQUEST , (time .monotonic () - self ._start_time )
442
- )
443
- self ._dhcp_state = STATE_DHCP_REQUEST
449
+ self ._dhcp_state = STATE_DHCP_REQUEST
450
+ else :
451
+ if self ._debug :
452
+ print ("* DHCP: Received OFFER with non-matching xid" )
444
453
else :
445
454
if self ._debug :
446
- print ("* DHCP: Received OFFER with non-matching xid" )
447
- else :
448
- if self ._debug :
449
- print ("* DHCP: Received DHCP Message is not OFFER" )
455
+ print ("* DHCP: Received DHCP Message is not OFFER" )
450
456
451
457
elif self ._dhcp_state == STATE_DHCP_REQUEST :
452
458
if self ._sock .available ():
453
459
if self ._debug :
454
460
print ("* DHCP: Parsing ACK" )
455
- msg_type , xid = self .parse_dhcp_response ()
456
- # Check if transaction ID matches, otherwise it may be
457
- # for another device
458
- if htonl (self ._transaction_id ) == int .from_bytes (xid , "big" ):
459
- if msg_type == DHCP_ACK :
460
- if self ._debug :
461
- print ("* DHCP: Successful lease" )
462
- self ._sock .close ()
463
- self ._sock = None
464
- self ._dhcp_state = STATE_DHCP_LEASED
465
- self ._last_lease_time = self ._start_time
466
- if self ._lease_time == 0 :
467
- self ._lease_time = DEFAULT_LEASE_TIME
468
- if self ._t1 == 0 :
469
- # T1 is 50% of _lease_time
470
- self ._t1 = self ._lease_time >> 1
471
- if self ._t2 == 0 :
472
- # T2 is 87.5% of _lease_time
473
- self ._t2 = self ._lease_time - (self ._lease_time >> 3 )
474
- self ._renew_in_sec = self ._t1
475
- self ._rebind_in_sec = self ._t2
476
- self ._eth .ifconfig = (
477
- self .local_ip ,
478
- self .subnet_mask ,
479
- self .gateway_ip ,
480
- self .dns_server_ip ,
481
- )
482
- gc .collect ()
461
+ try :
462
+ msg_type , xid = self .parse_dhcp_response ()
463
+ except ValueError as error :
464
+ if self ._debug :
465
+ print (error )
466
+ else :
467
+ # Check if transaction ID matches, otherwise it may be
468
+ # for another device
469
+ if htonl (self ._transaction_id ) == int .from_bytes (xid , "big" ):
470
+ if msg_type == DHCP_ACK :
471
+ if self ._debug :
472
+ print ("* DHCP: Successful lease" )
473
+ self ._sock .close ()
474
+ self ._sock = None
475
+ self ._dhcp_state = STATE_DHCP_LEASED
476
+ self ._last_lease_time = self ._start_time
477
+ if self ._lease_time == 0 :
478
+ self ._lease_time = DEFAULT_LEASE_TIME
479
+ if self ._t1 == 0 :
480
+ # T1 is 50% of _lease_time
481
+ self ._t1 = self ._lease_time >> 1
482
+ if self ._t2 == 0 :
483
+ # T2 is 87.5% of _lease_time
484
+ self ._t2 = self ._lease_time - (self ._lease_time >> 3 )
485
+ self ._renew_in_sec = self ._t1
486
+ self ._rebind_in_sec = self ._t2
487
+ self ._eth .ifconfig = (
488
+ self .local_ip ,
489
+ self .subnet_mask ,
490
+ self .gateway_ip ,
491
+ self .dns_server_ip ,
492
+ )
493
+ gc .collect ()
494
+ else :
495
+ if self ._debug :
496
+ print ("* DHCP: Received DHCP Message is not ACK" )
483
497
else :
484
498
if self ._debug :
485
- print ("* DHCP: Received DHCP Message is not ACK" )
486
- else :
487
- if self ._debug :
488
- print ("* DHCP: Received non-matching xid" )
499
+ print ("* DHCP: Received non-matching xid" )
489
500
490
501
elif self ._dhcp_state == STATE_DHCP_WAIT :
491
502
if time .monotonic () > (self ._start_time + DHCP_WAIT_TIME ):
0 commit comments