Skip to content

Commit d1a45d6

Browse files
author
duanhongyi
committed
add ping support
1 parent d318938 commit d1a45d6

3 files changed

Lines changed: 145 additions & 3 deletions

File tree

bin/dnsserver.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ def __call__(self, failure):
5454

5555

5656
class MapResolver(client.Resolver):
57-
def __init__(self, Finder, Amapping, NSmapping, SOAmapping, servers):
57+
def __init__(self, Finder, Amapping, AmappingBlacklist, NSmapping, SOAmapping, servers):
5858
self.Finder = Finder
5959
self.Amapping = Amapping
60+
self.AmappingBlacklist = AmappingBlacklist
6061
self.NSmapping = NSmapping
6162
self.SOAmapping = SOAmapping
6263
client.Resolver.__init__(self, servers=servers)
@@ -77,6 +78,7 @@ def packResult( value ):
7778
ret = []
7879
add = []
7980
for x in value:
81+
if x in self.AmappingBlacklist: continue
8082
ret.append(dns.RRHeader(name, dns.A, dns.IN, ttl, dns.Record_A(x, ttl), True))
8183

8284
if edns is not None:

bin/icmp.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import os, sys, socket, struct, select, time
2+
3+
# From /usr/include/linux/icmp.h; your milage may vary.
4+
ICMP_ECHO_REQUEST = 8 # Seems to be the same on Solaris.
5+
6+
7+
def checksum(source_string):
8+
"""
9+
I'm not too confident that this is right but testing seems
10+
to suggest that it gives the same answers as in_cksum in ping.c
11+
"""
12+
sum = 0
13+
countTo = (len(source_string)/2)*2
14+
count = 0
15+
while count<countTo:
16+
thisVal = ord(source_string[count + 1])*256 + ord(source_string[count])
17+
sum = sum + thisVal
18+
sum = sum & 0xffffffff # Necessary?
19+
count = count + 2
20+
21+
if countTo<len(source_string):
22+
sum = sum + ord(source_string[len(source_string) - 1])
23+
sum = sum & 0xffffffff # Necessary?
24+
25+
sum = (sum >> 16) + (sum & 0xffff)
26+
sum = sum + (sum >> 16)
27+
answer = ~sum
28+
answer = answer & 0xffff
29+
30+
# Swap bytes. Bugger me if I know why.
31+
answer = answer >> 8 | (answer << 8 & 0xff00)
32+
33+
return answer
34+
35+
36+
def receive_one_ping(my_socket, ID, timeout):
37+
"""
38+
receive the ping from the socket.
39+
"""
40+
timeLeft = timeout
41+
while True:
42+
startedSelect = time.time()
43+
whatReady = select.select([my_socket], [], [], timeLeft)
44+
howLongInSelect = (time.time() - startedSelect)
45+
if whatReady[0] == []: # Timeout
46+
return
47+
48+
timeReceived = time.time()
49+
recPacket, addr = my_socket.recvfrom(1024)
50+
icmpHeader = recPacket[20:28]
51+
type, code, checksum, packetID, sequence = struct.unpack(
52+
"bbHHh", icmpHeader
53+
)
54+
if packetID == ID:
55+
bytesInDouble = struct.calcsize("d")
56+
timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0]
57+
return timeReceived - timeSent
58+
59+
timeLeft = timeLeft - howLongInSelect
60+
if timeLeft <= 0:
61+
return
62+
63+
64+
def send_one_ping(my_socket, dest_addr, ID):
65+
"""
66+
Send one ping to the given >dest_addr<.
67+
"""
68+
dest_addr = socket.gethostbyname(dest_addr)
69+
70+
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
71+
my_checksum = 0
72+
73+
# Make a dummy heder with a 0 checksum.
74+
header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, ID, 1)
75+
bytesInDouble = struct.calcsize("d")
76+
data = (192 - bytesInDouble) * "Q"
77+
data = struct.pack("d", time.time()) + data
78+
79+
# Calculate the checksum on the data and the dummy header.
80+
my_checksum = checksum(header + data)
81+
82+
# Now that we have the right checksum, we put that in. It's just easier
83+
# to make up a new header than to stuff it into the dummy.
84+
header = struct.pack(
85+
"bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), ID, 1
86+
)
87+
packet = header + data
88+
my_socket.sendto(packet, (dest_addr, 1)) # Don't know about the 1
89+
90+
91+
def ping(dest_addr, timeout):
92+
"""
93+
Returns either the delay (in seconds) or none on timeout.
94+
"""
95+
icmp = socket.getprotobyname("icmp")
96+
try:
97+
my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
98+
except socket.error, (errno, msg):
99+
if errno == 1:
100+
# Operation not permitted
101+
msg = msg + (
102+
" - Note that ICMP messages can only be sent from processes"
103+
" running as root."
104+
)
105+
raise socket.error(msg)
106+
raise # raise the original error
107+
108+
my_ID = os.getpid() & 0xFFFF
109+
110+
send_one_ping(my_socket, dest_addr, my_ID)
111+
delay = receive_one_ping(my_socket, my_ID, timeout)
112+
113+
my_socket.close()
114+
return delay

