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

53 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.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) 

33 

34 

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

36_LLMNR_IPv4_mcast_addr = "224.0.0.252" 

37 

38 

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

52 

53 def get_full(self): 

54 # Required for DNSCompressedPacket 

55 return self.original 

56 

57 def hashret(self): 

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

59 

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] 

77 

78 

79class LLMNRResponse(LLMNRQuery): 

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

81 qr = 1 

82 

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) 

88 

89 

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 

99 

100 

101bind_bottom_up(UDP, _LLMNR, dport=5355) 

102bind_bottom_up(UDP, _LLMNR, sport=5355) 

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

104 

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) 

109 

110 

111class LLMNR_am(DNS_am): 

112 """ 

113 LLMNR answering machine. 

114 

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

116 

117 Example:: 

118 

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 

125 

126 

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