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 DestField, 

21 DestIP6Field, 

22 ShortField, 

23) 

24from scapy.packet import Packet, bind_layers, bind_bottom_up 

25from scapy.compat import orb 

26from scapy.layers.inet import UDP 

27from scapy.layers.dns import ( 

28 DNSCompressedPacket, 

29 DNS_am, 

30 DNS, 

31 DNSQR, 

32 DNSRR, 

33) 

34 

35 

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

37_LLMNR_IPv4_mcast_addr = "224.0.0.252" 

38 

39 

40class LLMNRQuery(DNSCompressedPacket): 

41 name = "Link Local Multicast Node Resolution - Query" 

42 qd = [] 

43 fields_desc = [ 

44 ShortField("id", 0), 

45 BitField("qr", 0, 1), 

46 BitEnumField("opcode", 0, 4, {0: "QUERY"}), 

47 BitField("c", 0, 1), 

48 BitField("tc", 0, 1), 

49 BitField("t", 0, 1), 

50 BitField("z", 0, 4) 

51 ] + DNS.fields_desc[-9:] 

52 overload_fields = {UDP: {"sport": 5355, "dport": 5355}} 

53 

54 def get_full(self): 

55 # Required for DNSCompressedPacket 

56 return self.original 

57 

58 def hashret(self): 

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

60 

61 def mysummary(self): 

62 s = self.__class__.__name__ 

63 if self.qr: 

64 if self.an and isinstance(self.an[0], DNSRR): 

65 s += " '%s' is at '%s'" % ( 

66 self.an[0].rrname.decode(errors="backslashreplace"), 

67 self.an[0].rdata, 

68 ) 

69 else: 

70 s += " [malformed]" 

71 elif self.qd and isinstance(self.qd[0], DNSQR): 

72 s += " who has '%s'" % ( 

73 self.qd[0].qname.decode(errors="backslashreplace"), 

74 ) 

75 else: 

76 s += " [malformed]" 

77 return s, [UDP] 

78 

79 

80class LLMNRResponse(LLMNRQuery): 

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

82 qr = 1 

83 

84 def answers(self, other): 

85 return (isinstance(other, LLMNRQuery) and 

86 self.id == other.id and 

87 self.qr == 1 and 

88 other.qr == 0) 

89 

90 

91class _LLMNR(Packet): 

92 @classmethod 

93 def dispatch_hook(cls, _pkt=None, *args, **kargs): 

94 if len(_pkt) >= 2: 

95 if (orb(_pkt[2]) & 0x80): # Response 

96 return LLMNRResponse 

97 else: # Query 

98 return LLMNRQuery 

99 return cls 

100 

101 

102bind_bottom_up(UDP, _LLMNR, dport=5355) 

103bind_bottom_up(UDP, _LLMNR, sport=5355) 

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

105 

106DestField.bind_addr(LLMNRQuery, _LLMNR_IPv4_mcast_addr, dport=5355) 

107DestField.bind_addr(LLMNRResponse, _LLMNR_IPv4_mcast_addr, dport=5355) 

108DestIP6Field.bind_addr(LLMNRQuery, _LLMNR_IPv6_mcast_Addr, dport=5355) 

109DestIP6Field.bind_addr(LLMNRResponse, _LLMNR_IPv6_mcast_Addr, dport=5355) 

110 

111 

112class LLMNR_am(DNS_am): 

113 """ 

114 LLMNR answering machine. 

115 

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

117 

118 Example:: 

119 

120 >>> llmnrd(joker="192.168.0.2", iface="eth0") 

121 >>> llmnrd(match={"TEST": "192.168.0.2"}) 

122 """ 

123 function_name = "llmnrd" 

124 filter = "udp port 5355" 

125 cls = LLMNRQuery 

126 

127 

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