-
Notifications
You must be signed in to change notification settings - Fork 0
/
llmnr-responder
executable file
·110 lines (82 loc) · 2.67 KB
/
llmnr-responder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env ruby
# This file is part of packetgen-plugin-smb.
# See https://github.com/sdaubert/packetgen-plugin-smb for more informations
# Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
# This program is published under MIT license.
#
# This small example implements a LLMNR responder. It responds to all LLMNR
# requests on local network, and says requested name is its IP address.
# frozen_string_literal: true
require 'optparse'
require 'socket'
require 'ipaddr'
require 'packetgen'
require 'packetgen-plugin-smb'
BIND_ADDR = '0.0.0.0'
class LlmnrResponder
attr_reader :socket, :my_ip, :my_ip_data
LLMNR_MCAST_ADDR = '224.0.0.252'
def initialize
@socket = UDPSocket.new
end
def start(bind_addr:, iface:)
@my_ip = Interfacez.ipv4_address_of(iface)
@my_ip_data = IPAddr.new(my_ip).hton
configure_multicast(my_ip_data)
socket.bind(bind_addr, PacketGen::Plugin::LLMNR::UDP_PORT)
start_loop
end
private
def log(str)
puts "[LLMNR] #{str}"
end
def configure_multicast(local_ip_bin)
mreq = IPAddr.new(LLMNR_MCAST_ADDR).hton + local_ip_bin
socket.setsockopt(:IPPROTO_IP, :IP_ADD_MEMBERSHIP, mreq)
end
def start_loop
loop do
data, peer = socket.recvfrom(1024)
pkt = PacketGen.parse(data, first_header: 'LLMNR')
next unless pkt.is?('LLMNR')
peer_port = peer[1]
peer_ip = peer[3]
log "received LLMNR request from #{peer_ip}"
# Forge LLMNR response
response_pkt = pkt.reply
response_pkt.llmnr.qr = true
response_pkt.llmnr.qd.each do |question|
next unless (question.human_rrclass == 'IN') && (question.human_type == 'A')
log "Say to #{peer_ip} #{question.name} is #{my_ip}"
answer = { rtype: 'RR', name: question.name, rdata: my_ip_data }
response_pkt.llmnr.an << answer
end
response_pkt.calc
next unless response_pkt.llmnr.ancount > 0
socket.send(response_pkt.to_s, 0, peer_ip, peer_port)
end
end
end
def parse_options
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
opts.separator ''
opts.separator 'Options:'
opts.on_tail('-h', '--help', 'Show this message') do
puts opts
exit
end
opts.on('-i IFACE', '--interface IFACE', 'interface on which responds') do |iface|
options[:iface] = iface
end
end.parse!
options
end
def check_options(options)
raise 'No interface given' if options[:iface].nil?
raise "unknown interface #{options[:iface]}" unless Interfacez.all.include? options[:iface]
end
options = parse_options
check_options options
LlmnrResponder.new.start(bind_addr: BIND_ADDR, iface: options[:iface])