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

52 statements  

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> 

5 

6""" 

7LLMNR (Link Local Multicast Node Resolution). 

8 

9[RFC 4795] 

10 

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""" 

14 

15import struct 

16 

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) 

32 

33 

34_LLMNR_IPv6_mcast_Addr = "FF02:0:0:0:0:0:1:3" 

35_LLMNR_IPv4_mcast_addr = "224.0.0.252" 

36 

37 

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}} 

51 

52 def get_full(self): 

53 # Required for DNSCompressedPacket 

54 return self.original 

55 

56 def hashret(self): 

57 return struct.pack("!H", self.id) 

58 

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] 

76 

77 

78class LLMNRResponse(LLMNRQuery): 

79 name = "Link Local Multicast Node Resolution - Response" 

80 qr = 1 

81 

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) 

87 

88 

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 

98 

99 

100bind_bottom_up(UDP, _LLMNR, dport=5355) 

101bind_bottom_up(UDP, _LLMNR, sport=5355) 

102bind_layers(UDP, _LLMNR, sport=5355, dport=5355) 

103 

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) 

108 

109 

110class LLMNR_am(DNS_am): 

111 """ 

112 LLMNR answering machine. 

113 

114 This has the same arguments as DNS_am. See help(DNS_am) 

115 

116 Example:: 

117 

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 

124 

125 

126# LLMNRQuery(id=RandShort(), qd=DNSQR(qname="vista.")))