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