Skip to content

Commit b673424

Browse files
gpotter2mzen17
andauthored
DHCP_am: support multiple nameservers (#4834)
* DHCP_am: support multiple nameservers * Fix dhcpd IPython test --------- Co-authored-by: Mike Zeng <mzen17.jp@gmail.com>
1 parent 6bd8acf commit b673424

File tree

3 files changed

+50
-11
lines changed

3 files changed

+50
-11
lines changed

scapy/layers/dhcp.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@
6161
from scapy.error import warning
6262
from scapy.config import conf
6363

64+
# Typing imports
65+
from typing import (
66+
List,
67+
Optional,
68+
Union,
69+
)
70+
6471
dhcpmagic = b"c\x82Sc"
6572

6673

@@ -601,27 +608,33 @@ class BOOTP_am(AnsweringMachine):
601608
filter = "udp and port 68 and port 67"
602609

603610
def parse_options(self,
604-
pool=Net("192.168.1.128/25"),
605-
network="192.168.1.0/24",
606-
gw="192.168.1.1",
607-
nameserver=None,
608-
domain=None,
609-
renewal_time=60,
610-
lease_time=1800,
611+
pool: Union[Net, List[str]] = Net("192.168.1.128/25"),
612+
network: str = "192.168.1.0/24",
613+
gw: str = "192.168.1.1",
614+
nameserver: Union[str, List[str]] = None,
615+
domain: Optional[str] = None,
616+
renewal_time: int = 60,
617+
lease_time: int = 1800,
611618
**kwargs):
612619
"""
613620
:param pool: the range of addresses to distribute. Can be a Net,
614621
a list of IPs or a string (always gives the same IP).
615622
:param network: the subnet range
616623
:param gw: the gateway IP (can be None)
617-
:param nameserver: the DNS server IP (by default, same than gw)
624+
:param nameserver: the DNS server IP (by default, same than gw).
625+
This can also be a list.
618626
:param domain: the domain to advertise (can be None)
619627
620628
Other DHCP parameters can be passed as kwargs. See DHCPOptions in dhcp.py.
621629
For instance::
622630
623631
dhcpd(pool=Net("10.0.10.0/24"), network="10.0.0.0/8", gw="10.0.10.1",
624632
classless_static_routes=["1.2.3.4/32:9.8.7.6"])
633+
634+
Other example with different options::
635+
636+
dhcpd(pool=Net("10.0.10.0/24"), network="10.0.0.0/8", gw="10.0.10.1",
637+
nameserver=["8.8.8.8", "4.4.4.4"], domain="DOMAIN.LOCAL")
625638
"""
626639
self.domain = domain
627640
netw, msk = (network.split("/") + ["32"])[:2]
@@ -630,7 +643,13 @@ def parse_options(self,
630643
self.network = ltoa(atol(netw) & msk)
631644
self.broadcast = ltoa(atol(self.network) | (0xffffffff & ~msk))
632645
self.gw = gw
633-
self.nameserver = nameserver or gw
646+
if nameserver is None:
647+
self.nameserver = (gw,)
648+
elif isinstance(nameserver, str):
649+
self.nameserver = (nameserver,)
650+
else:
651+
self.nameserver = tuple(nameserver)
652+
634653
if isinstance(pool, str):
635654
pool = Net(pool)
636655
if isinstance(pool, Iterable):
@@ -691,7 +710,7 @@ def make_reply(self, req):
691710
("server_id", self.gw),
692711
("domain", self.domain),
693712
("router", self.gw),
694-
("name_server", self.nameserver),
713+
("name_server", *self.nameserver),
695714
("broadcast_address", self.broadcast),
696715
("subnet_mask", self.netmask),
697716
("renewal_time", self.renewal_time),

test/answering_machines.uts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,23 @@ test_am(BOOTP_am,
3939
def check_DHCP_am_reply(packet):
4040
assert DHCP in packet and len(packet[DHCP].options)
4141
assert ("domain", b"localnet") in packet[DHCP].options
42+
assert ('name_server', '192.168.1.1') in packet[DHCP].options
43+
44+
def check_ns_DHCP_am_reply(packet):
45+
assert DHCP in packet and len(packet[DHCP].options)
46+
assert ("domain", b"localnet") in packet[DHCP].options
47+
assert ('name_server', '1.1.1.1', '2.2.2.2') in packet[DHCP].options
4248

4349
test_am(DHCP_am,
4450
Ether()/IP()/UDP()/BOOTP(op=1)/DHCP(options=[('message-type', 'request')]),
4551
check_DHCP_am_reply,
4652
domain="localnet")
4753

54+
test_am(DHCP_am,
55+
Ether()/IP()/UDP()/BOOTP(op=1)/DHCP(options=[('message-type', 'request')]),
56+
check_ns_DHCP_am_reply,
57+
domain="localnet",
58+
nameserver=["1.1.1.1", "2.2.2.2"])
4859

4960
= ARP_am
5061
def check_ARP_am_reply(packet):

test/scapy/layers/dhcp.uts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,14 @@ assert DHCPRevOptions['static-routes'][0] == 33
125125

126126
assert dhcpd
127127
import IPython
128-
assert IPython.lib.pretty.pretty(dhcpd) == '<function scapy.ansmachine.dhcpd(self, pool=Net("192.168.1.128/25"), network=\'192.168.1.0/24\', gw=\'192.168.1.1\', nameserver=None, domain=None, renewal_time=60, lease_time=1800, **kwargs)>'
128+
129+
result = IPython.lib.pretty.pretty(dhcpd)
130+
result
131+
132+
# 3 results depending on the Python version
133+
assert result in [
134+
'<function scapy.ansmachine.dhcpd(self, pool: Union[scapy.base_classes.Net, List[str]] = Net("192.168.1.128/25"), network: str = \'192.168.1.0/24\', gw: str = \'192.168.1.1\', nameserver: Union[str, List[str]] = None, domain: Optional[str] = None, renewal_time: int = 60, lease_time: int = 1800, **kwargs)>',
135+
'<function scapy.ansmachine.dhcpd(self, pool: Union[scapy.base_classes.Net, List[str]] = Net("192.168.1.128/25"), network: str = \'192.168.1.0/24\', gw: str = \'192.168.1.1\', nameserver: Union[str, List[str]] = None, domain: Union[str, NoneType] = None, renewal_time: int = 60, lease_time: int = 1800, **kwargs)>',
136+
'<function scapy.ansmachine.dhcpd(self, pool=Net("192.168.1.128/25"), network=\'192.168.1.0/24\', gw=\'192.168.1.1\', nameserver=None, domain=None, renewal_time=60, lease_time=1800, **kwargs)>',
137+
]
129138

0 commit comments

Comments
 (0)