bin/sdns.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import sys, os
77
import signal
88
import time
9+
import threading
910
from os.path import isfile
1011
sys.path.append('../lib')
1112
import yaml
@@ -28,6 +29,7 @@
2829
sys.path.append('ippool.py')
2930
sys.path.append('dnsserver.py')
3031
import ippool, dnsserver
32+
from icmp import ping
3133

3234
def loadconfig(path):
3335
if not isfile(path):
@@ -61,6 +63,23 @@ def get_local_ip():
6163
socket.inet_ntoa(namestr[i+20:i+24]))
6264
for i in range(0, outbytes, struct_size)]
6365

66+
def prepare_ip_blacklist(amapping_blacklist, ip_list):
67+
def task(amapping_blacklist, ip_list):
68+
while True:
69+
for ip in ip_list:
70+
try:
71+
delay = ping(ip, 9)
72+
except socket.gaierror as e:
73+
delay = None
74+
if delay == None and ip not in amapping_blacklist:
75+
amapping_blacklist.add(ip)
76+
if delay != None and ip in amapping_blacklist:
77+
amapping_blacklist.remove(ip)
78+
thread = threading.Thread(target=task, args=(amapping_blacklist, ip_list))
79+
thread.setDaemon(True)
80+
thread.start()
81+
82+
6483
def prepare_run(run_env):
6584
#load main config
6685
logger.info('start to load conf/sdns.yaml ......')
@@ -75,15 +94,22 @@ def prepare_run(run_env):
7594
Amapping = loadconfig(conf['AFILE'])
7695
NSmapping = loadconfig(conf['NSFILE'])
7796
SOAmapping = loadconfig(conf['SOAFILE'])
78-
97+
AmappingBlacklist = set()
98+
ip_list = set()
99+
for item in Amapping.values():
100+
for key, value in item.items():
101+
if key == 'ttl': continue
102+
ip_list.update(value.split(' '))
103+
prepare_ip_blacklist(AmappingBlacklist, ip_list)
79104
# set up a resolver that uses the mapping or a secondary nameserver
80105
dnsforward = []
81106
for i in conf['dnsforward_ip']:
82107
dnsforward.append((i, conf['dnsforward_port']))
83108

84109
for ifc,ip in get_local_ip():
85110
# create the protocols
86-
SmartResolver = dnsserver.MapResolver(Finder, Amapping, NSmapping, SOAmapping, servers=dnsforward)
111+
SmartResolver = dnsserver.MapResolver(
112+
Finder, Amapping, AmappingBlacklist, NSmapping, SOAmapping, servers=dnsforward)
87113
f = dnsserver.SmartDNSFactory(caches=[cache.CacheResolver()], clients=[SmartResolver])
88114
p = dns.DNSDatagramProtocol(f)
89115
f.noisy = p.noisy = False

0 commit comments

Comments
 (0)