1010- rfc951 - BOOTSTRAP PROTOCOL (BOOTP)
1111- rfc1542 - Clarifications and Extensions for the Bootstrap Protocol
1212- rfc1533 - DHCP Options and BOOTP Vendor Extensions
13+ - rfc3396 - Encoding Long Options in DHCPv4
1314"""
1415
1516try :
@@ -436,8 +437,62 @@ def i2repr(self, pkt, x):
436437 s .append (sane (v ))
437438 return "[%s]" % (" " .join (s ))
438439
440+ def _find_overload (self , x ):
441+ """
442+ Quickly scans the raw options buffer to find the value
443+ of the Option Overload option (code 52).
444+ Returns 0 if the option is not present.
445+ """
446+ while x :
447+ o = orb (x [0 ])
448+ if o == 255 :
449+ break
450+ if o == 0 :
451+ x = x [1 :]
452+ continue
453+ if len (x ) < 2 :
454+ break
455+ olen = orb (x [1 ])
456+ if len (x ) < olen + 2 :
457+ break
458+ if o == 52 and olen == 1 :
459+ return orb (x [2 ])
460+ x = x [olen + 2 :]
461+ return 0
462+
439463 def getfield (self , pkt , s ):
440- return b"" , self .m2i (pkt , s )
464+ """
465+ Retrieve the binary value of the s option field.
466+ RFC 3396: build the aggregated options buffer (options, then file, then sname)
467+ if the overload option is present.
468+ """
469+ aggregate = s
470+ overload = self ._find_overload (s )
471+ if (overload
472+ and pkt .underlayer is not None
473+ and isinstance (pkt .underlayer , BOOTP )):
474+ if overload in (1 , 3 ):
475+ aggregate += pkt .underlayer .file
476+ if overload in (2 , 3 ):
477+ aggregate += pkt .underlayer .sname
478+ return b"" , self .m2i (pkt , aggregate )
479+
480+ def _concat_fragments (self , x , o ):
481+ """
482+ RFC 3396: concatenate consecutive option fragments with the same code o
483+ found at the beginning of buffer x.
484+ Returns (raw_value, remaining_buffer) where:
485+ - raw_value contains the concatenated data from all consumed fragments.
486+ - remaining_buffer is x advanced past those fragments.
487+ """
488+ raw_value = b""
489+ while (x and len (x ) >= 2 and orb (x [0 ]) == o ):
490+ next_olen = orb (x [1 ])
491+ if len (x ) < next_olen + 2 :
492+ break
493+ raw_value += x [2 :next_olen + 2 ]
494+ x = x [next_olen + 2 :]
495+ return raw_value , x
441496
442497 def m2i (self , pkt , x ):
443498 opt = []
@@ -456,40 +511,51 @@ def m2i(self, pkt, x):
456511 break
457512 elif o in DHCPOptions :
458513 f = DHCPOptions [o ]
514+ olen = orb (x [1 ])
515+ x_before = x
516+ raw_value = x [2 :olen + 2 ]
517+ x = x [olen + 2 :]
518+
519+ # RFC 3396: concatenate subsequent fragments
520+ extra , x = self ._concat_fragments (x , o )
521+ raw_value += extra
459522
460523 if isinstance (f , str ):
461- olen = orb (x [1 ])
462- opt .append ((f , x [2 :olen + 2 ]))
463- x = x [olen + 2 :]
524+ opt .append ((f , raw_value ))
464525 else :
465- olen = orb (x [1 ])
466526 lval = [f .name ]
467527
468- if olen == 0 :
528+ if len ( raw_value ) == 0 :
469529 try :
470530 _ , val = f .getfield (pkt , b'' )
471531 except Exception :
472- opt .append (x )
532+ opt .append (x_before )
473533 break
474534 else :
475535 lval .append (val )
476536
477537 try :
478- left = x [ 2 : olen + 2 ]
538+ left = raw_value
479539 while left :
480540 left , val = f .getfield (pkt , left )
481541 lval .append (val )
482542 except Exception :
483- opt .append (x )
543+ opt .append (x_before )
484544 break
485545 else :
486546 otuple = tuple (lval )
487547 opt .append (otuple )
488- x = x [olen + 2 :]
489548 else :
490549 olen = orb (x [1 ])
491- opt .append ((o , x [2 :olen + 2 ]))
550+ x_before = x
551+ raw_value = x [2 :olen + 2 ]
492552 x = x [olen + 2 :]
553+
554+ # RFC 3396: concatenate subsequent fragments
555+ extra , x = self ._concat_fragments (x , o )
556+ raw_value += extra
557+
558+ opt .append ((o , raw_value ))
493559 return opt
494560
495561 def i2m (self , pkt , x ):
@@ -514,8 +580,11 @@ def i2m(self, pkt, x):
514580 warning ("Unknown field option %s" , name )
515581 continue
516582
517- s += struct .pack ("!BB" , onum , len (oval ))
518- s += oval
583+ while oval :
584+ chunk = oval [:255 ]
585+ oval = oval [255 :]
586+ s += struct .pack ("!BB" , onum , len (chunk ))
587+ s += chunk
519588
520589 elif (isinstance (o , str ) and o in DHCPRevOptions and
521590 DHCPRevOptions [o ][1 ] is None ):
0 commit comments