source: mainline/uspace/srv/net/dnsrsrv/transport.c@ dba3e2c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since dba3e2c was 06fe3b6, checked in by Jiri Svoboda <jiri@…>, 13 years ago

Simple retry mechanism for DNS queries.

  • Property mode set to 100644
File size: 6.4 KB
Line 
1/*
2 * Copyright (c) 2013 Jiri Svoboda
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
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup dnsres
30 * @{
31 */
32/**
33 * @file
34 */
35
36#include <adt/list.h>
37#include <errno.h>
38#include <fibril_synch.h>
39#include <io/log.h>
40#include <net/in.h>
41#include <net/inet.h>
42#include <net/socket.h>
43#include <stdbool.h>
44#include <stdlib.h>
45
46#include "dns_msg.h"
47#include "dns_type.h"
48#include "transport.h"
49
50#define RECV_BUF_SIZE 4096
51#define DNS_SERVER_PORT 53
52
53/** Request timeout (microseconds) */
54#define REQ_TIMEOUT (5*1000*1000)
55
56/** Maximum number of retries */
57#define REQ_RETRY_MAX 3
58
59typedef struct {
60 link_t lreq;
61 dns_message_t *req;
62 dns_message_t *resp;
63
64 bool done;
65 fibril_condvar_t done_cv;
66 fibril_mutex_t done_lock;
67
68 int status;
69} trans_req_t;
70
71static uint8_t recv_buf[RECV_BUF_SIZE];
72static fid_t recv_fid;
73static int transport_fd = -1;
74inet_addr_t dns_server_addr;
75
76/** Outstanding requests */
77static LIST_INITIALIZE(treq_list);
78static FIBRIL_MUTEX_INITIALIZE(treq_lock);
79
80static int transport_recv_fibril(void *arg);
81
82int transport_init(void)
83{
84 struct sockaddr_in laddr;
85 int fd;
86 fid_t fid;
87 int rc;
88
89 laddr.sin_family = AF_INET;
90 laddr.sin_port = htons(12345);
91 laddr.sin_addr.s_addr = INADDR_ANY;
92
93 fd = -1;
94
95 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
96 if (fd < 0) {
97 rc = EIO;
98 goto error;
99 }
100
101 rc = bind(fd, (struct sockaddr *)&laddr, sizeof(laddr));
102 if (rc != EOK)
103 goto error;
104
105 transport_fd = fd;
106
107 fid = fibril_create(transport_recv_fibril, NULL);
108 if (fid == 0)
109 goto error;
110
111 fibril_add_ready(fid);
112 recv_fid = fid;
113 return EOK;
114error:
115 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed initializing network socket.");
116 if (fd >= 0)
117 closesocket(fd);
118 return rc;
119}
120
121void transport_fini(void)
122{
123 if (transport_fd >= 0)
124 closesocket(transport_fd);
125}
126
127static trans_req_t *treq_create(dns_message_t *req)
128{
129 trans_req_t *treq;
130
131 treq = calloc(1, sizeof(trans_req_t));
132 if (treq == NULL)
133 return NULL;
134
135 treq->req = req;
136 treq->resp = NULL;
137 treq->done = false;
138 fibril_condvar_initialize(&treq->done_cv);
139 fibril_mutex_initialize(&treq->done_lock);
140
141 fibril_mutex_lock(&treq_lock);
142 list_append(&treq->lreq, &treq_list);
143 fibril_mutex_unlock(&treq_lock);
144
145 return treq;
146}
147
148static void treq_destroy(trans_req_t *treq)
149{
150 if (link_in_use(&treq->lreq))
151 list_remove(&treq->lreq);
152 free(treq);
153}
154
155static trans_req_t *treq_match_resp(dns_message_t *resp)
156{
157 assert(fibril_mutex_is_locked(&treq_lock));
158
159 list_foreach(treq_list, link) {
160 trans_req_t *treq = list_get_instance(link, trans_req_t, lreq);
161
162 if (treq->req->id == resp->id) {
163 /* Match */
164 return treq;
165 }
166 }
167
168 return NULL;
169}
170
171static void treq_complete(trans_req_t *treq, dns_message_t *resp)
172{
173 fibril_mutex_lock(&treq->done_lock);
174 treq->done = true;
175 treq->status = EOK;
176 treq->resp = resp;
177 fibril_mutex_unlock(&treq->done_lock);
178
179 fibril_condvar_broadcast(&treq->done_cv);
180}
181
182int dns_request(dns_message_t *req, dns_message_t **rresp)
183{
184 int rc;
185 void *req_data;
186 size_t req_size;
187 struct sockaddr_in addr;
188 trans_req_t *treq;
189 int ntry;
190
191 req_data = NULL;
192 treq = NULL;
193
194 addr.sin_family = AF_INET;
195 addr.sin_port = htons(DNS_SERVER_PORT);
196 addr.sin_addr.s_addr = host2uint32_t_be(dns_server_addr.ipv4);
197
198 rc = dns_message_encode(req, &req_data, &req_size);
199 if (rc != EOK)
200 goto error;
201
202 ntry = 0;
203
204 while (ntry < REQ_RETRY_MAX) {
205 rc = sendto(transport_fd, req_data, req_size, 0,
206 (struct sockaddr *)&addr, sizeof(addr));
207 if (rc != EOK)
208 goto error;
209
210 treq = treq_create(req);
211 if (treq == NULL) {
212 rc = ENOMEM;
213 goto error;
214 }
215
216
217 fibril_mutex_lock(&treq->done_lock);
218 while (treq->done != true) {
219 rc = fibril_condvar_wait_timeout(&treq->done_cv, &treq->done_lock,
220 REQ_TIMEOUT);
221 if (rc == ETIMEOUT) {
222 ++ntry;
223 break;
224 }
225 }
226
227 fibril_mutex_unlock(&treq->done_lock);
228
229 if (rc != ETIMEOUT)
230 break;
231 }
232
233 if (ntry >= REQ_RETRY_MAX) {
234 rc = EIO;
235 goto error;
236 }
237
238 if (treq->status != EOK) {
239 rc = treq->status;
240 goto error;
241 }
242
243 *rresp = treq->resp;
244 treq_destroy(treq);
245 free(req_data);
246 return EOK;
247error:
248 if (treq != NULL)
249 treq_destroy(treq);
250 free(req_data);
251 return rc;
252}
253
254static int transport_recv_msg(dns_message_t **rresp)
255{
256 struct sockaddr_in src_addr;
257 socklen_t src_addr_size;
258 size_t recv_size;
259 dns_message_t *resp;
260 int rc;
261
262 src_addr_size = sizeof(src_addr);
263 rc = recvfrom(transport_fd, recv_buf, RECV_BUF_SIZE, 0,
264 (struct sockaddr *)&src_addr, &src_addr_size);
265 if (rc < 0) {
266 log_msg(LOG_DEFAULT, LVL_ERROR, "recvfrom returns error - %d", rc);
267 goto error;
268 }
269
270 recv_size = (size_t)rc;
271
272 rc = dns_message_decode(recv_buf, recv_size, &resp);
273 if (rc != EOK) {
274 rc = EIO;
275 goto error;
276 }
277
278 *rresp = resp;
279 return EOK;
280
281error:
282 return rc;
283}
284
285static int transport_recv_fibril(void *arg)
286{
287 dns_message_t *resp;
288 trans_req_t *treq;
289 int rc;
290
291 while (true) {
292 rc = transport_recv_msg(&resp);
293 if (rc != EOK)
294 continue;
295
296 fibril_mutex_lock(&treq_lock);
297 treq = treq_match_resp(resp);
298 if (treq == NULL) {
299 fibril_mutex_unlock(&treq_lock);
300 continue;
301 }
302
303 list_remove(&treq->lreq);
304 fibril_mutex_unlock(&treq_lock);
305
306 treq_complete(treq, resp);
307 }
308
309 return 0;
310}
311
312/** @}
313 */
Note: See TracBrowser for help on using the repository browser.