1 /*
2 * Copyright (c) 2013, NLNet Labs, Verisign, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the names of the copyright holders nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28
29 #include "check_getdns_context_set_timeout.h"
30 #include "check_getdns_common.h"
31 #include <pthread.h>
32 #include <unistd.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <stdio.h>
36
37
38 /*
39 **************************************************************************
40 * *
41 * T E S T S F O R G E T D N S _ C O N T E X T _ S E T _ TIMEOUT *
42 * *
43 **************************************************************************
44 */
45
START_TEST(getdns_context_set_timeout_1)46 START_TEST (getdns_context_set_timeout_1)
47 {
48 /*
49 * context is NULL
50 * expect: GETDNS_RETURN_INVALID_PARAMETER
51 */
52
53 struct getdns_context *context = NULL;
54
55 ASSERT_RC(getdns_context_set_timeout(context, 1000),
56 GETDNS_RETURN_INVALID_PARAMETER, "Return code from getdns_context_set_timeout()");
57
58 }
59 END_TEST
60
START_TEST(getdns_context_set_timeout_2)61 START_TEST (getdns_context_set_timeout_2)
62 {
63 /*
64 * timeout is 0
65 * expect: GETDNS_RETURN_INVALID_PARAMETER
66 */
67
68 struct getdns_context *context = NULL;
69 CONTEXT_CREATE(TRUE);
70
71 ASSERT_RC(getdns_context_set_timeout(context, 0),
72 GETDNS_RETURN_INVALID_PARAMETER, "Return code from getdns_context_set_timeout()");
73
74 CONTEXT_DESTROY;
75
76 }
77 END_TEST
78
START_TEST(getdns_context_set_idle_timeout_1)79 START_TEST (getdns_context_set_idle_timeout_1)
80 {
81 /*
82 * context is NULL
83 * expect: GETDNS_RETURN_INVALID_PARAMETER
84 */
85
86 struct getdns_context *context = NULL;
87
88 ASSERT_RC(getdns_context_set_idle_timeout(context, 1000),
89 GETDNS_RETURN_INVALID_PARAMETER, "Return code from getdns_context_set_timeout()");
90
91 }
92 END_TEST
93
START_TEST(getdns_context_set_idle_timeout_2)94 START_TEST (getdns_context_set_idle_timeout_2)
95 {
96 /*
97 * timeout is 0 and then 100
98 * expect: GETDNS_RETURN_GOOD
99 */
100
101 struct getdns_context *context = NULL;
102 uint64_t time;
103 CONTEXT_CREATE(TRUE);
104
105 ASSERT_RC(getdns_context_set_idle_timeout(context, 0),
106 GETDNS_RETURN_GOOD, "Return code from getdns_context_set_timeout()");
107 ASSERT_RC(getdns_context_set_idle_timeout(context, 100),
108 GETDNS_RETURN_GOOD, "Return code from getdns_context_set_timeout()");
109 ASSERT_RC(getdns_context_get_idle_timeout(context, &time),
110 GETDNS_RETURN_GOOD, "Return code from getdns_context_set_timeout()");
111 ck_assert_msg(time == 100, "idle_timeout should be 100, got %d", (int)time);
112
113 CONTEXT_DESTROY;
114
115 }
116 END_TEST
117
118 #define GETDNS_STR_IPV4 "IPv4"
119 #define GETDNS_STR_IPV6 "IPv6"
120 #define GETDNS_STR_ADDRESS_TYPE "address_type"
121 #define GETDNS_STR_ADDRESS_DATA "address_data"
122 #define GETDNS_STR_PORT "port"
123 #define TEST_PORT 43210
124
get_test_port(void)125 static uint16_t get_test_port(void)
126 {
127 char *test_port_str;
128 uint16_t test_port;
129 struct timeval tv;
130
131 if (!(test_port_str = getenv("GETDNS_TEST_PORT")) ||
132 !(test_port = (uint16_t)atoi(test_port_str)))
133 test_port = TEST_PORT;
134
135 (void)gettimeofday(&tv, NULL);
136 srandom((int)getpid() + (int)tv.tv_usec);
137 test_port += random() % 1000;
138 return test_port;
139 }
140
141 /* utilities to start a junk udp listener */
142 typedef struct timeout_thread_data {
143 uint16_t port;
144 volatile int running;
145 int num_callbacks;
146 int num_timeouts;
147 } timeout_thread_data;
148
149 typedef struct queued_response {
150 struct sockaddr_in client_addr;
151 getdns_dict *reply;
152 } queued_response;
153
run_server(void * data)154 void* run_server(void* data) {
155 timeout_thread_data* tdata = (timeout_thread_data*)data;
156 int fd;
157 struct sockaddr_in serv_addr;
158 uint8_t mesg[65536];
159 fd_set read_fds;
160 getdns_context *ctxt;
161 getdns_return_t r;
162 getdns_dict *dns_msg;
163 getdns_bindata *qname;
164 char *qname_str;
165 uint32_t qtype, qid;
166 int num_received = 0;
167 queued_response responses[10];
168
169 if ((r = getdns_context_create(&ctxt, 1))) {
170 fprintf( stderr, "Could not create a getdns context: \"%s\"\n"
171 , getdns_get_errorstr_by_id(r));
172 return NULL;
173 }
174 if ((r = getdns_context_set_resolution_type(ctxt, GETDNS_RESOLUTION_STUB))) {
175 fprintf( stderr, "Could not set context in stub mode resolution: \"%s\"\n"
176 , getdns_get_errorstr_by_id(r));
177 return NULL;
178 }
179
180 fd=socket(AF_INET,SOCK_DGRAM,0);
181
182 memset(&serv_addr, 0, sizeof(serv_addr));
183 serv_addr.sin_family = AF_INET;
184 serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
185 serv_addr.sin_port=htons(tdata->port);
186 bind(fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
187
188 /* signal that it's listening */
189 /* dirty timing hack to yield */
190 sleep(1);
191 tdata->running = 1;
192 /* queue up query responses to send out, and delay sending them
193 * for a second */
194 while (tdata->running) {
195 struct sockaddr_in client_addr;
196 FD_ZERO(&read_fds);
197 FD_SET(fd, &read_fds);
198 struct timeval tv;
199 tv.tv_sec = 1;
200 tv.tv_usec = 0;
201 int r = select(fd + 1, &read_fds, NULL, NULL, &tv);
202 if (r > 0 && num_received < 10) {
203 socklen_t len = sizeof(client_addr);
204 int n = recvfrom(fd,mesg,65536,0,(struct sockaddr *)&(responses[num_received].client_addr),&len);
205 if ((r = getdns_wire2msg_dict(mesg, n, &dns_msg))) {
206 fprintf( stderr, "Could not convert wireformat to DNS message dict: \"%s\"\n"
207 , getdns_get_errorstr_by_id(r));
208 continue;
209 }
210 if ((r = getdns_dict_get_bindata(dns_msg, "/question/qname", &qname))) {
211 fprintf( stderr, "Could not get query name from request dict: \"%s\"\n"
212 , getdns_get_errorstr_by_id(r));
213 continue;
214 }
215 if ((r = getdns_convert_dns_name_to_fqdn(qname, &qname_str))) {
216 fprintf( stderr, "Could not convert qname to fqdn: \"%s\"\n"
217 , getdns_get_errorstr_by_id(r));
218 continue;
219 }
220 if ((r = getdns_dict_get_int(dns_msg, "/question/qtype", &qtype))) {
221 fprintf( stderr, "Could not get query type from request dict: \"%s\"\n"
222 , getdns_get_errorstr_by_id(r));
223 continue;
224 }
225 if ((r = getdns_dict_get_int(dns_msg, "/header/id", &qid))) {
226 fprintf( stderr, "Could not get message ID from request dict: \"%s\"\n"
227 , getdns_get_errorstr_by_id(r));
228 continue;
229 }
230 getdns_dict_destroy(dns_msg);
231 r = getdns_general_sync(ctxt, qname_str, qtype, NULL, &responses[num_received].reply);
232 if (r) {
233 fprintf( stderr, "Could query for \"%s\" %d: \"%s\"\n", qname_str, (int)qtype
234 , getdns_get_errorstr_by_id(r));
235 free(qname_str);
236 continue;
237 }
238 free(qname_str);
239 if ((r = getdns_dict_set_int(responses[num_received].reply, "/replies_tree/0/header/id", qid))) {
240 fprintf( stderr, "Could not set message ID on reply dict: \"%s\"\n"
241 , getdns_get_errorstr_by_id(r));
242 continue;
243 }
244 ++num_received;
245 } else if (r == 0 && num_received > 0) {
246 int i = 0;
247 /* timeout - see if we have anything to send */
248 for (i = 0; i < num_received; ++i) {
249 size_t pkt_len = sizeof(mesg);
250 if ((r = getdns_msg_dict2wire_buf(responses[i].reply, mesg, &pkt_len))) {
251 fprintf( stderr, "Could not convert DNS message dict to wireformat: \"%s\"\n"
252 , getdns_get_errorstr_by_id(r));
253 }
254 sendto(fd,mesg,pkt_len,0,(struct sockaddr *)&(responses[i].client_addr),sizeof(client_addr));
255 getdns_dict_destroy(responses[i].reply);
256 }
257 num_received = 0;
258 }
259 }
260 getdns_context_destroy(ctxt);
261 return NULL;
262
263 }
264
timeout_3_cb(struct getdns_context * context,getdns_callback_type_t callback_type,struct getdns_dict * response,void * userarg,getdns_transaction_t transaction_id)265 void timeout_3_cb(struct getdns_context *context,
266 getdns_callback_type_t callback_type,
267 struct getdns_dict * response,
268 void *userarg, getdns_transaction_t transaction_id) {
269 (void)response; (void)transaction_id;
270 timeout_thread_data *tdata = (timeout_thread_data*)userarg;
271 tdata->num_callbacks++;
272 if (callback_type == GETDNS_CALLBACK_TIMEOUT) {
273 tdata->num_timeouts++;
274 }
275 if (tdata->num_callbacks == 1) {
276 /* set timeout to 2 seconds and then issue request */
277 getdns_context_set_timeout(context, 500);
278 getdns_general(context, "getdnsapi.org", GETDNS_RRTYPE_A, NULL,
279 tdata, NULL, timeout_3_cb);
280 }
281 }
282
283 /* Temporarily disabled because of unpredictable results */
START_TEST(getdns_context_set_timeout_3)284 START_TEST (getdns_context_set_timeout_3)
285 {
286 /*
287 * Create a context by calling getdns_context_create()
288 * Create listener thread
289 * Set upstream to localhost:port
290 *
291 * getdns_context_set_resolution_type() to GETDNS_RESOLUTION_STUB
292 * expect: GETDNS_CONTEXT_CODE_RESOLUTION_TYPE
293 */
294
295 struct getdns_context *context = NULL;
296 void* eventloop = NULL;
297 struct getdns_dict* server_dict;
298 struct getdns_list* upstream_list;
299 struct getdns_bindata bindata;
300 uint32_t local_addr = htonl(0x7F000001);
301 pthread_t thread;
302
303 timeout_thread_data t_data;
304 t_data.running = 0;
305 t_data.num_callbacks = 0;
306 t_data.num_timeouts = 0;
307 uint64_t timeout;
308 t_data.port = get_test_port();
309
310 pthread_create(&thread, NULL, run_server, (void *)&t_data);
311
312 while (!t_data.running) {
313 sleep(1);
314 }
315
316 /* set up */
317 CONTEXT_CREATE(TRUE);
318 server_dict = getdns_dict_create_with_context(context);
319 ck_assert_msg(server_dict != NULL, "Allocate IP dictionary failed");
320 bindata.size = strlen(GETDNS_STR_IPV4) + 1;
321 bindata.data = (uint8_t*) GETDNS_STR_IPV4;
322 ASSERT_RC(getdns_dict_set_bindata(server_dict, GETDNS_STR_ADDRESS_TYPE, &bindata),
323 GETDNS_RETURN_GOOD, "set ip bindata");
324 bindata.size = 4;
325 bindata.data = (uint8_t*) &local_addr;
326 ASSERT_RC(getdns_dict_set_bindata(server_dict, GETDNS_STR_ADDRESS_DATA, &bindata),
327 GETDNS_RETURN_GOOD, "set addr bindata");
328 ASSERT_RC(getdns_dict_set_int(server_dict, GETDNS_STR_PORT, t_data.port),
329 GETDNS_RETURN_GOOD, "set addr port");
330
331 upstream_list = getdns_list_create_with_context(context);
332 ck_assert_msg(upstream_list != NULL, "Allocate lists");
333
334 ASSERT_RC(getdns_list_set_dict(upstream_list, 0, server_dict),
335 GETDNS_RETURN_GOOD, "set upstream");
336
337 ASSERT_RC(getdns_context_set_upstream_recursive_servers(context, upstream_list),
338 GETDNS_RETURN_GOOD, "set rec servers");
339
340 /* stub */
341 ASSERT_RC(getdns_context_set_resolution_type(context, GETDNS_RESOLUTION_STUB),
342 GETDNS_RETURN_GOOD, "Return code from getdns_context_set_resolution_type()");
343
344 EVENT_BASE_CREATE;
345
346 getdns_general(context, "getdnsapi.net", GETDNS_RRTYPE_A, NULL,
347 &t_data, NULL, timeout_3_cb);
348
349 RUN_EVENT_LOOP;
350
351 ASSERT_RC(getdns_context_get_timeout(context, &timeout),
352 GETDNS_RETURN_GOOD, "Return code from getdns_context_get_timeout()");
353 ck_assert_msg(timeout == 500, "timeout should be 500, got %d", (int)timeout);
354
355 CONTEXT_DESTROY;
356
357 t_data.running = 0;
358 pthread_join(thread, NULL);
359 ck_assert_msg(t_data.num_callbacks == 2, "callbacks != 2");
360 ck_assert_msg(t_data.num_timeouts == 1, "timeouts != 1");
361
362 }
363 END_TEST
364
365
366
367 Suite *
getdns_context_set_timeout_suite(void)368 getdns_context_set_timeout_suite (void)
369 {
370 Suite *s = suite_create ("getdns_context_set_timeout()");
371
372 /* Negative test caseis */
373 TCase *tc_neg = tcase_create("Negative");
374 tcase_add_test(tc_neg, getdns_context_set_timeout_1);
375 tcase_add_test(tc_neg, getdns_context_set_timeout_2);
376 tcase_add_test(tc_neg, getdns_context_set_idle_timeout_1);
377 tcase_add_test(tc_neg, getdns_context_set_idle_timeout_2);
378 suite_add_tcase(s, tc_neg);
379
380 /* Positive test cases */
381 /* Temporarily disabled because of unpredictable results */
382 TCase *tc_pos = tcase_create("Positive");
383 tcase_set_timeout(tc_pos, 15.0);
384 tcase_add_test(tc_pos, getdns_context_set_timeout_3);
385 suite_add_tcase(s, tc_pos);
386
387 return s;
388
389 }
390
391