Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/llmnr.py: 94%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Philippe Biondi <phil@secdev.org>
6"""
7LLMNR (Link Local Multicast Node Resolution).
9[RFC 4795]
11LLMNR is based on the DNS packet format (RFC1035 Section 4)
12RFC also envisions LLMNR over TCP. Like vista, we don't support it -- arno
13"""
15import struct
17from scapy.fields import (
18 BitEnumField,
19 BitField,
20 DestIP6Field,
21 ShortField,
22)
23from scapy.packet import Packet, bind_layers, bind_bottom_up
24from scapy.layers.inet import UDP, DestIPField
25from scapy.layers.dns import (
26 DNSCompressedPacket,
27 DNS_am,
28 DNS,
29 DNSQR,
30 DNSRR,
31)
34_LLMNR_IPv6_mcast_Addr = "FF02:0:0:0:0:0:1:3"
35_LLMNR_IPv4_mcast_addr = "224.0.0.252"
38class LLMNRQuery(DNSCompressedPacket):
39 name = "Link Local Multicast Node Resolution - Query"
40 qd = []
41 fields_desc = [
42 ShortField("id", 0),
43 BitField("qr", 0, 1),
44 BitEnumField("opcode", 0, 4, {0: "QUERY"}),
45 BitField("c", 0, 1),
46 BitField("tc", 0, 1),
47 BitField("t", 0, 1),
48 BitField("z", 0, 4)
49 ] + DNS.fields_desc[-9:]
50 overload_fields = {UDP: {"sport": 5355, "dport": 5355}}
52 def get_full(self):
53 # Required for DNSCompressedPacket
54 return self.original
56 def hashret(self):
57 return struct.pack("!H", self.id)
59 def mysummary(self):
60 s = self.__class__.__name__
61 if self.qr:
62 if self.an and isinstance(self.an[0], DNSRR):
63 s += " '%s' is at '%s'" % (
64 self.an[0].rrname.decode(errors="backslashreplace"),
65 self.an[0].rdata,
66 )
67 else:
68 s += " [malformed]"
69 elif self.qd and isinstance(self.qd[0], DNSQR):
70 s += " who has '%s'" % (
71 self.qd[0].qname.decode(errors="backslashreplace"),
72 )
73 else:
74 s += " [malformed]"
75 return s, [UDP]
78class LLMNRResponse(LLMNRQuery):
79 name = "Link Local Multicast Node Resolution - Response"
80 qr = 1
82 def answers(self, other):
83 return (isinstance(other, LLMNRQuery) and
84 self.id == other.id and
85 self.qr == 1 and
86 other.qr == 0)
89class _LLMNR(Packet):
90 @classmethod
91 def dispatch_hook(cls, _pkt=None, *args, **kargs):
92 if len(_pkt) >= 2:
93 if (_pkt[2] & 0x80): # Response
94 return LLMNRResponse
95 else: # Query
96 return LLMNRQuery
97 return cls
100bind_bottom_up(UDP, _LLMNR, dport=5355)
101bind_bottom_up(UDP, _LLMNR, sport=5355)
102bind_layers(UDP, _LLMNR, sport=5355, dport=5355)
104DestIPField.bind_addr(LLMNRQuery, _LLMNR_IPv4_mcast_addr, dport=5355)
105DestIPField.bind_addr(LLMNRResponse, _LLMNR_IPv4_mcast_addr, dport=5355)
106DestIP6Field.bind_addr(LLMNRQuery, _LLMNR_IPv6_mcast_Addr, dport=5355)
107DestIP6Field.bind_addr(LLMNRResponse, _LLMNR_IPv6_mcast_Addr, dport=5355)
110class LLMNR_am(DNS_am):
111 """
112 LLMNR answering machine.
114 This has the same arguments as DNS_am. See help(DNS_am)
116 Example::
118 >>> llmnrd(joker="192.168.0.2", iface="eth0")
119 >>> llmnrd(match={"TEST": "192.168.0.2"})
120 """
121 function_name = "llmnrd"
122 filter = "udp port 5355"
123 cls = LLMNRQuery
126# LLMNRQuery(id=RandShort(), qd=DNSQR(qname="vista.")))