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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
# SPDX-License-Identifier: GPL-3.0-or-later
"""TCP Connection Management tests"""
import socket
import struct
import time
import pytest
import utils
@pytest.mark.parametrize('garbage_lengths', [
(1,),
(1024,),
(65533,), # max size garbage
(65533, 65533),
(1024, 1024, 1024),
# (0,), # currently kresd uses this as a heuristic of "lost in bytestream"
# (0, 1024), # and closes the connection
])
def test_ignore_garbage(kresd_sock, garbage_lengths, single_buffer, query_before):
"""Send chunk of garbage, prefixed by garbage length. It should be ignored."""
buff = b''
if query_before: # optionally send initial query
msg_buff_before, msgid_before = utils.get_msgbuff()
if single_buffer:
buff += msg_buff_before
else:
kresd_sock.sendall(msg_buff_before)
for glength in garbage_lengths: # prepare garbage data
if glength is None:
continue
garbage_buff = utils.get_prefixed_garbage(glength)
if single_buffer:
buff += garbage_buff
else:
kresd_sock.sendall(garbage_buff)
msg_buff, msgid = utils.get_msgbuff() # final query
buff += msg_buff
kresd_sock.sendall(buff)
if query_before:
answer_before = utils.receive_parse_answer(kresd_sock)
assert answer_before.id == msgid_before
answer = utils.receive_parse_answer(kresd_sock)
assert answer.id == msgid
def test_pipelining(kresd_sock):
"""
First query takes longer to resolve - answer to second query should arrive sooner.
This test requires internet connection.
"""
# initialization (to avoid issues with net.ipv6=true)
buff_pre, msgid_pre = utils.get_msgbuff('0.delay.getdnsapi.net.')
kresd_sock.sendall(buff_pre)
msg_answer = utils.receive_parse_answer(kresd_sock)
assert msg_answer.id == msgid_pre
# test
buff1, msgid1 = utils.get_msgbuff('1500.delay.getdnsapi.net.', msgid=1)
buff2, msgid2 = utils.get_msgbuff('1.delay.getdnsapi.net.', msgid=2)
buff = buff1 + buff2
kresd_sock.sendall(buff)
msg_answer = utils.receive_parse_answer(kresd_sock)
assert msg_answer.id == msgid2
msg_answer = utils.receive_parse_answer(kresd_sock)
assert msg_answer.id == msgid1
@pytest.mark.parametrize('duration, delay', [
(utils.MAX_TIMEOUT, 0.1),
(utils.MAX_TIMEOUT, 3),
(utils.MAX_TIMEOUT, 7),
(utils.MAX_TIMEOUT + 10, 3),
])
def test_long_lived(kresd_sock, duration, delay):
"""Establish and keep connection alive for longer than maximum timeout."""
utils.ping_alive(kresd_sock)
end_time = time.time() + duration
while time.time() < end_time:
time.sleep(delay)
utils.ping_alive(kresd_sock)
def test_close(kresd_sock, query_before):
"""Establish a connection and wait for timeout from kresd."""
if query_before:
utils.ping_alive(kresd_sock)
time.sleep(utils.MAX_TIMEOUT)
with utils.expect_kresd_close():
utils.ping_alive(kresd_sock)
def test_slow_lorris(kresd_sock, query_before):
"""Simulate slow-lorris attack by sending byte after byte with delays in between."""
if query_before:
utils.ping_alive(kresd_sock)
buff, _ = utils.get_msgbuff()
end_time = time.time() + utils.MAX_TIMEOUT
with utils.expect_kresd_close():
for i in range(len(buff)):
b = buff[i:i+1]
kresd_sock.send(b)
if time.time() > end_time:
break
time.sleep(1)
@pytest.mark.parametrize('sock_func_name', [
'ip_tcp_socket',
'ip6_tcp_socket',
])
def test_oob(kresd, sock_func_name):
"""TCP out-of-band (urgent) data must not crash resolver."""
make_sock = getattr(kresd, sock_func_name)
sock = make_sock()
msg_buff, msgid = utils.get_msgbuff()
sock.sendall(msg_buff, socket.MSG_OOB)
try:
msg_answer = utils.receive_parse_answer(sock)
assert msg_answer.id == msgid
except ConnectionError:
pass # TODO kresd responds with TCP RST, this should be fixed
# check kresd is alive
sock2 = make_sock()
utils.ping_alive(sock2)
def flood_buffer(msgcount):
flood_buff = bytes()
msgbuff, _ = utils.get_msgbuff()
noid_msgbuff = msgbuff[2:]
def gen_msg(msgid):
return struct.pack("!H", len(msgbuff)) + struct.pack("!H", msgid) + noid_msgbuff
for i in range(msgcount):
flood_buff += gen_msg(i)
return flood_buff
def test_query_flood_close(make_kresd_sock):
"""Flood resolver with queries and close the connection."""
buff = flood_buffer(10000)
sock1 = make_kresd_sock()
sock1.sendall(buff)
sock1.close()
sock2 = make_kresd_sock()
utils.ping_alive(sock2)
def test_query_flood_no_recv(make_kresd_sock):
"""Flood resolver with queries but don't read any data."""
# A use-case for TCP_USER_TIMEOUT socket option? See RFC 793 and RFC 5482
# It seems it doesn't works as expected. libuv doesn't return any error
# (neither on uv_write() call, not in the callback) when kresd sends answers,
# so kresd can't recognize that client didn't read any answers. At a certain
# point, kresd stops receiving queries from the client (whilst client keep
# sending) and closes connection due to timeout.
buff = flood_buffer(10000)
sock1 = make_kresd_sock()
end_time = time.time() + utils.MAX_TIMEOUT
with utils.expect_kresd_close(rst_ok=True): # connection must be closed
while time.time() < end_time:
sock1.sendall(buff)
time.sleep(0.5)
sock2 = make_kresd_sock()
utils.ping_alive(sock2) # resolver must stay alive
@pytest.mark.parametrize('glength, gcount, delay', [
(65533, 100, 0.5),
(0, 100000, 0.5),
(1024, 1000, 0.5),
(65533, 1, 0),
(0, 1, 0),
(1024, 1, 0),
])
def test_query_flood_garbage(make_kresd_silent_sock, glength, gcount, delay, query_before):
"""Flood resolver with prefixed garbage."""
sock1 = make_kresd_silent_sock()
if query_before:
utils.ping_alive(sock1)
gbuff = utils.get_prefixed_garbage(glength)
buff = gbuff * gcount
end_time = time.time() + utils.MAX_TIMEOUT
with utils.expect_kresd_close(rst_ok=True): # connection must be closed
while time.time() < end_time:
sock1.sendall(buff)
time.sleep(delay)
sock2 = make_kresd_silent_sock()
utils.ping_alive(sock2) # resolver must stay alive
|