78 lines
2.5 KiB
Python
78 lines
2.5 KiB
Python
|
# This file is part of Scapy
|
||
|
# See http://www.secdev.org/projects/scapy for more information
|
||
|
# Copyright (C) Philippe Biondi <phil@secdev.org>
|
||
|
# This program is published under a GPLv2 license
|
||
|
|
||
|
"""
|
||
|
LLMNR (Link Local Multicast Node Resolution).
|
||
|
|
||
|
[RFC 4795]
|
||
|
|
||
|
LLMNR is based on the DNS packet format (RFC1035 Section 4)
|
||
|
RFC also envisions LLMNR over TCP. Like vista, we don't support it -- arno
|
||
|
"""
|
||
|
|
||
|
import struct
|
||
|
|
||
|
from scapy.fields import BitEnumField, BitField, ShortField
|
||
|
from scapy.packet import Packet, bind_layers, bind_bottom_up
|
||
|
from scapy.compat import orb
|
||
|
from scapy.layers.inet import UDP
|
||
|
from scapy.layers.dns import DNSQRField, DNSRRField, DNSRRCountField
|
||
|
|
||
|
|
||
|
_LLMNR_IPv6_mcast_Addr = "FF02:0:0:0:0:0:1:3"
|
||
|
_LLMNR_IPv4_mcast_addr = "224.0.0.252"
|
||
|
|
||
|
|
||
|
class LLMNRQuery(Packet):
|
||
|
name = "Link Local Multicast Node Resolution - Query"
|
||
|
fields_desc = [ShortField("id", 0),
|
||
|
BitField("qr", 0, 1),
|
||
|
BitEnumField("opcode", 0, 4, {0: "QUERY"}),
|
||
|
BitField("c", 0, 1),
|
||
|
BitField("tc", 0, 2),
|
||
|
BitField("z", 0, 4),
|
||
|
BitEnumField("rcode", 0, 4, {0: "ok"}),
|
||
|
DNSRRCountField("qdcount", None, "qd"),
|
||
|
DNSRRCountField("ancount", None, "an"),
|
||
|
DNSRRCountField("nscount", None, "ns"),
|
||
|
DNSRRCountField("arcount", None, "ar"),
|
||
|
DNSQRField("qd", "qdcount"),
|
||
|
DNSRRField("an", "ancount"),
|
||
|
DNSRRField("ns", "nscount"),
|
||
|
DNSRRField("ar", "arcount", 0)]
|
||
|
overload_fields = {UDP: {"sport": 5355, "dport": 5355}}
|
||
|
|
||
|
def hashret(self):
|
||
|
return struct.pack("!H", self.id)
|
||
|
|
||
|
|
||
|
class LLMNRResponse(LLMNRQuery):
|
||
|
name = "Link Local Multicast Node Resolution - Response"
|
||
|
qr = 1
|
||
|
|
||
|
def answers(self, other):
|
||
|
return (isinstance(other, LLMNRQuery) and
|
||
|
self.id == other.id and
|
||
|
self.qr == 1 and
|
||
|
other.qr == 0)
|
||
|
|
||
|
|
||
|
class _LLMNR(Packet):
|
||
|
@classmethod
|
||
|
def dispatch_hook(cls, _pkt=None, *args, **kargs):
|
||
|
if len(_pkt) >= 2:
|
||
|
if (orb(_pkt[2]) & 0x80): # Response
|
||
|
return LLMNRResponse
|
||
|
else: # Query
|
||
|
return LLMNRQuery
|
||
|
return cls
|
||
|
|
||
|
|
||
|
bind_bottom_up(UDP, _LLMNR, dport=5355)
|
||
|
bind_bottom_up(UDP, _LLMNR, sport=5355)
|
||
|
bind_layers(UDP, _LLMNR, sport=5355, dport=5355)
|
||
|
|
||
|
# LLMNRQuery(id=RandShort(), qd=DNSQR(qname="vista.")))
|