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.compat import orb
25from scapy.layers.inet import UDP, DestIPField
26from scapy.layers.dns import (
27 DNSCompressedPacket,
28 DNS_am,
29 DNS,
30 DNSQR,
31 DNSRR,
32)
35_LLMNR_IPv6_mcast_Addr = "FF02:0:0:0:0:0:1:3"
36_LLMNR_IPv4_mcast_addr = "224.0.0.252"
39class LLMNRQuery(DNSCompressedPacket):
40 name = "Link Local Multicast Node Resolution - Query"
41 qd = []
42 fields_desc = [
43 ShortField("id", 0),
44 BitField("qr", 0, 1),
45 BitEnumField("opcode", 0, 4, {0: "QUERY"}),
46 BitField("c", 0, 1),
47 BitField("tc", 0, 1),
48 BitField("t", 0, 1),
49 BitField("z", 0, 4)
50 ] + DNS.fields_desc[-9:]
51 overload_fields = {UDP: {"sport": 5355, "dport": 5355}}
53 def get_full(self):
54 # Required for DNSCompressedPacket
55 return self.original
57 def hashret(self):
58 return struct.pack("!H", self.id)
60 def mysummary(self):
61 s = self.__class__.__name__
62 if self.qr:
63 if self.an and isinstance(self.an[0], DNSRR):
64 s += " '%s' is at '%s'" % (
65 self.an[0].rrname.decode(errors="backslashreplace"),
66 self.an[0].rdata,
67 )
68 else:
69 s += " [malformed]"
70 elif self.qd and isinstance(self.qd[0], DNSQR):
71 s += " who has '%s'" % (
72 self.qd[0].qname.decode(errors="backslashreplace"),
73 )
74 else:
75 s += " [malformed]"
76 return s, [UDP]
79class LLMNRResponse(LLMNRQuery):
80 name = "Link Local Multicast Node Resolution - Response"
81 qr = 1
83 def answers(self, other):
84 return (isinstance(other, LLMNRQuery) and
85 self.id == other.id and
86 self.qr == 1 and
87 other.qr == 0)
90class _LLMNR(Packet):
91 @classmethod
92 def dispatch_hook(cls, _pkt=None, *args, **kargs):
93 if len(_pkt) >= 2:
94 if (orb(_pkt[2]) & 0x80): # Response
95 return LLMNRResponse
96 else: # Query
97 return LLMNRQuery
98 return cls
101bind_bottom_up(UDP, _LLMNR, dport=5355)
102bind_bottom_up(UDP, _LLMNR, sport=5355)
103bind_layers(UDP, _LLMNR, sport=5355, dport=5355)
105DestIPField.bind_addr(LLMNRQuery, _LLMNR_IPv4_mcast_addr, dport=5355)
106DestIPField.bind_addr(LLMNRResponse, _LLMNR_IPv4_mcast_addr, dport=5355)
107DestIP6Field.bind_addr(LLMNRQuery, _LLMNR_IPv6_mcast_Addr, dport=5355)
108DestIP6Field.bind_addr(LLMNRResponse, _LLMNR_IPv6_mcast_Addr, dport=5355)
111class LLMNR_am(DNS_am):
112 """
113 LLMNR answering machine.
115 This has the same arguments as DNS_am. See help(DNS_am)
117 Example::
119 >>> llmnrd(joker="192.168.0.2", iface="eth0")
120 >>> llmnrd(match={"TEST": "192.168.0.2"})
121 """
122 function_name = "llmnrd"
123 filter = "udp port 5355"
124 cls = LLMNRQuery
127# LLMNRQuery(id=RandShort(), qd=DNSQR(qname="vista.")))