aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorPhil <phil.buschmann@tum.de>2018-07-02 15:42:34 +0200
committerPhil <phil.buschmann@tum.de>2018-07-02 15:42:34 +0200
commit6ede545d597509fefcc3d4fd2ef865bc5f57603f (patch)
tree3839f25c1a4b594a5da56df47b8ef5bd25880c76 /src/util
parentdb8c5cd31a92cd18450bf9d2d595a8fd314583e0 (diff)
parentbb3371542e3c1fff2c4abb4d2ba3decf925b4352 (diff)
downloadgnunet-6ede545d597509fefcc3d4fd2ef865bc5f57603f.tar.gz
gnunet-6ede545d597509fefcc3d4fd2ef865bc5f57603f.zip
-Merge branch 'master' of ssh://gnunet.org/gnunet into gsoc2018/rest_api
Diffstat (limited to 'src/util')
-rw-r--r--src/util/.gitignore4
-rw-r--r--src/util/Makefile.am37
-rw-r--r--src/util/client.c13
-rw-r--r--src/util/dnsparser.c1458
-rw-r--r--src/util/dnsstub.c747
-rw-r--r--src/util/gnunet-config.c33
-rw-r--r--src/util/gnunet-service-resolver.c1173
-rw-r--r--src/util/gnunet-timeout-w32.c191
-rw-r--r--src/util/gnunet-timeout.c128
-rw-r--r--src/util/os_priority.c6
-rw-r--r--src/util/regex.c834
-rw-r--r--src/util/resolver.h4
-rw-r--r--src/util/resolver_api.c26
-rw-r--r--src/util/strings.c39
-rw-r--r--src/util/test_hexcoder.c54
-rw-r--r--src/util/test_regex.c179
-rw-r--r--src/util/test_tun.c72
-rw-r--r--src/util/tun.c309
18 files changed, 4610 insertions, 697 deletions
diff --git a/src/util/.gitignore b/src/util/.gitignore
index 23139a1ab..7b190ca76 100644
--- a/src/util/.gitignore
+++ b/src/util/.gitignore
@@ -69,3 +69,7 @@ perf_crypto_hash
69perf_crypto_symmetric 69perf_crypto_symmetric
70perf_crypto_rsa 70perf_crypto_rsa
71perf_crypto_ecc_dlog 71perf_crypto_ecc_dlog
72test_hexcoder
73test_regex
74test_tun
75gnunet-timeout
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 4296199db..4ae073c2c 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -89,6 +89,8 @@ libgnunetutil_la_SOURCES = \
89 crypto_rsa.c \ 89 crypto_rsa.c \
90 disk.c \ 90 disk.c \
91 disk.h \ 91 disk.h \
92 dnsparser.c \
93 dnsstub.c \
92 getopt.c \ 94 getopt.c \
93 getopt_helpers.c \ 95 getopt_helpers.c \
94 helper.c \ 96 helper.c \
@@ -104,12 +106,14 @@ libgnunetutil_la_SOURCES = \
104 peer.c \ 106 peer.c \
105 plugin.c \ 107 plugin.c \
106 program.c \ 108 program.c \
109 regex.c \
107 resolver_api.c resolver.h \ 110 resolver_api.c resolver.h \
108 scheduler.c \ 111 scheduler.c \
109 service.c \ 112 service.c \
110 signal.c \ 113 signal.c \
111 strings.c \ 114 strings.c \
112 time.c \ 115 time.c \
116 tun.c \
113 speedup.c speedup.h 117 speedup.c speedup.h
114 118
115libgnunetutil_la_LIBADD = \ 119libgnunetutil_la_LIBADD = \
@@ -117,7 +121,7 @@ libgnunetutil_la_LIBADD = \
117 $(LIBGCRYPT_LIBS) \ 121 $(LIBGCRYPT_LIBS) \
118 $(LTLIBICONV) \ 122 $(LTLIBICONV) \
119 $(LTLIBINTL) \ 123 $(LTLIBINTL) \
120 -lltdl $(Z_LIBS) -lunistring $(XLIB) 124 -lltdl -lidn $(Z_LIBS) -lunistring $(XLIB)
121 125
122libgnunetutil_la_LDFLAGS = \ 126libgnunetutil_la_LDFLAGS = \
123 $(GN_LIB_LDFLAGS) \ 127 $(GN_LIB_LDFLAGS) \
@@ -162,6 +166,7 @@ lib_LTLIBRARIES = libgnunetutil.la
162 166
163libexec_PROGRAMS = \ 167libexec_PROGRAMS = \
164 gnunet-service-resolver \ 168 gnunet-service-resolver \
169 gnunet-timeout \
165 $(W32CONSOLEHELPER) 170 $(W32CONSOLEHELPER)
166 171
167bin_SCRIPTS =\ 172bin_SCRIPTS =\
@@ -188,6 +193,15 @@ endif
188endif 193endif
189 194
190 195
196if !MINGW
197gnunet_timeout_SOURCES = \
198 gnunet-timeout.c
199else
200gnunet_timeout_SOURCES = \
201 gnunet-timeout-w32.c
202endif
203
204
191do_subst = $(SED) -e 's,[@]PYTHON[@],$(PYTHON),g' 205do_subst = $(SED) -e 's,[@]PYTHON[@],$(PYTHON),g'
192 206
193gnunet-qr: gnunet-qr.py.in Makefile 207gnunet-qr: gnunet-qr.py.in Makefile
@@ -291,19 +305,22 @@ check_PROGRAMS = \
291 test_crypto_rsa \ 305 test_crypto_rsa \
292 test_disk \ 306 test_disk \
293 test_getopt \ 307 test_getopt \
308 test_hexcoder \
294 test_mq \ 309 test_mq \
295 test_os_network \ 310 test_os_network \
296 test_peer \ 311 test_peer \
297 test_plugin \ 312 test_plugin \
298 test_program \ 313 test_program \
314 test_regex \
299 test_resolver_api.nc \ 315 test_resolver_api.nc \
300 test_scheduler \ 316 test_scheduler \
301 test_scheduler_delay \ 317 test_scheduler_delay \
302 test_service \ 318 test_service \
303 test_strings \ 319 test_strings \
304 test_strings_to_data \ 320 test_strings_to_data \
305 test_time \
306 test_speedup \ 321 test_speedup \
322 test_time \
323 test_tun \
307 $(BENCHMARKS) \ 324 $(BENCHMARKS) \
308 test_os_start_process \ 325 test_os_start_process \
309 test_common_logging_runtime_loglevels 326 test_common_logging_runtime_loglevels
@@ -319,6 +336,20 @@ test_bio_SOURCES = \
319test_bio_LDADD = \ 336test_bio_LDADD = \
320 libgnunetutil.la 337 libgnunetutil.la
321 338
339test_hexcoder_SOURCES = \
340 test_hexcoder.c
341test_hexcoder_LDADD = \
342 libgnunetutil.la
343
344test_tun_SOURCES = \
345 test_tun.c
346test_tun_LDADD = \
347 libgnunetutil.la
348
349test_regex_SOURCES = \
350 test_regex.c
351test_regex_LDADD = \
352 libgnunetutil.la
322 353
323test_os_start_process_SOURCES = \ 354test_os_start_process_SOURCES = \
324 test_os_start_process.c 355 test_os_start_process.c
@@ -601,4 +632,4 @@ EXTRA_DIST = \
601 test_resolver_api_data.conf \ 632 test_resolver_api_data.conf \
602 test_service_data.conf \ 633 test_service_data.conf \
603 test_speedup_data.conf \ 634 test_speedup_data.conf \
604 gnunet-qr.py.in 635 gnunet-qr.py.in
diff --git a/src/util/client.c b/src/util/client.c
index 44e326eab..1f569255a 100644
--- a/src/util/client.c
+++ b/src/util/client.c
@@ -11,7 +11,7 @@
11 WITHOUT ANY WARRANTY; without even the implied warranty of 11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details. 13 Affero General Public License for more details.
14 14
15 You should have received a copy of the GNU Affero General Public License 15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/ 17*/
@@ -721,6 +721,17 @@ test_service_configuration (const char *service_name,
721 &unixpath)) && 721 &unixpath)) &&
722 (0 < strlen (unixpath))) 722 (0 < strlen (unixpath)))
723 ret = GNUNET_OK; 723 ret = GNUNET_OK;
724 else if ((GNUNET_OK ==
725 GNUNET_CONFIGURATION_have_value (cfg,
726 service_name,
727 "UNIXPATH")))
728 {
729 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
730 service_name,
731 "UNIXPATH",
732 _("not a valid filename"));
733 return GNUNET_SYSERR; /* UNIXPATH specified but invalid! */
734 }
724 GNUNET_free_non_null (unixpath); 735 GNUNET_free_non_null (unixpath);
725#endif 736#endif
726 737
diff --git a/src/util/dnsparser.c b/src/util/dnsparser.c
new file mode 100644
index 000000000..6fb6d657f
--- /dev/null
+++ b/src/util/dnsparser.c
@@ -0,0 +1,1458 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * @file dns/dnsparser.c
21 * @brief helper library to parse DNS packets.
22 * @author Philipp Toelke
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include <idna.h>
27#if WINDOWS
28#include <idn-free.h>
29#endif
30#include "gnunet_util_lib.h"
31
32
33/**
34 * Check if a label in UTF-8 format can be coded into valid IDNA.
35 * This can fail if the ASCII-conversion becomes longer than 63 characters.
36 *
37 * @param label label to check (UTF-8 string)
38 * @return #GNUNET_OK if the label can be converted to IDNA,
39 * #GNUNET_SYSERR if the label is not valid for DNS names
40 */
41int
42GNUNET_DNSPARSER_check_label (const char *label)
43{
44 char *output;
45 size_t slen;
46
47 if (NULL != strchr (label, '.'))
48 return GNUNET_SYSERR; /* not a label! Did you mean GNUNET_DNSPARSER_check_name? */
49 if (IDNA_SUCCESS !=
50 idna_to_ascii_8z (label, &output, IDNA_ALLOW_UNASSIGNED))
51 return GNUNET_SYSERR;
52 slen = strlen (output);
53#if WINDOWS
54 idn_free (output);
55#else
56 free (output);
57#endif
58 return (slen > 63) ? GNUNET_SYSERR : GNUNET_OK;
59}
60
61
62/**
63 * Check if a label in UTF-8 format can be coded into valid IDNA.
64 * This can fail if the ASCII-conversion becomes longer than 253 characters.
65 *
66 * @param name name to check (UTF-8 string)
67 * @return #GNUNET_OK if the label can be converted to IDNA,
68 * #GNUNET_SYSERR if the label is not valid for DNS names
69 */
70int
71GNUNET_DNSPARSER_check_name (const char *name)
72{
73 char *ldup;
74 char *output;
75 size_t slen;
76 char *tok;
77
78 ldup = GNUNET_strdup (name);
79 for (tok = strtok (ldup, "."); NULL != tok; tok = strtok (NULL, "."))
80 if (GNUNET_OK !=
81 GNUNET_DNSPARSER_check_label (tok))
82 {
83 GNUNET_free (ldup);
84 return GNUNET_SYSERR;
85 }
86 GNUNET_free (ldup);
87 if (IDNA_SUCCESS !=
88 idna_to_ascii_8z (name, &output, IDNA_ALLOW_UNASSIGNED))
89 return GNUNET_SYSERR;
90 slen = strlen (output);
91#if WINDOWS
92 idn_free (output);
93#else
94 free (output);
95#endif
96 return (slen > 253) ? GNUNET_SYSERR : GNUNET_OK;
97}
98
99
100/**
101 * Free SOA information record.
102 *
103 * @param soa record to free
104 */
105void
106GNUNET_DNSPARSER_free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
107{
108 if (NULL == soa)
109 return;
110 GNUNET_free_non_null (soa->mname);
111 GNUNET_free_non_null (soa->rname);
112 GNUNET_free (soa);
113}
114
115
116/**
117 * Free CERT information record.
118 *
119 * @param cert record to free
120 */
121void
122GNUNET_DNSPARSER_free_cert (struct GNUNET_DNSPARSER_CertRecord *cert)
123{
124 if (NULL == cert)
125 return;
126 GNUNET_free_non_null (cert->certificate_data);
127 GNUNET_free (cert);
128}
129
130
131/**
132 * Free SRV information record.
133 *
134 * @param srv record to free
135 */
136void
137GNUNET_DNSPARSER_free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
138{
139 if (NULL == srv)
140 return;
141 GNUNET_free_non_null (srv->target);
142 GNUNET_free (srv);
143}
144
145
146/**
147 * Free MX information record.
148 *
149 * @param mx record to free
150 */
151void
152GNUNET_DNSPARSER_free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
153{
154 if (NULL == mx)
155 return;
156 GNUNET_free_non_null (mx->mxhost);
157 GNUNET_free (mx);
158}
159
160
161/**
162 * Free the given DNS record.
163 *
164 * @param r record to free
165 */
166void
167GNUNET_DNSPARSER_free_record (struct GNUNET_DNSPARSER_Record *r)
168{
169 GNUNET_free_non_null (r->name);
170 switch (r->type)
171 {
172 case GNUNET_DNSPARSER_TYPE_MX:
173 GNUNET_DNSPARSER_free_mx (r->data.mx);
174 break;
175 case GNUNET_DNSPARSER_TYPE_SOA:
176 GNUNET_DNSPARSER_free_soa (r->data.soa);
177 break;
178 case GNUNET_DNSPARSER_TYPE_SRV:
179 GNUNET_DNSPARSER_free_srv (r->data.srv);
180 break;
181 case GNUNET_DNSPARSER_TYPE_CERT:
182 GNUNET_DNSPARSER_free_cert (r->data.cert);
183 break;
184 case GNUNET_DNSPARSER_TYPE_NS:
185 case GNUNET_DNSPARSER_TYPE_CNAME:
186 case GNUNET_DNSPARSER_TYPE_PTR:
187 GNUNET_free_non_null (r->data.hostname);
188 break;
189 default:
190 GNUNET_free_non_null (r->data.raw.data);
191 break;
192 }
193}
194
195
196/**
197 * Parse name inside of a DNS query or record.
198 *
199 * @param udp_payload entire UDP payload
200 * @param udp_payload_length length of @a udp_payload
201 * @param off pointer to the offset of the name to parse in the udp_payload (to be
202 * incremented by the size of the name)
203 * @param depth current depth of our recursion (to prevent stack overflow)
204 * @return name as 0-terminated C string on success, NULL if the payload is malformed
205 */
206static char *
207parse_name (const char *udp_payload,
208 size_t udp_payload_length,
209 size_t *off,
210 unsigned int depth)
211{
212 const uint8_t *input = (const uint8_t *) udp_payload;
213 char *ret;
214 char *tmp;
215 char *xstr;
216 uint8_t len;
217 size_t xoff;
218 char *utf8;
219 Idna_rc rc;
220
221 ret = GNUNET_strdup ("");
222 while (1)
223 {
224 if (*off >= udp_payload_length)
225 {
226 GNUNET_break_op (0);
227 goto error;
228 }
229 len = input[*off];
230 if (0 == len)
231 {
232 (*off)++;
233 break;
234 }
235 if (len < 64)
236 {
237 if (*off + 1 + len > udp_payload_length)
238 {
239 GNUNET_break_op (0);
240 goto error;
241 }
242 GNUNET_asprintf (&tmp,
243 "%.*s",
244 (int) len,
245 &udp_payload[*off + 1]);
246 if (IDNA_SUCCESS !=
247 (rc = idna_to_unicode_8z8z (tmp, &utf8, IDNA_ALLOW_UNASSIGNED)))
248 {
249 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
250 _("Failed to convert DNS IDNA name `%s' to UTF-8: %s\n"),
251 tmp,
252 idna_strerror (rc));
253 GNUNET_free (tmp);
254 GNUNET_asprintf (&tmp,
255 "%s%.*s.",
256 ret,
257 (int) len,
258 &udp_payload[*off + 1]);
259 }
260 else
261 {
262 GNUNET_free (tmp);
263 GNUNET_asprintf (&tmp,
264 "%s%s.",
265 ret,
266 utf8);
267#if WINDOWS
268 idn_free (utf8);
269#else
270 free (utf8);
271#endif
272 }
273 GNUNET_free (ret);
274 ret = tmp;
275 *off += 1 + len;
276 }
277 else if ((64 | 128) == (len & (64 | 128)) )
278 {
279 if (depth > 32)
280 {
281 GNUNET_break_op (0);
282 goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
283 }
284 /* pointer to string */
285 if (*off + 1 > udp_payload_length)
286 {
287 GNUNET_break_op (0);
288 goto error;
289 }
290 xoff = ((len - (64 | 128)) << 8) + input[*off+1];
291 xstr = parse_name (udp_payload,
292 udp_payload_length,
293 &xoff,
294 depth + 1);
295 if (NULL == xstr)
296 {
297 GNUNET_break_op (0);
298 goto error;
299 }
300 GNUNET_asprintf (&tmp,
301 "%s%s.",
302 ret,
303 xstr);
304 GNUNET_free (ret);
305 GNUNET_free (xstr);
306 ret = tmp;
307 if (strlen (ret) > udp_payload_length)
308 {
309 GNUNET_break_op (0);
310 goto error; /* we are looping (building an infinite string) */
311 }
312 *off += 2;
313 /* pointers always terminate names */
314 break;
315 }
316 else
317 {
318 /* neither pointer nor inline string, not supported... */
319 GNUNET_break_op (0);
320 goto error;
321 }
322 }
323 if (0 < strlen(ret))
324 ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
325 return ret;
326 error:
327 GNUNET_break_op (0);
328 GNUNET_free (ret);
329 return NULL;
330}
331
332
333/**
334 * Parse name inside of a DNS query or record.
335 *
336 * @param udp_payload entire UDP payload
337 * @param udp_payload_length length of @a udp_payload
338 * @param off pointer to the offset of the name to parse in the udp_payload (to be
339 * incremented by the size of the name)
340 * @return name as 0-terminated C string on success, NULL if the payload is malformed
341 */
342char *
343GNUNET_DNSPARSER_parse_name (const char *udp_payload,
344 size_t udp_payload_length,
345 size_t *off)
346{
347 return parse_name (udp_payload, udp_payload_length, off, 0);
348}
349
350
351/**
352 * Parse a DNS query entry.
353 *
354 * @param udp_payload entire UDP payload
355 * @param udp_payload_length length of @a udp_payload
356 * @param off pointer to the offset of the query to parse in the udp_payload (to be
357 * incremented by the size of the query)
358 * @param q where to write the query information
359 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the query is malformed
360 */
361int
362GNUNET_DNSPARSER_parse_query (const char *udp_payload,
363 size_t udp_payload_length,
364 size_t *off,
365 struct GNUNET_DNSPARSER_Query *q)
366{
367 char *name;
368 struct GNUNET_TUN_DnsQueryLine ql;
369
370 name = GNUNET_DNSPARSER_parse_name (udp_payload,
371 udp_payload_length,
372 off);
373 if (NULL == name)
374 {
375 GNUNET_break_op (0);
376 return GNUNET_SYSERR;
377 }
378 q->name = name;
379 if (*off + sizeof (struct GNUNET_TUN_DnsQueryLine) > udp_payload_length)
380 {
381 GNUNET_break_op (0);
382 return GNUNET_SYSERR;
383 }
384 GNUNET_memcpy (&ql, &udp_payload[*off], sizeof (ql));
385 *off += sizeof (ql);
386 q->type = ntohs (ql.type);
387 q->dns_traffic_class = ntohs (ql.dns_traffic_class);
388 return GNUNET_OK;
389}
390
391
392/**
393 * Parse a DNS SOA record.
394 *
395 * @param udp_payload reference to UDP packet
396 * @param udp_payload_length length of @a udp_payload
397 * @param off pointer to the offset of the query to parse in the SOA record (to be
398 * incremented by the size of the record), unchanged on error
399 * @return the parsed SOA record, NULL on error
400 */
401struct GNUNET_DNSPARSER_SoaRecord *
402GNUNET_DNSPARSER_parse_soa (const char *udp_payload,
403 size_t udp_payload_length,
404 size_t *off)
405{
406 struct GNUNET_DNSPARSER_SoaRecord *soa;
407 struct GNUNET_TUN_DnsSoaRecord soa_bin;
408 size_t old_off;
409
410 old_off = *off;
411 soa = GNUNET_new (struct GNUNET_DNSPARSER_SoaRecord);
412 soa->mname = GNUNET_DNSPARSER_parse_name (udp_payload,
413 udp_payload_length,
414 off);
415 soa->rname = GNUNET_DNSPARSER_parse_name (udp_payload,
416 udp_payload_length,
417 off);
418 if ( (NULL == soa->mname) ||
419 (NULL == soa->rname) ||
420 (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > udp_payload_length) )
421 {
422 GNUNET_break_op (0);
423 GNUNET_DNSPARSER_free_soa (soa);
424 *off = old_off;
425 return NULL;
426 }
427 GNUNET_memcpy (&soa_bin,
428 &udp_payload[*off],
429 sizeof (struct GNUNET_TUN_DnsSoaRecord));
430 soa->serial = ntohl (soa_bin.serial);
431 soa->refresh = ntohl (soa_bin.refresh);
432 soa->retry = ntohl (soa_bin.retry);
433 soa->expire = ntohl (soa_bin.expire);
434 soa->minimum_ttl = ntohl (soa_bin.minimum);
435 (*off) += sizeof (struct GNUNET_TUN_DnsSoaRecord);
436 return soa;
437}
438
439
440/**
441 * Parse a DNS MX record.
442 *
443 * @param udp_payload reference to UDP packet
444 * @param udp_payload_length length of @a udp_payload
445 * @param off pointer to the offset of the query to parse in the MX record (to be
446 * incremented by the size of the record), unchanged on error
447 * @return the parsed MX record, NULL on error
448 */
449struct GNUNET_DNSPARSER_MxRecord *
450GNUNET_DNSPARSER_parse_mx (const char *udp_payload,
451 size_t udp_payload_length,
452 size_t *off)
453{
454 struct GNUNET_DNSPARSER_MxRecord *mx;
455 uint16_t mxpref;
456 size_t old_off;
457
458 old_off = *off;
459 if (*off + sizeof (uint16_t) > udp_payload_length)
460 {
461 GNUNET_break_op (0);
462 return NULL;
463 }
464 GNUNET_memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));
465 (*off) += sizeof (uint16_t);
466 mx = GNUNET_new (struct GNUNET_DNSPARSER_MxRecord);
467 mx->preference = ntohs (mxpref);
468 mx->mxhost = GNUNET_DNSPARSER_parse_name (udp_payload,
469 udp_payload_length,
470 off);
471 if (NULL == mx->mxhost)
472 {
473 GNUNET_break_op (0);
474 GNUNET_DNSPARSER_free_mx (mx);
475 *off = old_off;
476 return NULL;
477 }
478 return mx;
479}
480
481
482/**
483 * Parse a DNS SRV record.
484 *
485 * @param udp_payload reference to UDP packet
486 * @param udp_payload_length length of @a udp_payload
487 * @param off pointer to the offset of the query to parse in the SRV record (to be
488 * incremented by the size of the record), unchanged on error
489 * @return the parsed SRV record, NULL on error
490 */
491struct GNUNET_DNSPARSER_SrvRecord *
492GNUNET_DNSPARSER_parse_srv (const char *udp_payload,
493 size_t udp_payload_length,
494 size_t *off)
495{
496 struct GNUNET_DNSPARSER_SrvRecord *srv;
497 struct GNUNET_TUN_DnsSrvRecord srv_bin;
498 size_t old_off;
499
500 old_off = *off;
501 if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > udp_payload_length)
502 return NULL;
503 GNUNET_memcpy (&srv_bin,
504 &udp_payload[*off],
505 sizeof (struct GNUNET_TUN_DnsSrvRecord));
506 (*off) += sizeof (struct GNUNET_TUN_DnsSrvRecord);
507 srv = GNUNET_new (struct GNUNET_DNSPARSER_SrvRecord);
508 srv->priority = ntohs (srv_bin.prio);
509 srv->weight = ntohs (srv_bin.weight);
510 srv->port = ntohs (srv_bin.port);
511 srv->target = GNUNET_DNSPARSER_parse_name (udp_payload,
512 udp_payload_length,
513 off);
514 if (NULL == srv->target)
515 {
516 GNUNET_DNSPARSER_free_srv (srv);
517 *off = old_off;
518 return NULL;
519 }
520 return srv;
521}
522
523
524/**
525 * Parse a DNS CERT record.
526 *
527 * @param udp_payload reference to UDP packet
528 * @param udp_payload_length length of @a udp_payload
529 * @param off pointer to the offset of the query to parse in the CERT record (to be
530 * incremented by the size of the record), unchanged on error
531 * @return the parsed CERT record, NULL on error
532 */
533struct GNUNET_DNSPARSER_CertRecord *
534GNUNET_DNSPARSER_parse_cert (const char *udp_payload,
535 size_t udp_payload_length,
536 size_t *off)
537{
538 struct GNUNET_DNSPARSER_CertRecord *cert;
539 struct GNUNET_TUN_DnsCertRecord dcert;
540
541 if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) >= udp_payload_length)
542 {
543 GNUNET_break_op (0);
544 return NULL;
545 }
546 GNUNET_memcpy (&dcert,
547 &udp_payload[*off],
548 sizeof (struct GNUNET_TUN_DnsCertRecord));
549 (*off) += sizeof (struct GNUNET_TUN_DnsCertRecord);
550 cert = GNUNET_new (struct GNUNET_DNSPARSER_CertRecord);
551 cert->cert_type = ntohs (dcert.cert_type);
552 cert->cert_tag = ntohs (dcert.cert_tag);
553 cert->algorithm = dcert.algorithm;
554 cert->certificate_size = udp_payload_length - (*off);
555 cert->certificate_data = GNUNET_malloc (cert->certificate_size);
556 GNUNET_memcpy (cert->certificate_data,
557 &udp_payload[*off],
558 cert->certificate_size);
559 (*off) += cert->certificate_size;
560 return cert;
561}
562
563
564/**
565 * Parse a DNS record entry.
566 *
567 * @param udp_payload entire UDP payload
568 * @param udp_payload_length length of @a udp_payload
569 * @param off pointer to the offset of the record to parse in the udp_payload (to be
570 * incremented by the size of the record)
571 * @param r where to write the record information
572 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the record is malformed
573 */
574int
575GNUNET_DNSPARSER_parse_record (const char *udp_payload,
576 size_t udp_payload_length,
577 size_t *off,
578 struct GNUNET_DNSPARSER_Record *r)
579{
580 char *name;
581 struct GNUNET_TUN_DnsRecordLine rl;
582 size_t old_off;
583 uint16_t data_len;
584
585 name = GNUNET_DNSPARSER_parse_name (udp_payload,
586 udp_payload_length,
587 off);
588 if (NULL == name)
589 {
590 GNUNET_break_op (0);
591 return GNUNET_SYSERR;
592 }
593 r->name = name;
594 if (*off + sizeof (struct GNUNET_TUN_DnsRecordLine) > udp_payload_length)
595 {
596 GNUNET_break_op (0);
597 return GNUNET_SYSERR;
598 }
599 GNUNET_memcpy (&rl, &udp_payload[*off], sizeof (rl));
600 (*off) += sizeof (rl);
601 r->type = ntohs (rl.type);
602 r->dns_traffic_class = ntohs (rl.dns_traffic_class);
603 r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
604 ntohl (rl.ttl)));
605 data_len = ntohs (rl.data_len);
606 if (*off + data_len > udp_payload_length)
607 {
608 GNUNET_break_op (0);
609 return GNUNET_SYSERR;
610 }
611 old_off = *off;
612 switch (r->type)
613 {
614 case GNUNET_DNSPARSER_TYPE_NS:
615 case GNUNET_DNSPARSER_TYPE_CNAME:
616 case GNUNET_DNSPARSER_TYPE_DNAME:
617 case GNUNET_DNSPARSER_TYPE_PTR:
618 r->data.hostname = GNUNET_DNSPARSER_parse_name (udp_payload,
619 udp_payload_length,
620 off);
621 if ( (NULL == r->data.hostname) ||
622 (old_off + data_len != *off) )
623 return GNUNET_SYSERR;
624 return GNUNET_OK;
625 case GNUNET_DNSPARSER_TYPE_SOA:
626 r->data.soa = GNUNET_DNSPARSER_parse_soa (udp_payload,
627 udp_payload_length,
628 off);
629 if ( (NULL == r->data.soa) ||
630 (old_off + data_len != *off) )
631 {
632 GNUNET_break_op (0);
633 return GNUNET_SYSERR;
634 }
635 return GNUNET_OK;
636 case GNUNET_DNSPARSER_TYPE_MX:
637 r->data.mx = GNUNET_DNSPARSER_parse_mx (udp_payload,
638 udp_payload_length,
639 off);
640 if ( (NULL == r->data.mx) ||
641 (old_off + data_len != *off) )
642 {
643 GNUNET_break_op (0);
644 return GNUNET_SYSERR;
645 }
646 return GNUNET_OK;
647 case GNUNET_DNSPARSER_TYPE_SRV:
648 r->data.srv = GNUNET_DNSPARSER_parse_srv (udp_payload,
649 udp_payload_length,
650 off);
651 if ( (NULL == r->data.srv) ||
652 (old_off + data_len != *off) )
653 {
654 GNUNET_break_op (0);
655 return GNUNET_SYSERR;
656 }
657 return GNUNET_OK;
658 default:
659 r->data.raw.data = GNUNET_malloc (data_len);
660 r->data.raw.data_len = data_len;
661 GNUNET_memcpy (r->data.raw.data,
662 &udp_payload[*off],
663 data_len);
664 break;
665 }
666 (*off) += data_len;
667 return GNUNET_OK;
668}
669
670
671/**
672 * Parse a UDP payload of a DNS packet in to a nice struct for further
673 * processing and manipulation.
674 *
675 * @param udp_payload wire-format of the DNS packet
676 * @param udp_payload_length number of bytes in @a udp_payload
677 * @return NULL on error, otherwise the parsed packet
678 */
679struct GNUNET_DNSPARSER_Packet *
680GNUNET_DNSPARSER_parse (const char *udp_payload,
681 size_t udp_payload_length)
682{
683 struct GNUNET_DNSPARSER_Packet *p;
684 const struct GNUNET_TUN_DnsHeader *dns;
685 size_t off;
686 unsigned int n;
687
688 if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
689 return NULL;
690 dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
691 off = sizeof (struct GNUNET_TUN_DnsHeader);
692 p = GNUNET_new (struct GNUNET_DNSPARSER_Packet);
693 p->flags = dns->flags;
694 p->id = dns->id;
695 n = ntohs (dns->query_count);
696 if (n > 0)
697 {
698 p->queries = GNUNET_new_array (n,
699 struct GNUNET_DNSPARSER_Query);
700 p->num_queries = n;
701 for (unsigned int i=0;i<n;i++)
702 if (GNUNET_OK !=
703 GNUNET_DNSPARSER_parse_query (udp_payload,
704 udp_payload_length,
705 &off,
706 &p->queries[i]))
707 goto error;
708 }
709 n = ntohs (dns->answer_rcount);
710 if (n > 0)
711 {
712 p->answers = GNUNET_new_array (n,
713 struct GNUNET_DNSPARSER_Record);
714 p->num_answers = n;
715 for (unsigned int i=0;i<n;i++)
716 if (GNUNET_OK !=
717 GNUNET_DNSPARSER_parse_record (udp_payload,
718 udp_payload_length,
719 &off,
720 &p->answers[i]))
721 goto error;
722 }
723 n = ntohs (dns->authority_rcount);
724 if (n > 0)
725 {
726 p->authority_records = GNUNET_new_array (n,
727 struct GNUNET_DNSPARSER_Record);
728 p->num_authority_records = n;
729 for (unsigned int i=0;i<n;i++)
730 if (GNUNET_OK !=
731 GNUNET_DNSPARSER_parse_record (udp_payload,
732 udp_payload_length,
733 &off,
734 &p->authority_records[i]))
735 goto error;
736 }
737 n = ntohs (dns->additional_rcount);
738 if (n > 0)
739 {
740 p->additional_records = GNUNET_new_array (n,
741 struct GNUNET_DNSPARSER_Record);
742 p->num_additional_records = n;
743 for (unsigned int i=0;i<n;i++)
744 {
745 if (GNUNET_OK !=
746 GNUNET_DNSPARSER_parse_record (udp_payload,
747 udp_payload_length,
748 &off,
749 &p->additional_records[i]))
750 goto error;
751 }
752 }
753 return p;
754 error:
755 GNUNET_break_op (0);
756 GNUNET_DNSPARSER_free_packet (p);
757 return NULL;
758}
759
760
761/**
762 * Duplicate (deep-copy) the given DNS record
763 *
764 * @param r the record
765 * @return the newly allocated record
766 */
767struct GNUNET_DNSPARSER_Record *
768GNUNET_DNSPARSER_duplicate_record (const struct GNUNET_DNSPARSER_Record *r)
769{
770 struct GNUNET_DNSPARSER_Record *dup = GNUNET_memdup (r, sizeof (*r));
771
772 dup->name = GNUNET_strdup (r->name);
773 switch (r->type)
774 {
775 case GNUNET_DNSPARSER_TYPE_NS:
776 case GNUNET_DNSPARSER_TYPE_CNAME:
777 case GNUNET_DNSPARSER_TYPE_PTR:
778 {
779 dup->data.hostname = GNUNET_strdup (r->data.hostname);
780 break;
781 }
782 case GNUNET_DNSPARSER_TYPE_SOA:
783 {
784 dup->data.soa = GNUNET_DNSPARSER_duplicate_soa_record (r->data.soa);
785 break;
786 }
787 case GNUNET_DNSPARSER_TYPE_CERT:
788 {
789 dup->data.cert = GNUNET_DNSPARSER_duplicate_cert_record (r->data.cert);
790 break;
791 }
792 case GNUNET_DNSPARSER_TYPE_MX:
793 {
794 dup->data.mx = GNUNET_DNSPARSER_duplicate_mx_record (r->data.mx);
795 break;
796 }
797 case GNUNET_DNSPARSER_TYPE_SRV:
798 {
799 dup->data.srv = GNUNET_DNSPARSER_duplicate_srv_record (r->data.srv);
800 break;
801 }
802 default:
803 {
804 dup->data.raw.data = GNUNET_memdup (r->data.raw.data,
805 r->data.raw.data_len);
806 }
807 }
808 return dup;
809}
810
811
812/**
813 * Duplicate (deep-copy) the given DNS record
814 *
815 * @param r the record
816 * @return the newly allocated record
817 */
818struct GNUNET_DNSPARSER_SoaRecord *
819GNUNET_DNSPARSER_duplicate_soa_record (const struct GNUNET_DNSPARSER_SoaRecord *r)
820{
821 struct GNUNET_DNSPARSER_SoaRecord *dup = GNUNET_memdup (r, sizeof (*r));
822
823 dup->mname = GNUNET_strdup (r->mname);
824 dup->rname = GNUNET_strdup (r->rname);
825 return dup;
826}
827
828
829/**
830 * Duplicate (deep-copy) the given DNS record
831 *
832 * @param r the record
833 * @return the newly allocated record
834 */
835struct GNUNET_DNSPARSER_CertRecord *
836GNUNET_DNSPARSER_duplicate_cert_record (const struct GNUNET_DNSPARSER_CertRecord *r)
837{
838 struct GNUNET_DNSPARSER_CertRecord *dup = GNUNET_memdup (r, sizeof (*r));
839
840 dup->certificate_data = GNUNET_strdup (r->certificate_data);
841 return dup;
842}
843
844
845/**
846 * Duplicate (deep-copy) the given DNS record
847 *
848 * @param r the record
849 * @return the newly allocated record
850 */
851struct GNUNET_DNSPARSER_MxRecord *
852GNUNET_DNSPARSER_duplicate_mx_record (const struct GNUNET_DNSPARSER_MxRecord *r)
853{
854 struct GNUNET_DNSPARSER_MxRecord *dup = GNUNET_memdup (r, sizeof (*r));
855
856 dup->mxhost = GNUNET_strdup (r->mxhost);
857 return dup;
858}
859
860
861/**
862 * Duplicate (deep-copy) the given DNS record
863 *
864 * @param r the record
865 * @return the newly allocated record
866 */
867struct GNUNET_DNSPARSER_SrvRecord *
868GNUNET_DNSPARSER_duplicate_srv_record (const struct GNUNET_DNSPARSER_SrvRecord *r)
869{
870 struct GNUNET_DNSPARSER_SrvRecord *dup = GNUNET_memdup (r, sizeof (*r));
871
872 dup->target = GNUNET_strdup (r->target);
873 return dup;
874}
875
876
877/**
878 * Free memory taken by a packet.
879 *
880 * @param p packet to free
881 */
882void
883GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
884{
885 for (unsigned int i=0;i<p->num_queries;i++)
886 GNUNET_free_non_null (p->queries[i].name);
887 GNUNET_free_non_null (p->queries);
888 for (unsigned int i=0;i<p->num_answers;i++)
889 GNUNET_DNSPARSER_free_record (&p->answers[i]);
890 GNUNET_free_non_null (p->answers);
891 for (unsigned int i=0;i<p->num_authority_records;i++)
892 GNUNET_DNSPARSER_free_record (&p->authority_records[i]);
893 GNUNET_free_non_null (p->authority_records);
894 for (unsigned int i=0;i<p->num_additional_records;i++)
895 GNUNET_DNSPARSER_free_record (&p->additional_records[i]);
896 GNUNET_free_non_null (p->additional_records);
897 GNUNET_free (p);
898}
899
900
901/* ********************** DNS packet assembly code **************** */
902
903
904/**
905 * Add a DNS name to the UDP packet at the given location, converting
906 * the name to IDNA notation as necessary.
907 *
908 * @param dst where to write the name (UDP packet)
909 * @param dst_len number of bytes in @a dst
910 * @param off pointer to offset where to write the name (increment by bytes used)
911 * must not be changed if there is an error
912 * @param name name to write
913 * @return #GNUNET_SYSERR if @a name is invalid
914 * #GNUNET_NO if @a name did not fit
915 * #GNUNET_OK if @a name was added to @a dst
916 */
917int
918GNUNET_DNSPARSER_builder_add_name (char *dst,
919 size_t dst_len,
920 size_t *off,
921 const char *name)
922{
923 const char *dot;
924 const char *idna_name;
925 char *idna_start;
926 size_t start;
927 size_t pos;
928 size_t len;
929 Idna_rc rc;
930
931 if (NULL == name)
932 return GNUNET_SYSERR;
933
934 if (IDNA_SUCCESS !=
935 (rc = idna_to_ascii_8z (name,
936 &idna_start,
937 IDNA_ALLOW_UNASSIGNED)))
938 {
939 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
940 _("Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
941 name,
942 idna_strerror (rc));
943 return GNUNET_NO;
944 }
945 idna_name = idna_start;
946 start = *off;
947 if (start + strlen (idna_name) + 2 > dst_len)
948 goto fail;
949 pos = start;
950 do
951 {
952 dot = strchr (idna_name, '.');
953 if (NULL == dot)
954 len = strlen (idna_name);
955 else
956 len = dot - idna_name;
957 if ( (len >= 64) || (0 == len) )
958 {
959 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
960 "Invalid DNS name `%s': label with %u characters encountered\n",
961 name,
962 len);
963 goto fail; /* label too long or empty */
964 }
965 dst[pos++] = (char) (uint8_t) len;
966 GNUNET_memcpy (&dst[pos],
967 idna_name,
968 len);
969 pos += len;
970 idna_name += len + 1; /* also skip dot */
971 }
972 while (NULL != dot);
973 dst[pos++] = '\0'; /* terminator */
974 *off = pos;
975#if WINDOWS
976 idn_free (idna_start);
977#else
978 free (idna_start);
979#endif
980 return GNUNET_OK;
981 fail:
982#if WINDOWS
983 idn_free (idna_start);
984#else
985 free (idna_start);
986#endif
987 return GNUNET_NO;
988}
989
990
991/**
992 * Add a DNS query to the UDP packet at the given location.
993 *
994 * @param dst where to write the query
995 * @param dst_len number of bytes in @a dst
996 * @param off pointer to offset where to write the query (increment by bytes used)
997 * must not be changed if there is an error
998 * @param query query to write
999 * @return #GNUNET_SYSERR if @a query is invalid
1000 * #GNUNET_NO if @a query did not fit
1001 * #GNUNET_OK if @a query was added to @a dst
1002 */
1003int
1004GNUNET_DNSPARSER_builder_add_query (char *dst,
1005 size_t dst_len,
1006 size_t *off,
1007 const struct GNUNET_DNSPARSER_Query *query)
1008{
1009 int ret;
1010 struct GNUNET_TUN_DnsQueryLine ql;
1011
1012 ret = GNUNET_DNSPARSER_builder_add_name (dst, dst_len - sizeof (struct GNUNET_TUN_DnsQueryLine), off, query->name);
1013 if (ret != GNUNET_OK)
1014 return ret;
1015 ql.type = htons (query->type);
1016 ql.dns_traffic_class = htons (query->dns_traffic_class);
1017 GNUNET_memcpy (&dst[*off], &ql, sizeof (ql));
1018 (*off) += sizeof (ql);
1019 return GNUNET_OK;
1020}
1021
1022
1023/**
1024 * Add an MX record to the UDP packet at the given location.
1025 *
1026 * @param dst where to write the mx record
1027 * @param dst_len number of bytes in @a dst
1028 * @param off pointer to offset where to write the mx information (increment by bytes used);
1029 * can also change if there was an error
1030 * @param mx mx information to write
1031 * @return #GNUNET_SYSERR if @a mx is invalid
1032 * #GNUNET_NO if @a mx did not fit
1033 * #GNUNET_OK if @a mx was added to @a dst
1034 */
1035int
1036GNUNET_DNSPARSER_builder_add_mx (char *dst,
1037 size_t dst_len,
1038 size_t *off,
1039 const struct GNUNET_DNSPARSER_MxRecord *mx)
1040{
1041 uint16_t mxpref;
1042
1043 if (*off + sizeof (uint16_t) > dst_len)
1044 return GNUNET_NO;
1045 mxpref = htons (mx->preference);
1046 GNUNET_memcpy (&dst[*off],
1047 &mxpref,
1048 sizeof (mxpref));
1049 (*off) += sizeof (mxpref);
1050 return GNUNET_DNSPARSER_builder_add_name (dst,
1051 dst_len,
1052 off,
1053 mx->mxhost);
1054}
1055
1056
1057/**
1058 * Add a CERT record to the UDP packet at the given location.
1059 *
1060 * @param dst where to write the CERT record
1061 * @param dst_len number of bytes in @a dst
1062 * @param off pointer to offset where to write the CERT information (increment by bytes used);
1063 * can also change if there was an error
1064 * @param cert CERT information to write
1065 * @return #GNUNET_SYSERR if @a cert is invalid
1066 * #GNUNET_NO if @a cert did not fit
1067 * #GNUNET_OK if @a cert was added to @a dst
1068 */
1069int
1070GNUNET_DNSPARSER_builder_add_cert (char *dst,
1071 size_t dst_len,
1072 size_t *off,
1073 const struct GNUNET_DNSPARSER_CertRecord *cert)
1074{
1075 struct GNUNET_TUN_DnsCertRecord dcert;
1076
1077 if ( (cert->cert_type > UINT16_MAX) ||
1078 (cert->algorithm > UINT8_MAX) )
1079 {
1080 GNUNET_break (0);
1081 return GNUNET_SYSERR;
1082 }
1083 if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) + cert->certificate_size > dst_len)
1084 return GNUNET_NO;
1085 dcert.cert_type = htons ((uint16_t) cert->cert_type);
1086 dcert.cert_tag = htons ((uint16_t) cert->cert_tag);
1087 dcert.algorithm = (uint8_t) cert->algorithm;
1088 GNUNET_memcpy (&dst[*off], &dcert, sizeof (dcert));
1089 (*off) += sizeof (dcert);
1090 GNUNET_memcpy (&dst[*off], cert->certificate_data, cert->certificate_size);
1091 (*off) += cert->certificate_size;
1092 return GNUNET_OK;
1093}
1094
1095
1096/**
1097 * Add an SOA record to the UDP packet at the given location.
1098 *
1099 * @param dst where to write the SOA record
1100 * @param dst_len number of bytes in @a dst
1101 * @param off pointer to offset where to write the SOA information (increment by bytes used)
1102 * can also change if there was an error
1103 * @param soa SOA information to write
1104 * @return #GNUNET_SYSERR if @a soa is invalid
1105 * #GNUNET_NO if @a soa did not fit
1106 * #GNUNET_OK if @a soa was added to @a dst
1107 */
1108int
1109GNUNET_DNSPARSER_builder_add_soa (char *dst,
1110 size_t dst_len,
1111 size_t *off,
1112 const struct GNUNET_DNSPARSER_SoaRecord *soa)
1113{
1114 struct GNUNET_TUN_DnsSoaRecord sd;
1115 int ret;
1116
1117 if ( (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1118 dst_len,
1119 off,
1120 soa->mname))) ||
1121 (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1122 dst_len,
1123 off,
1124 soa->rname)) ) )
1125 return ret;
1126 if (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > dst_len)
1127 return GNUNET_NO;
1128 sd.serial = htonl (soa->serial);
1129 sd.refresh = htonl (soa->refresh);
1130 sd.retry = htonl (soa->retry);
1131 sd.expire = htonl (soa->expire);
1132 sd.minimum = htonl (soa->minimum_ttl);
1133 GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
1134 (*off) += sizeof (sd);
1135 return GNUNET_OK;
1136}
1137
1138
1139/**
1140 * Add an SRV record to the UDP packet at the given location.
1141 *
1142 * @param dst where to write the SRV record
1143 * @param dst_len number of bytes in @a dst
1144 * @param off pointer to offset where to write the SRV information (increment by bytes used)
1145 * can also change if there was an error
1146 * @param srv SRV information to write
1147 * @return #GNUNET_SYSERR if @a srv is invalid
1148 * #GNUNET_NO if @a srv did not fit
1149 * #GNUNET_OK if @a srv was added to @a dst
1150 */
1151int
1152GNUNET_DNSPARSER_builder_add_srv (char *dst,
1153 size_t dst_len,
1154 size_t *off,
1155 const struct GNUNET_DNSPARSER_SrvRecord *srv)
1156{
1157 struct GNUNET_TUN_DnsSrvRecord sd;
1158 int ret;
1159
1160 if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > dst_len)
1161 return GNUNET_NO;
1162 sd.prio = htons (srv->priority);
1163 sd.weight = htons (srv->weight);
1164 sd.port = htons (srv->port);
1165 GNUNET_memcpy (&dst[*off],
1166 &sd,
1167 sizeof (sd));
1168 (*off) += sizeof (sd);
1169 if (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1170 dst_len,
1171 off,
1172 srv->target)))
1173 return ret;
1174 return GNUNET_OK;
1175}
1176
1177
1178/**
1179 * Add a DNS record to the UDP packet at the given location.
1180 *
1181 * @param dst where to write the query
1182 * @param dst_len number of bytes in @a dst
1183 * @param off pointer to offset where to write the query (increment by bytes used)
1184 * must not be changed if there is an error
1185 * @param record record to write
1186 * @return #GNUNET_SYSERR if @a record is invalid
1187 * #GNUNET_NO if @a record did not fit
1188 * #GNUNET_OK if @a record was added to @a dst
1189 */
1190static int
1191add_record (char *dst,
1192 size_t dst_len,
1193 size_t *off,
1194 const struct GNUNET_DNSPARSER_Record *record)
1195{
1196 int ret;
1197 size_t start;
1198 size_t pos;
1199 struct GNUNET_TUN_DnsRecordLine rl;
1200
1201 start = *off;
1202 ret = GNUNET_DNSPARSER_builder_add_name (dst,
1203 dst_len - sizeof (struct GNUNET_TUN_DnsRecordLine),
1204 off,
1205 record->name);
1206 if (GNUNET_OK != ret)
1207 return ret;
1208 /* '*off' is now the position where we will need to write the record line */
1209
1210 pos = *off + sizeof (struct GNUNET_TUN_DnsRecordLine);
1211 switch (record->type)
1212 {
1213 case GNUNET_DNSPARSER_TYPE_MX:
1214 ret = GNUNET_DNSPARSER_builder_add_mx (dst,
1215 dst_len,
1216 &pos,
1217 record->data.mx);
1218 break;
1219 case GNUNET_DNSPARSER_TYPE_CERT:
1220 ret = GNUNET_DNSPARSER_builder_add_cert (dst,
1221 dst_len,
1222 &pos,
1223 record->data.cert);
1224 break;
1225 case GNUNET_DNSPARSER_TYPE_SOA:
1226 ret = GNUNET_DNSPARSER_builder_add_soa (dst,
1227 dst_len,
1228 &pos,
1229 record->data.soa);
1230 break;
1231 case GNUNET_DNSPARSER_TYPE_NS:
1232 case GNUNET_DNSPARSER_TYPE_CNAME:
1233 case GNUNET_DNSPARSER_TYPE_PTR:
1234 ret = GNUNET_DNSPARSER_builder_add_name (dst,
1235 dst_len,
1236 &pos,
1237 record->data.hostname);
1238 break;
1239 case GNUNET_DNSPARSER_TYPE_SRV:
1240 ret = GNUNET_DNSPARSER_builder_add_srv (dst,
1241 dst_len,
1242 &pos,
1243 record->data.srv);
1244 break;
1245 default:
1246 if (pos + record->data.raw.data_len > dst_len)
1247 {
1248 ret = GNUNET_NO;
1249 break;
1250 }
1251 GNUNET_memcpy (&dst[pos],
1252 record->data.raw.data,
1253 record->data.raw.data_len);
1254 pos += record->data.raw.data_len;
1255 ret = GNUNET_OK;
1256 break;
1257 }
1258 if (GNUNET_OK != ret)
1259 {
1260 *off = start;
1261 return GNUNET_NO;
1262 }
1263
1264 if (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine)) > UINT16_MAX)
1265 {
1266 /* record data too long */
1267 *off = start;
1268 return GNUNET_NO;
1269 }
1270 rl.type = htons (record->type);
1271 rl.dns_traffic_class = htons (record->dns_traffic_class);
1272 rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us / 1000LL / 1000LL); /* in seconds */
1273 rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine))));
1274 GNUNET_memcpy (&dst[*off],
1275 &rl,
1276 sizeof (struct GNUNET_TUN_DnsRecordLine));
1277 *off = pos;
1278 return GNUNET_OK;
1279}
1280
1281
1282/**
1283 * Given a DNS packet @a p, generate the corresponding UDP payload.
1284 * Note that we do not attempt to pack the strings with pointers
1285 * as this would complicate the code and this is about being
1286 * simple and secure, not fast, fancy and broken like bind.
1287 *
1288 * @param p packet to pack
1289 * @param max maximum allowed size for the resulting UDP payload
1290 * @param buf set to a buffer with the packed message
1291 * @param buf_length set to the length of @a buf
1292 * @return #GNUNET_SYSERR if @a p is invalid
1293 * #GNUNET_NO if @a p was truncated (but there is still a result in @a buf)
1294 * #GNUNET_OK if @a p was packed completely into @a buf
1295 */
1296int
1297GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
1298 uint16_t max,
1299 char **buf,
1300 size_t *buf_length)
1301{
1302 struct GNUNET_TUN_DnsHeader dns;
1303 size_t off;
1304 char tmp[max];
1305 int ret;
1306 int trc;
1307
1308 if ( (p->num_queries > UINT16_MAX) ||
1309 (p->num_answers > UINT16_MAX) ||
1310 (p->num_authority_records > UINT16_MAX) ||
1311 (p->num_additional_records > UINT16_MAX) )
1312 return GNUNET_SYSERR;
1313 dns.id = p->id;
1314 dns.flags = p->flags;
1315 dns.query_count = htons (p->num_queries);
1316 dns.answer_rcount = htons (p->num_answers);
1317 dns.authority_rcount = htons (p->num_authority_records);
1318 dns.additional_rcount = htons (p->num_additional_records);
1319
1320 off = sizeof (struct GNUNET_TUN_DnsHeader);
1321 trc = GNUNET_NO;
1322 for (unsigned int i=0;i<p->num_queries;i++)
1323 {
1324 ret = GNUNET_DNSPARSER_builder_add_query (tmp,
1325 sizeof (tmp),
1326 &off,
1327 &p->queries[i]);
1328 if (GNUNET_SYSERR == ret)
1329 return GNUNET_SYSERR;
1330 if (GNUNET_NO == ret)
1331 {
1332 dns.query_count = htons ((uint16_t) (i-1));
1333 trc = GNUNET_YES;
1334 break;
1335 }
1336 }
1337 for (unsigned int i=0;i<p->num_answers;i++)
1338 {
1339 ret = add_record (tmp,
1340 sizeof (tmp),
1341 &off,
1342 &p->answers[i]);
1343 if (GNUNET_SYSERR == ret)
1344 return GNUNET_SYSERR;
1345 if (GNUNET_NO == ret)
1346 {
1347 dns.answer_rcount = htons ((uint16_t) (i-1));
1348 trc = GNUNET_YES;
1349 break;
1350 }
1351 }
1352 for (unsigned int i=0;i<p->num_authority_records;i++)
1353 {
1354 ret = add_record (tmp,
1355 sizeof (tmp),
1356 &off,
1357 &p->authority_records[i]);
1358 if (GNUNET_SYSERR == ret)
1359 return GNUNET_SYSERR;
1360 if (GNUNET_NO == ret)
1361 {
1362 dns.authority_rcount = htons ((uint16_t) (i-1));
1363 trc = GNUNET_YES;
1364 break;
1365 }
1366 }
1367 for (unsigned int i=0;i<p->num_additional_records;i++)
1368 {
1369 ret = add_record (tmp,
1370 sizeof (tmp),
1371 &off,
1372 &p->additional_records[i]);
1373 if (GNUNET_SYSERR == ret)
1374 return GNUNET_SYSERR;
1375 if (GNUNET_NO == ret)
1376 {
1377 dns.additional_rcount = htons (i-1);
1378 trc = GNUNET_YES;
1379 break;
1380 }
1381 }
1382
1383 if (GNUNET_YES == trc)
1384 dns.flags.message_truncated = 1;
1385 GNUNET_memcpy (tmp,
1386 &dns,
1387 sizeof (struct GNUNET_TUN_DnsHeader));
1388
1389 *buf = GNUNET_malloc (off);
1390 *buf_length = off;
1391 GNUNET_memcpy (*buf,
1392 tmp,
1393 off);
1394 if (GNUNET_YES == trc)
1395 return GNUNET_NO;
1396 return GNUNET_OK;
1397}
1398
1399
1400/**
1401 * Convert a block of binary data to HEX.
1402 *
1403 * @param data binary data to convert
1404 * @param data_size number of bytes in @a data
1405 * @return HEX string (lower case)
1406 */
1407char *
1408GNUNET_DNSPARSER_bin_to_hex (const void *data,
1409 size_t data_size)
1410{
1411 char *ret;
1412 size_t off;
1413 const uint8_t *idata;
1414
1415 idata = data;
1416 ret = GNUNET_malloc (data_size * 2 + 1);
1417 for (off = 0; off < data_size; off++)
1418 sprintf (&ret[off * 2],
1419 "%02x",
1420 idata[off]);
1421 return ret;
1422}
1423
1424
1425/**
1426 * Convert a HEX string to block of binary data.
1427 *
1428 * @param hex HEX string to convert (may contain mixed case)
1429 * @param data where to write result, must be
1430 * at least `strlen(hex)/2` bytes long
1431 * @return number of bytes written to data
1432 */
1433size_t
1434GNUNET_DNSPARSER_hex_to_bin (const char *hex,
1435 void *data)
1436{
1437 size_t data_size;
1438 size_t off;
1439 uint8_t *idata;
1440 unsigned int h;
1441 char in[3];
1442
1443 data_size = strlen (hex) / 2;
1444 idata = data;
1445 in[2] = '\0';
1446 for (off = 0; off < data_size; off++)
1447 {
1448 in[0] = tolower ((unsigned char) hex[off * 2]);
1449 in[1] = tolower ((unsigned char) hex[off * 2 + 1]);
1450 if (1 != sscanf (in, "%x", &h))
1451 return off;
1452 idata[off] = (uint8_t) h;
1453 }
1454 return off;
1455}
1456
1457
1458/* end of dnsparser.c */
diff --git a/src/util/dnsstub.c b/src/util/dnsstub.c
new file mode 100644
index 000000000..5b84e6e63
--- /dev/null
+++ b/src/util/dnsstub.c
@@ -0,0 +1,747 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2018 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18/**
19 * @file dns/dnsstub.c
20 * @brief DNS stub resolver which sends DNS requests to an actual resolver
21 * @author Christian Grothoff
22 */
23#include "platform.h"
24#include "gnunet_util_lib.h"
25
26/**
27 * Timeout for retrying DNS queries.
28 */
29#define DNS_RETRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
30
31
32/**
33 * DNS Server used for resolution.
34 */
35struct DnsServer;
36
37
38/**
39 * UDP socket we are using for sending DNS requests to the Internet.
40 */
41struct GNUNET_DNSSTUB_RequestSocket
42{
43
44 /**
45 * UDP socket we use for this request for IPv4
46 */
47 struct GNUNET_NETWORK_Handle *dnsout4;
48
49 /**
50 * UDP socket we use for this request for IPv6
51 */
52 struct GNUNET_NETWORK_Handle *dnsout6;
53
54 /**
55 * Function to call with result.
56 */
57 GNUNET_DNSSTUB_ResultCallback rc;
58
59 /**
60 * Closure for @e rc.
61 */
62 void *rc_cls;
63
64 /**
65 * Task for reading from dnsout4 and dnsout6.
66 */
67 struct GNUNET_SCHEDULER_Task *read_task;
68
69 /**
70 * Task for retrying transmission of the query.
71 */
72 struct GNUNET_SCHEDULER_Task *retry_task;
73
74 /**
75 * Next address we sent the DNS request to.
76 */
77 struct DnsServer *ds_pos;
78
79 /**
80 * Context this request executes in.
81 */
82 struct GNUNET_DNSSTUB_Context *ctx;
83
84 /**
85 * Query we sent to @e addr.
86 */
87 void *request;
88
89 /**
90 * Number of bytes in @a request.
91 */
92 size_t request_len;
93
94};
95
96
97/**
98 * DNS Server used for resolution.
99 */
100struct DnsServer
101{
102
103 /**
104 * Kept in a DLL.
105 */
106 struct DnsServer *next;
107
108 /**
109 * Kept in a DLL.
110 */
111 struct DnsServer *prev;
112
113 /**
114 * IP address of the DNS resolver.
115 */
116 struct sockaddr_storage ss;
117};
118
119
120/**
121 * Handle to the stub resolver.
122 */
123struct GNUNET_DNSSTUB_Context
124{
125
126 /**
127 * Array of all open sockets for DNS requests.
128 */
129 struct GNUNET_DNSSTUB_RequestSocket *sockets;
130
131 /**
132 * DLL of DNS resolvers we use.
133 */
134 struct DnsServer *dns_head;
135
136 /**
137 * DLL of DNS resolvers we use.
138 */
139 struct DnsServer *dns_tail;
140
141 /**
142 * How frequently do we retry requests?
143 */
144 struct GNUNET_TIME_Relative retry_freq;
145
146 /**
147 * Length of @e sockets array.
148 */
149 unsigned int num_sockets;
150
151};
152
153
154/**
155 * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
156 *
157 * @param rs request socket to clean up
158 */
159static void
160cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
161{
162 if (NULL != rs->dnsout4)
163 {
164 GNUNET_NETWORK_socket_close (rs->dnsout4);
165 rs->dnsout4 = NULL;
166 }
167 if (NULL != rs->dnsout6)
168 {
169 GNUNET_NETWORK_socket_close (rs->dnsout6);
170 rs->dnsout6 = NULL;
171 }
172 if (NULL != rs->read_task)
173 {
174 GNUNET_SCHEDULER_cancel (rs->read_task);
175 rs->read_task = NULL;
176 }
177 if (NULL != rs->retry_task)
178 {
179 GNUNET_SCHEDULER_cancel (rs->retry_task);
180 rs->retry_task = NULL;
181 }
182 if (NULL != rs->request)
183 {
184 GNUNET_free (rs->request);
185 rs->request = NULL;
186 }
187}
188
189
190/**
191 * Open source port for sending DNS requests
192 *
193 * @param af AF_INET or AF_INET6
194 * @return #GNUNET_OK on success
195 */
196static struct GNUNET_NETWORK_Handle *
197open_socket (int af)
198{
199 struct sockaddr_in a4;
200 struct sockaddr_in6 a6;
201 struct sockaddr *sa;
202 socklen_t alen;
203 struct GNUNET_NETWORK_Handle *ret;
204
205 ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
206 if (NULL == ret)
207 return NULL;
208 switch (af)
209 {
210 case AF_INET:
211 memset (&a4, 0, alen = sizeof (struct sockaddr_in));
212 sa = (struct sockaddr *) &a4;
213 break;
214 case AF_INET6:
215 memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
216 sa = (struct sockaddr *) &a6;
217 break;
218 default:
219 GNUNET_break (0);
220 GNUNET_NETWORK_socket_close (ret);
221 return NULL;
222 }
223 sa->sa_family = af;
224 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
225 sa,
226 alen))
227 {
228 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
229 _("Could not bind to any port: %s\n"),
230 STRERROR (errno));
231 GNUNET_NETWORK_socket_close (ret);
232 return NULL;
233 }
234 return ret;
235}
236
237
238/**
239 * Get a socket of the specified address family to send out a
240 * UDP DNS request to the Internet.
241 *
242 * @param ctx the DNSSTUB context
243 * @return NULL on error
244 */
245static struct GNUNET_DNSSTUB_RequestSocket *
246get_request_socket (struct GNUNET_DNSSTUB_Context *ctx)
247{
248 struct GNUNET_DNSSTUB_RequestSocket *rs;
249
250 for (unsigned int i=0;i<256;i++)
251 {
252 rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
253 ctx->num_sockets)];
254 if (NULL == rs->rc)
255 break;
256 }
257 if (NULL != rs->rc)
258 {
259 /* signal "failure" */
260 rs->rc (rs->rc_cls,
261 NULL,
262 0);
263 rs->rc = NULL;
264 }
265 if (NULL != rs->read_task)
266 {
267 GNUNET_SCHEDULER_cancel (rs->read_task);
268 rs->read_task = NULL;
269 }
270 if (NULL != rs->retry_task)
271 {
272 GNUNET_SCHEDULER_cancel (rs->retry_task);
273 rs->retry_task = NULL;
274 }
275 if (NULL != rs->request)
276 {
277 GNUNET_free (rs->request);
278 rs->request = NULL;
279 }
280 rs->ctx = ctx;
281 return rs;
282}
283
284
285/**
286 * Actually do the reading of a DNS packet from our UDP socket and see
287 * if we have a valid, matching, pending request.
288 *
289 * @param rs request socket with callback details
290 * @param dnsout socket to read from
291 * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
292 */
293static int
294do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
295 struct GNUNET_NETWORK_Handle *dnsout)
296{
297 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
298 ssize_t r;
299 int len;
300
301#ifndef MINGW
302 if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout),
303 FIONREAD,
304 &len))
305 {
306 /* conservative choice: */
307 len = UINT16_MAX;
308 }
309#else
310 /* port the code above? */
311 len = UINT16_MAX;
312#endif
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314 "Receiving %d byte DNS reply\n",
315 len);
316 {
317 unsigned char buf[len] GNUNET_ALIGN;
318 int found;
319 struct sockaddr_storage addr;
320 socklen_t addrlen;
321 struct GNUNET_TUN_DnsHeader *dns;
322
323 addrlen = sizeof (addr);
324 memset (&addr,
325 0,
326 sizeof (addr));
327 r = GNUNET_NETWORK_socket_recvfrom (dnsout,
328 buf,
329 sizeof (buf),
330 (struct sockaddr*) &addr,
331 &addrlen);
332 if (-1 == r)
333 {
334 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
335 "recvfrom");
336 GNUNET_NETWORK_socket_close (dnsout);
337 return GNUNET_SYSERR;
338 }
339 found = GNUNET_NO;
340 for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
341 {
342 if (0 == memcmp (&addr,
343 &ds->ss,
344 GNUNET_MIN (sizeof (struct sockaddr_storage),
345 addrlen)))
346 {
347 found = GNUNET_YES;
348 break;
349 }
350 }
351 if (GNUNET_NO == found)
352 {
353 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354 "Received DNS response from server we never asked (ignored)");
355 return GNUNET_NO;
356 }
357 if (sizeof (struct GNUNET_TUN_DnsHeader) > (size_t) r)
358 {
359 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
360 _("Received DNS response that is too small (%u bytes)"),
361 (unsigned int) r);
362 return GNUNET_NO;
363 }
364 dns = (struct GNUNET_TUN_DnsHeader *) buf;
365 if (NULL == rs->rc)
366 {
367 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368 "Request timeout or cancelled; ignoring reply\n");
369 return GNUNET_NO;
370 }
371 rs->rc (rs->rc_cls,
372 dns,
373 r);
374 }
375 return GNUNET_OK;
376}
377
378
379/**
380 * Read a DNS response from the (unhindered) UDP-Socket
381 *
382 * @param cls socket to read from
383 */
384static void
385read_response (void *cls);
386
387
388/**
389 * Schedule #read_response() task for @a rs.
390 *
391 * @param rs request to schedule read operation for
392 */
393static void
394schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs)
395{
396 struct GNUNET_NETWORK_FDSet *rset;
397
398 if (NULL != rs->read_task)
399 GNUNET_SCHEDULER_cancel (rs->read_task);
400 rset = GNUNET_NETWORK_fdset_create ();
401 if (NULL != rs->dnsout4)
402 GNUNET_NETWORK_fdset_set (rset,
403 rs->dnsout4);
404 if (NULL != rs->dnsout6)
405 GNUNET_NETWORK_fdset_set (rset,
406 rs->dnsout6);
407 rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
408 GNUNET_TIME_UNIT_FOREVER_REL,
409 rset,
410 NULL,
411 &read_response,
412 rs);
413 GNUNET_NETWORK_fdset_destroy (rset);
414}
415
416
417/**
418 * Read a DNS response from the (unhindered) UDP-Socket
419 *
420 * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
421 */
422static void
423read_response (void *cls)
424{
425 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
426 const struct GNUNET_SCHEDULER_TaskContext *tc;
427
428 rs->read_task = NULL;
429 tc = GNUNET_SCHEDULER_get_task_context ();
430 /* read and process ready sockets */
431 if ( (NULL != rs->dnsout4) &&
432 (GNUNET_NETWORK_fdset_isset (tc->read_ready,
433 rs->dnsout4)) &&
434 (GNUNET_SYSERR ==
435 do_dns_read (rs,
436 rs->dnsout4)) )
437 rs->dnsout4 = NULL;
438 if ( (NULL != rs->dnsout6) &&
439 (GNUNET_NETWORK_fdset_isset (tc->read_ready,
440 rs->dnsout6)) &&
441 (GNUNET_SYSERR ==
442 do_dns_read (rs,
443 rs->dnsout6)) )
444 rs->dnsout6 = NULL;
445 /* re-schedule read task */
446 schedule_read (rs);
447}
448
449
450/**
451 * Task to (re)transmit the DNS query, possibly repeatedly until
452 * we succeed.
453 *
454 * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
455 */
456static void
457transmit_query (void *cls)
458{
459 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
460 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
461 const struct sockaddr *sa;
462 socklen_t salen;
463 struct DnsServer *ds;
464 struct GNUNET_NETWORK_Handle *dnsout;
465
466 rs->retry_task = GNUNET_SCHEDULER_add_delayed (ctx->retry_freq,
467 &transmit_query,
468 rs);
469 ds = rs->ds_pos;
470 rs->ds_pos = ds->next;
471 if (NULL == rs->ds_pos)
472 rs->ds_pos = ctx->dns_head;
473 GNUNET_assert (NULL != ds);
474 dnsout = NULL;
475 switch (ds->ss.ss_family)
476 {
477 case AF_INET:
478 if (NULL == rs->dnsout4)
479 rs->dnsout4 = open_socket (AF_INET);
480 dnsout = rs->dnsout4;
481 sa = (const struct sockaddr *) &ds->ss;
482 salen = sizeof (struct sockaddr_in);
483 break;
484 case AF_INET6:
485 if (NULL == rs->dnsout6)
486 rs->dnsout6 = open_socket (AF_INET6);
487 dnsout = rs->dnsout6;
488 sa = (const struct sockaddr *) &ds->ss;
489 salen = sizeof (struct sockaddr_in6);
490 break;
491 default:
492 return;
493 }
494 if (NULL == dnsout)
495 {
496 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
497 "Unable to use configure DNS server, skipping\n");
498 return;
499 }
500 if (GNUNET_SYSERR ==
501 GNUNET_NETWORK_socket_sendto (dnsout,
502 rs->request,
503 rs->request_len,
504 sa,
505 salen))
506 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
507 _("Failed to send DNS request to %s: %s\n"),
508 GNUNET_a2s (sa,
509 salen),
510 STRERROR (errno));
511 else
512 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
513 _("Sent DNS request to %s\n"),
514 GNUNET_a2s (sa,
515 salen));
516 schedule_read (rs);
517}
518
519
520/**
521 * Perform DNS resolution using our default IP from init.
522 *
523 * @param ctx stub resolver to use
524 * @param request DNS request to transmit
525 * @param request_len number of bytes in msg
526 * @param rc function to call with result
527 * @param rc_cls closure for 'rc'
528 * @return socket used for the request, NULL on error
529 */
530struct GNUNET_DNSSTUB_RequestSocket *
531GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
532 const void *request,
533 size_t request_len,
534 GNUNET_DNSSTUB_ResultCallback rc,
535 void *rc_cls)
536{
537 struct GNUNET_DNSSTUB_RequestSocket *rs;
538
539 if (NULL == ctx->dns_head)
540 {
541 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
542 "No DNS server configured for resolution\n");
543 return NULL;
544 }
545 if (NULL == (rs = get_request_socket (ctx)))
546 {
547 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
548 "No request socket available for DNS resolution\n");
549 return NULL;
550 }
551 rs->ds_pos = ctx->dns_head;
552 rs->rc = rc;
553 rs->rc_cls = rc_cls;
554 rs->request = GNUNET_memdup (request,
555 request_len);
556 rs->request_len = request_len;
557 rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query,
558 rs);
559 return rs;
560}
561
562
563/**
564 * Cancel DNS resolution.
565 *
566 * @param rs resolution to cancel
567 */
568void
569GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
570{
571 rs->rc = NULL;
572 if (NULL != rs->retry_task)
573 {
574 GNUNET_SCHEDULER_cancel (rs->retry_task);
575 rs->retry_task = NULL;
576 }
577 if (NULL != rs->read_task)
578 {
579 GNUNET_SCHEDULER_cancel (rs->read_task);
580 rs->read_task = NULL;
581 }
582}
583
584
585/**
586 * Start a DNS stub resolver.
587 *
588 * @param num_sockets how many sockets should we open
589 * in parallel for DNS queries for this stub?
590 * @return NULL on error
591 */
592struct GNUNET_DNSSTUB_Context *
593GNUNET_DNSSTUB_start (unsigned int num_sockets)
594{
595 struct GNUNET_DNSSTUB_Context *ctx;
596
597 if (0 == num_sockets)
598 {
599 GNUNET_break (0);
600 return NULL;
601 }
602 ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
603 ctx->num_sockets = num_sockets;
604 ctx->sockets = GNUNET_new_array (num_sockets,
605 struct GNUNET_DNSSTUB_RequestSocket);
606 ctx->retry_freq = DNS_RETRANSMIT_DELAY;
607 return ctx;
608}
609
610
611/**
612 * Add nameserver for use by the DNSSTUB. We will use
613 * all provided nameservers for resolution (round-robin).
614 *
615 * @param ctx resolver context to modify
616 * @param dns_ip target IP address to use (as string)
617 * @return #GNUNET_OK on success
618 */
619int
620GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
621 const char *dns_ip)
622{
623 struct DnsServer *ds;
624 struct in_addr i4;
625 struct in6_addr i6;
626
627 ds = GNUNET_new (struct DnsServer);
628 if (1 == inet_pton (AF_INET,
629 dns_ip,
630 &i4))
631 {
632 struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss;
633
634 s4->sin_family = AF_INET;
635 s4->sin_port = htons (53);
636 s4->sin_addr = i4;
637#if HAVE_SOCKADDR_IN_SIN_LEN
638 s4->sin_len = (u_char) sizeof (struct sockaddr_in);
639#endif
640 }
641 else if (1 == inet_pton (AF_INET6,
642 dns_ip,
643 &i6))
644 {
645 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss;
646
647 s6->sin6_family = AF_INET6;
648 s6->sin6_port = htons (53);
649 s6->sin6_addr = i6;
650#if HAVE_SOCKADDR_IN_SIN_LEN
651 s6->sin6_len = (u_char) sizeof (struct sockaddr_in6);
652#endif
653 }
654 else
655 {
656 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
657 "Malformed IP address `%s' for DNS server\n",
658 dns_ip);
659 GNUNET_free (ds);
660 return GNUNET_SYSERR;
661 }
662 GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
663 ctx->dns_tail,
664 ds);
665 return GNUNET_OK;
666}
667
668
669/**
670 * Add nameserver for use by the DNSSTUB. We will use
671 * all provided nameservers for resolution (round-robin).
672 *
673 * @param ctx resolver context to modify
674 * @param sa socket address of DNS resolver to use
675 * @return #GNUNET_OK on success
676 */
677int
678GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
679 const struct sockaddr *sa)
680{
681 struct DnsServer *ds;
682
683 ds = GNUNET_new (struct DnsServer);
684 switch (sa->sa_family)
685 {
686 case AF_INET:
687 GNUNET_memcpy (&ds->ss,
688 sa,
689 sizeof (struct sockaddr_in));
690 break;
691 case AF_INET6:
692 GNUNET_memcpy (&ds->ss,
693 sa,
694 sizeof (struct sockaddr_in6));
695 break;
696 default:
697 GNUNET_break (0);
698 GNUNET_free (ds);
699 return GNUNET_SYSERR;
700 }
701 GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
702 ctx->dns_tail,
703 ds);
704 return GNUNET_OK;
705}
706
707
708/**
709 * How long should we try requests before timing out?
710 * Only effective for requests issued after this call.
711 *
712 * @param ctx resolver context to modify
713 * @param retry_freq how long to wait between retries
714 */
715void
716GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
717 struct GNUNET_TIME_Relative retry_freq)
718{
719 ctx->retry_freq = retry_freq;
720}
721
722
723/**
724 * Cleanup DNSSTUB resolver.
725 *
726 * @param ctx stub resolver to clean up
727 */
728void
729GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
730{
731 struct DnsServer *ds;
732
733 while (NULL != (ds = ctx->dns_head))
734 {
735 GNUNET_CONTAINER_DLL_remove (ctx->dns_head,
736 ctx->dns_tail,
737 ds);
738 GNUNET_free (ds);
739 }
740 for (unsigned int i=0;i<ctx->num_sockets;i++)
741 cleanup_rs (&ctx->sockets[i]);
742 GNUNET_free (ctx->sockets);
743 GNUNET_free (ctx);
744}
745
746
747/* end of dnsstub.c */
diff --git a/src/util/gnunet-config.c b/src/util/gnunet-config.c
index 16b826ee2..4528bbe24 100644
--- a/src/util/gnunet-config.c
+++ b/src/util/gnunet-config.c
@@ -75,27 +75,36 @@ print_option (void *cls,
75 const char *option, 75 const char *option,
76 const char *value) 76 const char *value)
77{ 77{
78 (void) section;
79 const struct GNUNET_CONFIGURATION_Handle *cfg = cls; 78 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
80 char *value_fn; 79
80 (void) section;
81 if (is_filename) 81 if (is_filename)
82 { 82 {
83 char *value_fn;
84 char *fn;
85
83 GNUNET_assert (GNUNET_OK == 86 GNUNET_assert (GNUNET_OK ==
84 GNUNET_CONFIGURATION_get_value_filename (cfg, 87 GNUNET_CONFIGURATION_get_value_filename (cfg,
85 section, 88 section,
86 option, 89 option,
87 &value_fn)); 90 &value_fn));
91 fn = GNUNET_STRINGS_filename_expand (value_fn);
92 if (NULL == fn)
93 fn = value_fn;
94 else
95 GNUNET_free (value_fn);
88 fprintf (stdout, 96 fprintf (stdout,
89 "%s = %s\n", 97 "%s = %s\n",
90 option, 98 option,
91 GNUNET_STRINGS_filename_expand (value_fn)); 99 fn);
100 GNUNET_free (fn);
92 } 101 }
93 else 102 else
94 { 103 {
95 fprintf (stdout, 104 fprintf (stdout,
96 "%s = %s\n", 105 "%s = %s\n",
97 option, 106 option,
98 value); 107 value);
99 } 108 }
100} 109}
101 110
diff --git a/src/util/gnunet-service-resolver.c b/src/util/gnunet-service-resolver.c
index d90d8ec10..5b890261b 100644
--- a/src/util/gnunet-service-resolver.c
+++ b/src/util/gnunet-service-resolver.c
@@ -27,721 +27,559 @@
27#include "gnunet_statistics_service.h" 27#include "gnunet_statistics_service.h"
28#include "resolver.h" 28#include "resolver.h"
29 29
30
31struct Record
32{
33 struct Record *next;
34
35 struct Record *prev;
36
37 struct GNUNET_DNSPARSER_Record *record;
38};
39
30/** 40/**
31 * A cached DNS lookup result (for reverse lookup). 41 * A cached DNS lookup result.
32 */ 42 */
33struct IPCache 43struct ResolveCache
34{ 44{
35 /** 45 /**
36 * This is a doubly linked list. 46 * This is a doubly linked list.
37 */ 47 */
38 struct IPCache *next; 48 struct ResolveCache *next;
39 49
40 /** 50 /**
41 * This is a doubly linked list. 51 * This is a doubly linked list.
42 */ 52 */
43 struct IPCache *prev; 53 struct ResolveCache *prev;
44 54
45 /** 55 /**
46 * Hostname in human-readable form. 56 * type of queried DNS record
47 */ 57 */
48 char *addr; 58 uint16_t record_type;
49 59
50 /** 60 /**
51 * Binary IP address, allocated at the end of this struct. 61 * a pointer to the request_id if a query for this hostname/record_type
62 * is currently pending, NULL otherwise.
52 */ 63 */
53 const void *ip; 64 int16_t *request_id;
54 65
55 /** 66 /**
56 * Last time this entry was updated. 67 * The client that queried the records contained in this cache entry.
57 */ 68 */
58 struct GNUNET_TIME_Absolute last_refresh; 69 struct GNUNET_SERVICE_Client *client;
59 70
60 /** 71 /**
61 * Last time this entry was requested. 72 * head of a double linked list containing the lookup results
62 */ 73 */
63 struct GNUNET_TIME_Absolute last_request; 74 struct Record *records_head;
64 75
65 /** 76 /**
66 * Number of bytes in ip. 77 * tail of a double linked list containing the lookup results
67 */ 78 */
68 size_t ip_len; 79 struct Record *records_tail;
69 80
70 /** 81 /**
71 * Address family of the IP. 82 * handle for cancelling a request
72 */ 83 */
73 int af; 84 struct GNUNET_DNSSTUB_RequestSocket *resolve_handle;
85
86 /**
87 * handle for the resolution timeout task
88 */
89 struct GNUNET_SCHEDULER_Task *timeout_task;
90
74}; 91};
75 92
76 93
77/** 94/**
78 * Start of the linked list of cached DNS lookup results. 95 * Start of the linked list of cached DNS lookup results.
79 */ 96 */
80static struct IPCache *cache_head; 97static struct ResolveCache *cache_head;
81 98
82/** 99/**
83 * Tail of the linked list of cached DNS lookup results. 100 * Tail of the linked list of cached DNS lookup results.
84 */ 101 */
85static struct IPCache *cache_tail; 102static struct ResolveCache *cache_tail;
86 103
87/** 104/**
88 * Pipe for asynchronously notifying about resolve result 105 * context of dnsstub library
89 */ 106 */
90static struct GNUNET_DISK_PipeHandle *resolve_result_pipe; 107static struct GNUNET_DNSSTUB_Context *dnsstub_ctx;
91 108
92/**
93 * Task for reading from resolve_result_pipe
94 */
95static struct GNUNET_SCHEDULER_Task *resolve_result_pipe_task;
96 109
97 110void free_cache_entry (struct ResolveCache *entry)
98#if HAVE_GETNAMEINFO
99/**
100 * Resolve the given request using getnameinfo
101 *
102 * @param cache the request to resolve (and where to store the result)
103 */
104static void
105getnameinfo_resolve (struct IPCache *cache)
106{ 111{
107 char hostname[256]; 112 struct Record *pos;
108 const struct sockaddr *sa; 113 struct Record *next;
109 struct sockaddr_in v4; 114
110 struct sockaddr_in6 v6; 115 next = entry->records_head;
111 size_t salen; 116 while (NULL != (pos = next))
112 int ret;
113
114 switch (cache->af)
115 { 117 {
116 case AF_INET: 118 next = pos->next;
117 GNUNET_assert (cache->ip_len == sizeof (struct in_addr)); 119 GNUNET_CONTAINER_DLL_remove (entry->records_head,
118 sa = (const struct sockaddr*) &v4; 120 entry->records_tail,
119 memset (&v4, 0, sizeof (v4)); 121 pos);
120 v4.sin_addr = * (const struct in_addr*) cache->ip; 122 if (NULL != pos->record)
121 v4.sin_family = AF_INET; 123 {
122#if HAVE_SOCKADDR_IN_SIN_LEN 124 GNUNET_DNSPARSER_free_record (pos->record);
123 v4.sin_len = sizeof (v4); 125 GNUNET_free (pos->record);
124#endif 126 }
125 salen = sizeof (v4); 127 GNUNET_free (pos);
126 break;
127 case AF_INET6:
128 GNUNET_assert (cache->ip_len == sizeof (struct in6_addr));
129 sa = (const struct sockaddr*) &v6;
130 memset (&v6, 0, sizeof (v6));
131 v6.sin6_addr = * (const struct in6_addr*) cache->ip;
132 v6.sin6_family = AF_INET6;
133#if HAVE_SOCKADDR_IN_SIN_LEN
134 v6.sin6_len = sizeof (v6);
135#endif
136 salen = sizeof (v6);
137 break;
138 default:
139 GNUNET_assert (0);
140 } 128 }
141 129 if (NULL != entry->resolve_handle)
142 if (0 ==
143 (ret = getnameinfo (sa, salen,
144 hostname, sizeof (hostname),
145 NULL,
146 0, 0)))
147 { 130 {
148 cache->addr = GNUNET_strdup (hostname); 131 GNUNET_DNSSTUB_resolve_cancel (entry->resolve_handle);
132 entry->resolve_handle = NULL;
149 } 133 }
150 else 134 if (NULL != entry->timeout_task)
151 { 135 {
152 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 136 GNUNET_SCHEDULER_cancel (entry->timeout_task);
153 "getnameinfo failed: %s\n", 137 entry->timeout_task = NULL;
154 gai_strerror (ret));
155 } 138 }
139 GNUNET_free_non_null (entry->request_id);
140 GNUNET_free (entry);
156} 141}
157#endif
158 142
159 143
160#if HAVE_GETHOSTBYADDR 144static char*
145extract_dns_server (const char* line, size_t line_len)
146{
147 if (0 == strncmp (line, "nameserver ", 11))
148 return GNUNET_strndup (line + 11, line_len - 11);
149 return NULL;
150}
151
152
161/** 153/**
162 * Resolve the given request using gethostbyaddr 154 * reads the list of nameservers from /etc/resolve.conf
163 * 155 *
164 * @param cache the request to resolve (and where to store the result) 156 * @param server_addrs[out] a list of null-terminated server address strings
157 * @return the number of server addresses in @server_addrs, -1 on error
165 */ 158 */
166static void 159static ssize_t
167gethostbyaddr_resolve (struct IPCache *cache) 160lookup_dns_servers (char ***server_addrs)
168{ 161{
169 struct hostent *ent; 162 struct GNUNET_DISK_FileHandle *fh;
170 163 char buf[2048];
171 ent = gethostbyaddr (cache->ip, 164 ssize_t bytes_read;
172 cache->ip_len, 165 size_t read_offset = 0;
173 cache->af); 166 unsigned int num_dns_servers = 0;
174 if (NULL != ent) 167
168 fh = GNUNET_DISK_file_open ("/etc/resolv.conf",
169 GNUNET_DISK_OPEN_READ,
170 GNUNET_DISK_PERM_NONE);
171 if (NULL == fh)
175 { 172 {
176 cache->addr = GNUNET_strdup (ent->h_name); 173 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
174 "Could not open /etc/resolv.conf. "
175 "DNS resolution will not be possible.\n");
176 return -1;
177 } 177 }
178 else 178 bytes_read = GNUNET_DISK_file_read (fh,
179 buf,
180 sizeof (buf));
181 *server_addrs = NULL;
182 while (read_offset < bytes_read)
179 { 183 {
180 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 184 char *newline;
181 "gethostbyaddr failed: %s\n", 185 size_t line_len;
182 hstrerror (h_errno)); 186 char *dns_server;
187
188 newline = strchr (buf + read_offset, '\n');
189 if (NULL == newline)
190 {
191 break;
192 }
193 line_len = newline - buf - read_offset;
194 dns_server = extract_dns_server (buf + read_offset, line_len);
195 if (NULL != dns_server)
196 {
197 GNUNET_array_append (*server_addrs,
198 num_dns_servers,
199 dns_server);
200 }
201 read_offset += line_len + 1;
183 } 202 }
203 GNUNET_DISK_file_close (fh);
204 return num_dns_servers;
184} 205}
185#endif
186 206
187 207
188/** 208static char *
189 * Resolve the given request using the available methods. 209make_reverse_hostname (const void *ip, int af)
190 *
191 * @param cache the request to resolve (and where to store the result)
192 */
193static void
194cache_resolve (struct IPCache *cache)
195{ 210{
196#if HAVE_GETNAMEINFO 211 char *buf = GNUNET_new_array (80, char);
197 if (NULL == cache->addr) 212 int pos = 0;
198 getnameinfo_resolve (cache); 213 if (AF_INET == af)
199#endif 214 {
200#if HAVE_GETHOSTBYADDR 215 struct in_addr *addr = (struct in_addr *)ip;
201 if (NULL == cache->addr) 216 uint32_t ip_int = addr->s_addr;
202 gethostbyaddr_resolve (cache); 217 for (int i = 3; i >= 0; i--)
203#endif 218 {
219 int n = GNUNET_snprintf (buf + pos,
220 80 - pos,
221 "%u.",
222 ((uint8_t *)&ip_int)[i]);
223 if (n < 0)
224 {
225 GNUNET_free (buf);
226 return NULL;
227 }
228 pos += n;
229 }
230 pos += GNUNET_snprintf (buf + pos, 80 - pos, "in-addr.arpa");
231 }
232 else if (AF_INET6 == af)
233 {
234 struct in6_addr *addr = (struct in6_addr *)ip;
235 for (int i = 15; i >= 0; i--)
236 {
237 int n = GNUNET_snprintf (buf + pos, 80 - pos, "%x.", addr->s6_addr[i] & 0xf);
238 if (n < 0)
239 {
240 GNUNET_free (buf);
241 return NULL;
242 }
243 pos += n;
244 n = GNUNET_snprintf (buf + pos, 80 - pos, "%x.", addr->s6_addr[i] >> 4);
245 if (n < 0)
246 {
247 GNUNET_free (buf);
248 return NULL;
249 }
250 pos += n;
251 }
252 pos += GNUNET_snprintf (buf + pos, 80 - pos, "ip6.arpa");
253 }
254 buf[pos] = '\0';
255 return buf;
204} 256}
205 257
206 258
207/**
208 * Function called after the replies for the request have all
209 * been transmitted to the client, and we can now read the next
210 * request from the client.
211 *
212 * @param cls the `struct GNUNET_SERVICE_Client` to continue with
213 */
214static void 259static void
215notify_service_client_done (void *cls) 260send_reply (struct GNUNET_DNSPARSER_Record *record,
261 uint16_t request_id,
262 struct GNUNET_SERVICE_Client *client)
216{ 263{
217 struct GNUNET_SERVICE_Client *client = cls;
218
219 GNUNET_SERVICE_client_continue (client);
220}
221
222
223/**
224 * Get an IP address as a string (works for both IPv4 and IPv6). Note
225 * that the resolution happens asynchronously and that the first call
226 * may not immediately result in the FQN (but instead in a
227 * human-readable IP address).
228 *
229 * @param client handle to the client making the request (for sending the reply)
230 * @param af AF_INET or AF_INET6
231 * @param ip `struct in_addr` or `struct in6_addr`
232 */
233static void
234get_ip_as_string (struct GNUNET_SERVICE_Client *client,
235 int af,
236 const void *ip,
237 uint32_t request_id)
238{
239 struct IPCache *pos;
240 struct IPCache *next;
241 struct GNUNET_TIME_Absolute now;
242 struct GNUNET_MQ_Envelope *env;
243 struct GNUNET_MQ_Handle *mq;
244 struct GNUNET_RESOLVER_ResponseMessage *msg; 264 struct GNUNET_RESOLVER_ResponseMessage *msg;
245 size_t ip_len; 265 struct GNUNET_MQ_Envelope *env;
246 struct in6_addr ix; 266 void *payload;
247 size_t alen; 267 size_t payload_len;
248 268
249 switch (af) 269 switch (record->type)
250 {
251 case AF_INET:
252 ip_len = sizeof (struct in_addr);
253 break;
254 case AF_INET6:
255 ip_len = sizeof (struct in6_addr);
256 break;
257 default:
258 GNUNET_assert (0);
259 }
260 now = GNUNET_TIME_absolute_get ();
261 next = cache_head;
262 while ( (NULL != (pos = next)) &&
263 ( (pos->af != af) ||
264 (pos->ip_len != ip_len) ||
265 (0 != memcmp (pos->ip, ip, ip_len))) )
266 { 270 {
267 next = pos->next; 271 case GNUNET_DNSPARSER_TYPE_PTR:
268 if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us <
269 60 * 60 * 1000 * 1000LL)
270 { 272 {
271 GNUNET_CONTAINER_DLL_remove (cache_head, 273 char *hostname = record->data.hostname;
272 cache_tail, 274 payload = hostname;
273 pos); 275 payload_len = strlen (hostname) + 1;
274 GNUNET_free_non_null (pos->addr); 276 break;
275 GNUNET_free (pos);
276 continue;
277 } 277 }
278 } 278 case GNUNET_DNSPARSER_TYPE_A:
279 if (NULL != pos) 279 case GNUNET_DNSPARSER_TYPE_AAAA:
280 {
281 if ( (1 == inet_pton (af,
282 pos->ip,
283 &ix)) &&
284 (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us >
285 120 * 1000 * 1000LL) )
286 { 280 {
287 /* try again if still numeric AND 2 minutes have expired */ 281 payload = record->data.raw.data;
288 GNUNET_free_non_null (pos->addr); 282 payload_len = record->data.raw.data_len;
289 pos->addr = NULL; 283 break;
290 cache_resolve (pos); 284 }
291 pos->last_request = now; 285 default:
286 {
287 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
288 "Cannot handle DNS response type: unimplemented\n");
289 return;
292 } 290 }
293 } 291 }
294 else
295 {
296 pos = GNUNET_malloc (sizeof (struct IPCache) + ip_len);
297 pos->ip = &pos[1];
298 GNUNET_memcpy (&pos[1],
299 ip,
300 ip_len);
301 pos->last_request = now;
302 pos->last_refresh = now;
303 pos->ip_len = ip_len;
304 pos->af = af;
305 GNUNET_CONTAINER_DLL_insert (cache_head,
306 cache_tail,
307 pos);
308 cache_resolve (pos);
309 }
310 if (NULL != pos->addr)
311 alen = strlen (pos->addr) + 1;
312 else
313 alen = 0;
314 mq = GNUNET_SERVICE_client_get_mq (client);
315 env = GNUNET_MQ_msg_extra (msg, 292 env = GNUNET_MQ_msg_extra (msg,
316 alen, 293 payload_len,
317 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); 294 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
318 msg->id = request_id; 295 msg->id = request_id;
319 GNUNET_memcpy (&msg[1], 296 GNUNET_memcpy (&msg[1],
320 pos->addr, 297 payload,
321 alen); 298 payload_len);
322 GNUNET_MQ_send (mq, 299 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
323 env); 300 env);
324 // send end message
325 env = GNUNET_MQ_msg (msg,
326 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
327 msg->id = request_id;
328 GNUNET_MQ_notify_sent (env,
329 &notify_service_client_done,
330 client);
331 GNUNET_MQ_send (mq,
332 env);
333} 301}
334 302
335 303
336#if HAVE_GETADDRINFO_A
337struct AsyncCls
338{
339 struct gaicb *host;
340 struct sigevent *sig;
341 struct GNUNET_MQ_Handle *mq;
342 uint32_t request_id;
343};
344
345
346static void 304static void
347resolve_result_pipe_cb (void *cls) 305send_end_msg (uint16_t request_id,
306 struct GNUNET_SERVICE_Client *client)
348{ 307{
349 struct AsyncCls *async_cls;
350 struct gaicb *host;
351 struct GNUNET_RESOLVER_ResponseMessage *msg; 308 struct GNUNET_RESOLVER_ResponseMessage *msg;
352 struct GNUNET_MQ_Envelope *env; 309 struct GNUNET_MQ_Envelope *env;
353 310
354 GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle (resolve_result_pipe, 311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
355 GNUNET_DISK_PIPE_END_READ), 312 "Sending end message\n");
356 &async_cls, 313 env = GNUNET_MQ_msg (msg,
357 sizeof (struct AsyncCls *)); 314 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
358 resolve_result_pipe_task = 315 msg->id = request_id;
359 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, 316 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
360 GNUNET_DISK_pipe_handle (resolve_result_pipe, 317 env);
361 GNUNET_DISK_PIPE_END_READ), 318}
362 &resolve_result_pipe_cb, 319
363 NULL); 320
364 host = async_cls->host; 321static void
365 for (struct addrinfo *pos = host->ar_result; pos != NULL; pos = pos->ai_next) 322handle_resolve_result (void *cls,
323 const struct GNUNET_TUN_DnsHeader *dns,
324 size_t dns_len)
325{
326 struct ResolveCache *cache = cls;
327 struct GNUNET_DNSPARSER_Packet *parsed;
328 uint16_t request_id = *cache->request_id;
329 struct GNUNET_SERVICE_Client *client = cache->client;
330
331 parsed = GNUNET_DNSPARSER_parse ((const char *)dns,
332 dns_len);
333 if (NULL == parsed)
334 {
335 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
336 "Failed to parse DNS reply (request ID %u\n",
337 request_id);
338 return;
339 }
340 if (request_id != ntohs (parsed->id))
341 {
342 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
343 "Request ID in DNS reply does not match\n");
344 return;
345 }
346 else if (0 == parsed->num_answers)
347 {
348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
349 "DNS reply (request ID %u) contains no answers\n",
350 request_id);
351 GNUNET_CONTAINER_DLL_remove (cache_head,
352 cache_tail,
353 cache);
354 free_cache_entry (cache);
355 cache = NULL;
356 }
357 else
366 { 358 {
367 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368 "Lookup result for hostname %s: %s (request ID %u)\n", 360 "Got reply for request ID %u\n",
369 host->ar_name, 361 request_id);
370 GNUNET_a2s (pos->ai_addr, pos->ai_addrlen), 362 for (unsigned int i = 0; i != parsed->num_answers; i++)
371 async_cls->request_id);
372 switch (pos->ai_family)
373 { 363 {
374 case AF_INET: 364 struct Record *cache_entry = GNUNET_new (struct Record);
375 env = GNUNET_MQ_msg_extra (msg, 365 struct GNUNET_DNSPARSER_Record *record = &parsed->answers[i];
376 sizeof (struct in_addr), 366 cache_entry->record = GNUNET_DNSPARSER_duplicate_record (record);
377 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); 367 GNUNET_CONTAINER_DLL_insert (cache->records_head,
378 msg->id = async_cls->request_id; 368 cache->records_tail,
379 GNUNET_memcpy (&msg[1], 369 cache_entry);
380 &((struct sockaddr_in*) pos->ai_addr)->sin_addr, 370 send_reply (cache_entry->record,
381 sizeof (struct in_addr)); 371 request_id,
382 GNUNET_MQ_send (async_cls->mq, 372 cache->client);
383 env);
384 break;
385 case AF_INET6:
386 env = GNUNET_MQ_msg_extra (msg,
387 sizeof (struct in6_addr),
388 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
389 msg->id = async_cls->request_id;
390 GNUNET_memcpy (&msg[1],
391 &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
392 sizeof (struct in6_addr));
393 GNUNET_MQ_send (async_cls->mq,
394 env);
395 break;
396 default:
397 /* unsupported, skip */
398 break;
399 } 373 }
374 GNUNET_free_non_null (cache->request_id);
375 cache->request_id = NULL;
400 } 376 }
401 // send end message 377 send_end_msg (request_id,
402 env = GNUNET_MQ_msg (msg, 378 client);
403 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); 379 if (NULL != cache)
404 msg->id = async_cls->request_id; 380 cache->client = NULL;
405 GNUNET_MQ_send (async_cls->mq, 381 if (NULL != cache)
406 env); 382 {
407 freeaddrinfo (host->ar_result); 383 if (NULL != cache->timeout_task)
408 GNUNET_free ((struct gaicb *)host->ar_request); // free hints 384 {
409 GNUNET_free (host); 385 GNUNET_SCHEDULER_cancel (cache->timeout_task);
410 GNUNET_free (async_cls->sig); 386 cache->timeout_task = NULL;
411 GNUNET_free (async_cls); 387 }
388 if (NULL != cache->resolve_handle)
389 {
390 GNUNET_DNSSTUB_resolve_cancel (cache->resolve_handle);
391 cache->resolve_handle = NULL;
392 }
393 }
394 GNUNET_DNSPARSER_free_packet (parsed);
412} 395}
413 396
414 397
415static void 398static void
416handle_async_result (union sigval val) 399handle_resolve_timeout (void *cls)
417{ 400{
418 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (resolve_result_pipe, 401 struct ResolveCache *cache = cls;
419 GNUNET_DISK_PIPE_END_WRITE), 402
420 &val.sival_ptr, 403 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421 sizeof (val.sival_ptr)); 404 "timeout!\n");
405 if (NULL != cache->resolve_handle)
406 {
407 GNUNET_DNSSTUB_resolve_cancel (cache->resolve_handle);
408 cache->resolve_handle = NULL;
409 }
410 GNUNET_CONTAINER_DLL_remove (cache_head,
411 cache_tail,
412 cache);
413 free_cache_entry (cache);
422} 414}
423 415
424 416
425static int 417static int
426getaddrinfo_a_resolve (struct GNUNET_MQ_Handle *mq, 418resolve_and_cache (const char* hostname,
427 const char *hostname, 419 uint16_t record_type,
428 int af, 420 uint16_t request_id,
429 uint32_t request_id) 421 struct GNUNET_SERVICE_Client *client)
430{ 422{
431 int ret; 423 char *packet_buf;
432 struct gaicb *host; 424 size_t packet_size;
433 struct addrinfo *hints; 425 struct GNUNET_DNSPARSER_Query query;
434 struct sigevent *sig; 426 struct GNUNET_DNSPARSER_Packet packet;
435 struct AsyncCls *async_cls; 427 struct ResolveCache *cache;
436 428 struct GNUNET_TIME_Relative timeout =
437 host = GNUNET_new (struct gaicb); 429 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5);
438 hints = GNUNET_new (struct addrinfo); 430
439 sig = GNUNET_new (struct sigevent); 431 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
440 async_cls = GNUNET_new (struct AsyncCls); 432 "resolve_and_cache\n");
441 memset (hints, 433 query.name = (char *)hostname;
434 query.type = record_type;
435 query.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
436 memset (&packet,
442 0, 437 0,
443 sizeof (struct addrinfo)); 438 sizeof (packet));
444 memset (sig, 439 packet.num_queries = 1;
445 0, 440 packet.queries = &query;
446 sizeof (struct sigevent)); 441 packet.id = htons (request_id);
447 hints->ai_family = af; 442 packet.flags.recursion_desired = 1;
448 hints->ai_socktype = SOCK_STREAM; /* go for TCP */ 443 if (GNUNET_OK !=
449 host->ar_name = hostname; 444 GNUNET_DNSPARSER_pack (&packet,
450 host->ar_service = NULL; 445 UINT16_MAX,
451 host->ar_request = hints; 446 &packet_buf,
452 host->ar_result = NULL; 447 &packet_size))
453 sig->sigev_notify = SIGEV_THREAD; 448 {
454 sig->sigev_value.sival_ptr = async_cls; 449 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
455 sig->sigev_notify_function = &handle_async_result; 450 "Failed to pack query for hostname `%s'\n",
456 async_cls->host = host; 451 hostname);
457 async_cls->sig = sig;
458 async_cls->mq = mq;
459 async_cls->request_id = request_id;
460 ret = getaddrinfo_a (GAI_NOWAIT,
461 &host,
462 1,
463 sig);
464 if (0 != ret)
465 return GNUNET_SYSERR; 452 return GNUNET_SYSERR;
453
454 }
455 cache = GNUNET_malloc (sizeof (struct ResolveCache));
456 cache->record_type = record_type;
457 cache->request_id = GNUNET_memdup (&request_id, sizeof (request_id));
458 cache->client = client;
459 cache->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout,
460 &handle_resolve_timeout,
461 cache);
462 cache->resolve_handle =
463 GNUNET_DNSSTUB_resolve (dnsstub_ctx,
464 packet_buf,
465 packet_size,
466 &handle_resolve_result,
467 cache);
468 GNUNET_CONTAINER_DLL_insert (cache_head,
469 cache_tail,
470 cache);
471 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
472 "resolve %s, request_id = %u\n",
473 hostname,
474 request_id);
475 GNUNET_free (packet_buf);
466 return GNUNET_OK; 476 return GNUNET_OK;
467} 477}
468 478
469 479
470#elif HAVE_GETADDRINFO 480static const char *
471static int 481get_hostname (struct ResolveCache *cache_entry)
472getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq,
473 const char *hostname,
474 int af,
475 uint32_t request_id)
476{ 482{
477 int s; 483 if (NULL != cache_entry->records_head)
478 struct addrinfo hints;
479 struct addrinfo *result;
480 struct addrinfo *pos;
481 struct GNUNET_RESOLVER_ResponseMessage *msg;
482 struct GNUNET_MQ_Envelope *env;
483
484#ifdef WINDOWS
485 /* Due to a bug, getaddrinfo will not return a mix of different families */
486 if (AF_UNSPEC == af)
487 { 484 {
488 int ret1; 485 GNUNET_assert (NULL != cache_entry->records_head);
489 int ret2; 486 GNUNET_assert (NULL != cache_entry->records_head->record);
490 ret1 = getaddrinfo_resolve (mq, 487 GNUNET_assert (NULL != cache_entry->records_head->record->name);
491 hostname, 488 return cache_entry->records_head->record->name;
492 AF_INET,
493 request_id);
494 ret2 = getaddrinfo_resolve (mq,
495 hostname,
496 AF_INET6,
497 request_id);
498 if ( (ret1 == GNUNET_OK) ||
499 (ret2 == GNUNET_OK) )
500 return GNUNET_OK;
501 if ( (ret1 == GNUNET_SYSERR) ||
502 (ret2 == GNUNET_SYSERR) )
503 return GNUNET_SYSERR;
504 return GNUNET_NO;
505 } 489 }
506#endif 490 return NULL;
507
508 memset (&hints,
509 0,
510 sizeof (struct addrinfo));
511 hints.ai_family = af;
512 hints.ai_socktype = SOCK_STREAM; /* go for TCP */
513
514 if (0 != (s = getaddrinfo (hostname,
515 NULL,
516 &hints,
517 &result)))
518 {
519 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
520 _("Could not resolve `%s' (%s): %s\n"),
521 hostname,
522 (af ==
523 AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"),
524 gai_strerror (s));
525 if ( (s == EAI_BADFLAGS) ||
526#ifndef WINDOWS
527 (s == EAI_SYSTEM) ||
528#endif
529 (s == EAI_MEMORY) )
530 return GNUNET_NO; /* other function may still succeed */
531 return GNUNET_SYSERR;
532 }
533 if (NULL == result)
534 return GNUNET_SYSERR;
535 for (pos = result; pos != NULL; pos = pos->ai_next)
536 {
537 switch (pos->ai_family)
538 {
539 case AF_INET:
540 env = GNUNET_MQ_msg_extra (msg,
541 sizeof (struct in_addr),
542 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
543 msg->id = request_id;
544 GNUNET_memcpy (&msg[1],
545 &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
546 sizeof (struct in_addr));
547 GNUNET_MQ_send (mq,
548 env);
549 break;
550 case AF_INET6:
551 env = GNUNET_MQ_msg_extra (msg,
552 sizeof (struct in6_addr),
553 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
554 msg->id = request_id;
555 GNUNET_memcpy (&msg[1],
556 &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
557 sizeof (struct in6_addr));
558 GNUNET_MQ_send (mq,
559 env);
560 break;
561 default:
562 /* unsupported, skip */
563 break;
564 }
565 }
566 freeaddrinfo (result);
567 return GNUNET_OK;
568} 491}
569 492
570 493
571#elif HAVE_GETHOSTBYNAME2 494static const uint16_t *
572 495get_record_type (struct ResolveCache *cache_entry)
573
574static int
575gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq,
576 const char *hostname,
577 int af,
578 uint32_t request_id)
579{ 496{
580 struct hostent *hp; 497 if (NULL != cache_entry->records_head)
581 int ret1; 498 return &cache_entry->record_type;
582 int ret2; 499 return NULL;
583 struct GNUNET_MQ_Envelope *env; 500}
584 struct GNUNET_RESOLVER_ResponseMessage *msg;
585 501
586#ifdef WINDOWS
587 /* gethostbyname2() in plibc is a compat dummy that calls gethostbyname(). */
588 return GNUNET_NO;
589#endif
590 502
591 if (af == AF_UNSPEC) 503static const struct GNUNET_TIME_Absolute *
592 { 504get_expiration_time (struct ResolveCache *cache_entry)
593 ret1 = gethostbyname2_resolve (mq, 505{
594 hostname, 506 if (NULL != cache_entry->records_head)
595 AF_INET, 507 return &cache_entry->records_head->record->expiration_time;
596 request_id); 508 return NULL;
597 ret2 = gethostbyname2_resolve (mq,
598 hostname,
599 AF_INET6,
600 request_id);
601 if ( (ret1 == GNUNET_OK) ||
602 (ret2 == GNUNET_OK) )
603 return GNUNET_OK;
604 if ( (ret1 == GNUNET_SYSERR) ||
605 (ret2 == GNUNET_SYSERR) )
606 return GNUNET_SYSERR;
607 return GNUNET_NO;
608 }
609 hp = gethostbyname2 (hostname,
610 af);
611 if (hp == NULL)
612 {
613 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
614 _("Could not find IP of host `%s': %s\n"),
615 hostname,
616 hstrerror (h_errno));
617 return GNUNET_SYSERR;
618 }
619 GNUNET_assert (hp->h_addrtype == af);
620 switch (af)
621 {
622 case AF_INET:
623 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
624 env = GNUNET_MQ_msg_extra (msg,
625 hp->h_length,
626 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
627 msg->id = request_id;
628 GNUNET_memcpy (&msg[1],
629 hp->h_addr_list[0],
630 hp->h_length);
631 GNUNET_MQ_send (mq,
632 env);
633 break;
634 case AF_INET6:
635 GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
636 env = GNUNET_MQ_msg_extra (msg,
637 hp->h_length,
638 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
639 msg->id = request_id;
640 GNUNET_memcpy (&msg[1],
641 hp->h_addr_list[0],
642 hp->h_length);
643 GNUNET_MQ_send (mq,
644 env);
645 break;
646 default:
647 GNUNET_break (0);
648 return GNUNET_SYSERR;
649 }
650 return GNUNET_OK;
651} 509}
652 510
653#elif HAVE_GETHOSTBYNAME
654
655 511
656static int 512static int
657gethostbyname_resolve (struct GNUNET_MQ_Handle *mq, 513remove_if_expired (struct ResolveCache *cache_entry)
658 const char *hostname,
659 uint32_t request_id)
660{ 514{
661 struct hostent *hp; 515 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
662 struct GNUNET_RESOLVER_ResponseMessage *msg;
663 struct GNUNET_MQ_Envelope *env;
664 516
665 hp = GETHOSTBYNAME (hostname); 517 if ( (NULL != cache_entry->records_head) &&
666 if (NULL == hp) 518 (now.abs_value_us > get_expiration_time (cache_entry)->abs_value_us) )
667 { 519 {
668 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 520 GNUNET_CONTAINER_DLL_remove (cache_head,
669 _("Could not find IP of host `%s': %s\n"), 521 cache_tail,
670 hostname, 522 cache_entry);
671 hstrerror (h_errno)); 523 free_cache_entry (cache_entry);
672 return GNUNET_SYSERR; 524 return GNUNET_YES;
673 }
674 if (hp->h_addrtype != AF_INET)
675 {
676 GNUNET_break (0);
677 return GNUNET_SYSERR;
678 } 525 }
679 GNUNET_assert (hp->h_length == sizeof (struct in_addr)); 526 return GNUNET_NO;
680 env = GNUNET_MQ_msg_extra (msg,
681 hp->h_length,
682 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
683 msg->id = request_id;
684 GNUNET_memcpy (&msg[1],
685 hp->h_addr_list[0],
686 hp->h_length);
687 GNUNET_MQ_send (mq,
688 env);
689 return GNUNET_OK;
690} 527}
691#endif
692 528
693 529
694/** 530/**
695 * Convert a string to an IP address. 531 * Get an IP address as a string (works for both IPv4 and IPv6). Note
532 * that the resolution happens asynchronously and that the first call
533 * may not immediately result in the FQN (but instead in a
534 * human-readable IP address).
696 * 535 *
697 * @param client where to send the IP address 536 * @param client handle to the client making the request (for sending the reply)
698 * @param hostname the hostname to resolve 537 * @param af AF_INET or AF_INET6
699 * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any" 538 * @param ip `struct in_addr` or `struct in6_addr`
700 */ 539 */
701static void 540static int
702get_ip_from_hostname (struct GNUNET_SERVICE_Client *client, 541try_cache (const char *hostname,
703 const char *hostname, 542 uint16_t record_type,
704 int af, 543 uint16_t request_id,
705 uint32_t request_id) 544 struct GNUNET_SERVICE_Client *client)
706{ 545{
707 struct GNUNET_MQ_Envelope *env; 546 struct ResolveCache *pos;
708 struct GNUNET_RESOLVER_ResponseMessage *msg; 547 struct ResolveCache *next;
709 struct GNUNET_MQ_Handle *mq; 548
710 549 next = cache_head;
711 mq = GNUNET_SERVICE_client_get_mq (client); 550 while ( (NULL != (pos = next)) &&
712#if HAVE_GETADDRINFO_A 551 ( (NULL == pos->records_head) ||
713 getaddrinfo_a_resolve (mq, 552 (0 != strcmp (get_hostname (pos), hostname)) ||
714 hostname, 553 (*get_record_type (pos) != record_type) ) )
715 af, 554 {
716 request_id); 555 next = pos->next;
717 GNUNET_SERVICE_client_continue (client); 556 remove_if_expired (pos);
718 return; 557 }
719#elif HAVE_GETADDRINFO 558 if (NULL != pos)
720 getaddrinfo_resolve (mq, 559 {
721 hostname, 560 if (GNUNET_NO == remove_if_expired (pos))
722 af, 561 {
723 request_id); 562 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
724#elif HAVE_GETHOSTBYNAME2 563 "found cache entry for '%s', record type '%u'\n",
725 gethostbyname2_resolve (mq, 564 hostname,
726 hostname, 565 record_type);
727 af, 566 struct Record *cache_pos = pos->records_head;
728 request_id); 567 while (NULL != cache_pos)
729#elif HAVE_GETHOSTBYNAME 568 {
730 if ( ( (af == AF_UNSPEC) || 569 send_reply (cache_pos->record,
731 (af == PF_INET) ) ) 570 request_id,
732 gethostbyname_resolve (mq, 571 client);
733 hostname, 572 cache_pos = cache_pos->next;
734 request_id); 573 }
735#endif 574 send_end_msg (request_id,
736 // send end message 575 client);
737 env = GNUNET_MQ_msg (msg, 576 return GNUNET_YES;
738 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); 577 }
739 msg->id = request_id; 578 }
740 GNUNET_MQ_notify_sent (env, 579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
741 &notify_service_client_done, 580 "no cache entry for '%s'\n",
742 client); 581 hostname);
743 GNUNET_MQ_send (mq, 582 return GNUNET_NO;
744 env);
745} 583}
746 584
747 585
@@ -801,6 +639,23 @@ check_get (void *cls,
801} 639}
802 640
803 641
642static void
643process_get (const char *hostname,
644 uint16_t record_type,
645 uint16_t request_id,
646 struct GNUNET_SERVICE_Client *client)
647{
648 if (GNUNET_NO == try_cache (hostname, record_type, request_id, client))
649 {
650 int result = resolve_and_cache (hostname,
651 record_type,
652 request_id,
653 client);
654 GNUNET_assert (GNUNET_OK == result);
655 }
656}
657
658
804/** 659/**
805 * Handle GET-message. 660 * Handle GET-message.
806 * 661 *
@@ -812,45 +667,100 @@ handle_get (void *cls,
812 const struct GNUNET_RESOLVER_GetMessage *msg) 667 const struct GNUNET_RESOLVER_GetMessage *msg)
813{ 668{
814 struct GNUNET_SERVICE_Client *client = cls; 669 struct GNUNET_SERVICE_Client *client = cls;
815 const void *ip;
816 int direction; 670 int direction;
817 int af; 671 int af;
818 uint32_t id; 672 uint16_t request_id;
673 const char *hostname;
819 674
820 direction = ntohl (msg->direction); 675 direction = ntohl (msg->direction);
821 af = ntohl (msg->af); 676 af = ntohl (msg->af);
822 id = ntohl (msg->id); 677 request_id = ntohs (msg->id);
823 if (GNUNET_NO == direction) 678 if (GNUNET_NO == direction)
824 { 679 {
825 /* IP from hostname */ 680 /* IP from hostname */
826 const char *hostname; 681 hostname = GNUNET_strdup ((const char *) &msg[1]);
827 682 switch (af)
828 hostname = (const char *) &msg[1]; 683 {
829 get_ip_from_hostname (client, 684 case AF_UNSPEC:
830 hostname, 685 {
831 af, 686 process_get (hostname, GNUNET_DNSPARSER_TYPE_ALL, request_id, client);
832 id); 687 break;
833 return; 688 }
689 case AF_INET:
690 {
691 process_get (hostname, GNUNET_DNSPARSER_TYPE_A, request_id, client);
692 break;
693 }
694 case AF_INET6:
695 {
696 process_get (hostname, GNUNET_DNSPARSER_TYPE_AAAA, request_id, client);
697 break;
698 }
699 default:
700 {
701 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
702 "got invalid af: %d\n",
703 af);
704 GNUNET_assert (0);
705 }
706 }
707 }
708 else
709 {
710 /* hostname from IP */
711 hostname = make_reverse_hostname (&msg[1], af);
712 process_get (hostname, GNUNET_DNSPARSER_TYPE_PTR, request_id, client);
834 } 713 }
835 ip = &msg[1]; 714 GNUNET_free_non_null ((char *)hostname);
715 GNUNET_SERVICE_client_continue (client);
716}
836 717
837#if !defined(GNUNET_CULL_LOGGING) 718
719static void
720shutdown_task (void *cls)
721{
722 (void) cls;
723 struct ResolveCache *pos;
724
725 while (NULL != (pos = cache_head))
838 { 726 {
839 char buf[INET6_ADDRSTRLEN]; 727 GNUNET_CONTAINER_DLL_remove (cache_head,
728 cache_tail,
729 pos);
730 free_cache_entry (pos);
731 }
732 GNUNET_DNSSTUB_stop (dnsstub_ctx);
733}
734
840 735
736static void
737init_cb (void *cls,
738 const struct GNUNET_CONFIGURATION_Handle *cfg,
739 struct GNUNET_SERVICE_Handle *sh)
740{
741 (void) cfg;
742 (void) sh;
743
744 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
745 cls);
746 dnsstub_ctx = GNUNET_DNSSTUB_start (128);
747 char **dns_servers;
748 ssize_t num_dns_servers = lookup_dns_servers (&dns_servers);
749 if (0 == num_dns_servers)
750 {
751 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
752 "no DNS server available. DNS resolution will not be possible.\n");
753 }
754 for (int i = 0; i != num_dns_servers; i++)
755 {
756 int result = GNUNET_DNSSTUB_add_dns_ip (dnsstub_ctx, dns_servers[i]);
841 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 757 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
842 "Resolver asked to look up IP address `%s (request ID %u)'.\n", 758 "Adding DNS server '%s': %s\n",
843 inet_ntop (af, 759 dns_servers[i],
844 ip, 760 GNUNET_OK == result ? "success" : "failure");
845 buf, 761 GNUNET_free (dns_servers[i]);
846 sizeof (buf)),
847 id);
848 } 762 }
849#endif 763 GNUNET_free_non_null (dns_servers);
850 get_ip_as_string (client,
851 af,
852 ip,
853 id);
854} 764}
855 765
856 766
@@ -870,19 +780,6 @@ connect_cb (void *cls,
870 (void) cls; 780 (void) cls;
871 (void) mq; 781 (void) mq;
872 782
873#if HAVE_GETADDRINFO_A
874 resolve_result_pipe = GNUNET_DISK_pipe (GNUNET_NO,
875 GNUNET_NO,
876 GNUNET_NO,
877 GNUNET_NO);
878 GNUNET_assert (NULL != resolve_result_pipe);
879 resolve_result_pipe_task =
880 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
881 GNUNET_DISK_pipe_handle (resolve_result_pipe,
882 GNUNET_DISK_PIPE_END_READ),
883 &resolve_result_pipe_cb,
884 NULL);
885#endif
886 return c; 783 return c;
887} 784}
888 785
@@ -900,19 +797,16 @@ disconnect_cb (void *cls,
900 void *internal_cls) 797 void *internal_cls)
901{ 798{
902 (void) cls; 799 (void) cls;
800 struct ResolveCache *pos = cache_head;
903 801
904#if HAVE_GETADDRINFO_A 802 while (NULL != pos)
905 if (NULL != resolve_result_pipe_task)
906 {
907 GNUNET_SCHEDULER_cancel (resolve_result_pipe_task);
908 resolve_result_pipe_task = NULL;
909 }
910 if (NULL != resolve_result_pipe)
911 { 803 {
912 GNUNET_DISK_pipe_close (resolve_result_pipe); 804 if (pos->client == c)
913 resolve_result_pipe = NULL; 805 {
806 pos->client = NULL;
807 }
808 pos = pos->next;
914 } 809 }
915#endif
916 GNUNET_assert (c == internal_cls); 810 GNUNET_assert (c == internal_cls);
917} 811}
918 812
@@ -923,7 +817,7 @@ disconnect_cb (void *cls,
923GNUNET_SERVICE_MAIN 817GNUNET_SERVICE_MAIN
924("resolver", 818("resolver",
925 GNUNET_SERVICE_OPTION_NONE, 819 GNUNET_SERVICE_OPTION_NONE,
926 NULL, 820 &init_cb,
927 &connect_cb, 821 &connect_cb,
928 &disconnect_cb, 822 &disconnect_cb,
929 NULL, 823 NULL,
@@ -950,23 +844,4 @@ GNUNET_RESOLVER_memory_init ()
950#endif 844#endif
951 845
952 846
953/**
954 * Free globals on exit.
955 */
956void __attribute__ ((destructor))
957GNUNET_RESOLVER_memory_done ()
958{
959 struct IPCache *pos;
960
961 while (NULL != (pos = cache_head))
962 {
963 GNUNET_CONTAINER_DLL_remove (cache_head,
964 cache_tail,
965 pos);
966 GNUNET_free_non_null (pos->addr);
967 GNUNET_free (pos);
968 }
969}
970
971
972/* end of gnunet-service-resolver.c */ 847/* end of gnunet-service-resolver.c */
diff --git a/src/util/gnunet-timeout-w32.c b/src/util/gnunet-timeout-w32.c
new file mode 100644
index 000000000..78b268fe2
--- /dev/null
+++ b/src/util/gnunet-timeout-w32.c
@@ -0,0 +1,191 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file src/util/gnunet-timeout-w32.c
21 * @brief small tool starting a child process, waiting that it terminates or killing it after a given timeout period
22 * @author LRN
23 */
24
25#include <windows.h>
26#include <sys/types.h>
27#include <stdio.h>
28
29int
30main (int argc, char *argv[])
31{
32 int i;
33 DWORD wait_result;
34 wchar_t *commandline;
35 wchar_t **wargv;
36 wchar_t *arg;
37 unsigned int cmdlen;
38 STARTUPINFOW start;
39 PROCESS_INFORMATION proc;
40
41 wchar_t wpath[MAX_PATH + 1];
42
43 wchar_t *pathbuf;
44 DWORD pathbuf_len, alloc_len;
45 wchar_t *ptr;
46 wchar_t *non_const_filename;
47 wchar_t *wcmd;
48 int wargc;
49 int timeout = 0;
50 ssize_t wrote;
51
52 HANDLE job;
53
54 if (argc < 3)
55 {
56 printf
57 ("arg 1: timeout in sec., arg 2: executable, arg<n> arguments\n");
58 exit (1);
59 }
60
61 timeout = atoi (argv[1]);
62
63 if (timeout == 0)
64 timeout = 600;
65
66 commandline = GetCommandLineW ();
67 if (commandline == NULL)
68 {
69 printf ("Failed to get commandline: %lu\n", GetLastError ());
70 exit (2);
71 }
72
73 wargv = CommandLineToArgvW (commandline, &wargc);
74 if (wargv == NULL || wargc <= 1)
75 {
76 printf ("Failed to get parse commandline: %lu\n", GetLastError ());
77 exit (3);
78 }
79
80 job = CreateJobObject (NULL, NULL);
81 if (job == NULL)
82 {
83 printf ("Failed to create a job: %lu\n", GetLastError ());
84 exit (4);
85 }
86
87 pathbuf_len = GetEnvironmentVariableW (L"PATH", (wchar_t *) &pathbuf, 0);
88
89 alloc_len = pathbuf_len + 1;
90
91 pathbuf = malloc (alloc_len * sizeof (wchar_t));
92
93 ptr = pathbuf;
94
95 alloc_len = GetEnvironmentVariableW (L"PATH", ptr, pathbuf_len);
96
97 cmdlen = wcslen (wargv[2]);
98 if (cmdlen < 5 || wcscmp (&wargv[2][cmdlen - 4], L".exe") != 0)
99 {
100 non_const_filename = malloc (sizeof (wchar_t) * (cmdlen + 5));
101 swprintf (non_const_filename, cmdlen + 5, L"%S.exe", wargv[2]);
102 }
103 else
104 {
105 non_const_filename = wcsdup (wargv[2]);
106 }
107
108 /* Check that this is the full path. If it isn't, search. */
109 if (non_const_filename[1] == L':')
110 swprintf (wpath, sizeof (wpath) / sizeof (wchar_t), L"%S", non_const_filename);
111 else if (!SearchPathW
112 (pathbuf, non_const_filename, NULL, sizeof (wpath) / sizeof (wchar_t),
113 wpath, NULL))
114 {
115 printf ("Failed to get find executable: %lu\n", GetLastError ());
116 exit (5);
117 }
118 free (pathbuf);
119 free (non_const_filename);
120
121 cmdlen = wcslen (wpath) + 4;
122 i = 3;
123 while (NULL != (arg = wargv[i++]))
124 cmdlen += wcslen (arg) + 4;
125
126 wcmd = malloc (sizeof (wchar_t) * (cmdlen + 1));
127 wrote = 0;
128 i = 2;
129 while (NULL != (arg = wargv[i++]))
130 {
131 /* This is to escape trailing slash */
132 wchar_t arg_lastchar = arg[wcslen (arg) - 1];
133 if (wrote == 0)
134 {
135 wrote += swprintf (&wcmd[wrote], cmdlen + 1 - wrote, L"\"%S%S\" ", wpath,
136 arg_lastchar == L'\\' ? L"\\" : L"");
137 }
138 else
139 {
140 if (wcschr (arg, L' ') != NULL)
141 wrote += swprintf (&wcmd[wrote], cmdlen + 1 - wrote, L"\"%S%S\"%S", arg,
142 arg_lastchar == L'\\' ? L"\\" : L"", i == wargc ? L"" : L" ");
143 else
144 wrote += swprintf (&wcmd[wrote], cmdlen + 1 - wrote, L"%S%S%S", arg,
145 arg_lastchar == L'\\' ? L"\\" : L"", i == wargc ? L"" : L" ");
146 }
147 }
148
149 LocalFree (wargv);
150
151 memset (&start, 0, sizeof (start));
152 start.cb = sizeof (start);
153
154 if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE, CREATE_SUSPENDED,
155 NULL, NULL, &start, &proc))
156 {
157 wprintf (L"Failed to get spawn process `%S' with arguments `%S': %lu\n", wpath, wcmd, GetLastError ());
158 exit (6);
159 }
160
161 AssignProcessToJobObject (job, proc.hProcess);
162
163 ResumeThread (proc.hThread);
164 CloseHandle (proc.hThread);
165
166 free (wcmd);
167
168 wait_result = WaitForSingleObject (proc.hProcess, timeout * 1000);
169 if (wait_result == WAIT_OBJECT_0)
170 {
171 DWORD status;
172 wait_result = GetExitCodeProcess (proc.hProcess, &status);
173 CloseHandle (proc.hProcess);
174 if (wait_result != 0)
175 {
176 printf ("Test process exited with result %lu\n", status);
177 TerminateJobObject (job, status);
178 exit (status);
179 }
180 printf ("Test process exited (failed to obtain exit status)\n");
181 TerminateJobObject (job, 0);
182 exit (0);
183 }
184 printf ("Child processes were killed after timeout of %u seconds\n",
185 timeout);
186 TerminateJobObject (job, 1);
187 CloseHandle (proc.hProcess);
188 exit (1);
189}
190
191/* end of timeout_watchdog_w32.c */
diff --git a/src/util/gnunet-timeout.c b/src/util/gnunet-timeout.c
new file mode 100644
index 000000000..8dfb6ad17
--- /dev/null
+++ b/src/util/gnunet-timeout.c
@@ -0,0 +1,128 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file src/util/gnunet-timeout.c
21 * @brief small tool starting a child process, waiting that it terminates or killing it after a given timeout period
22 * @author Matthias Wachs
23 */
24
25#include <sys/types.h>
26#include <sys/wait.h>
27#include <signal.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <unistd.h>
31
32static pid_t child;
33
34
35static void
36sigchld_handler (int val)
37{
38 int status = 0;
39 int ret = 0;
40
41 (void) val;
42 waitpid (child,
43 &status,
44 0);
45 if (WIFEXITED (status) != 0)
46 {
47 ret = WEXITSTATUS (status);
48 fprintf (stderr,
49 "Process exited with result %u\n",
50 ret);
51 exit (ret); /* return same status code */
52 }
53 if (WIFSIGNALED (status) != 0)
54 {
55 ret = WTERMSIG (status);
56 fprintf (stderr,
57 "Process received signal %u\n",
58 ret);
59 kill (getpid (),
60 ret); /* kill self with the same signal */
61 }
62 exit (-1);
63}
64
65
66static void
67sigint_handler (int val)
68{
69 kill (0,
70 val);
71 exit (val);
72}
73
74
75int
76main (int argc,
77 char *argv[])
78{
79 int timeout = 0;
80 pid_t gpid = 0;
81
82 if (argc < 3)
83 {
84 fprintf (stderr,
85 "arg 1: timeout in sec., arg 2: executable, arg<n> arguments\n");
86 exit (-1);
87 }
88
89 timeout = atoi (argv[1]);
90
91 if (timeout == 0)
92 timeout = 600;
93
94 /* with getpgid() it does not compile, but getpgrp is the BSD version and working */
95 gpid = getpgrp ();
96
97 signal (SIGCHLD, sigchld_handler);
98 signal (SIGABRT, sigint_handler);
99 signal (SIGFPE, sigint_handler);
100 signal (SIGILL, sigint_handler);
101 signal (SIGINT, sigint_handler);
102 signal (SIGSEGV, sigint_handler);
103 signal (SIGTERM, sigint_handler);
104
105 child = fork ();
106 if (child == 0)
107 {
108 /* int setpgrp(pid_t pid, pid_t pgid); is not working on this machine */
109 //setpgrp (0, pid_t gpid);
110 if (-1 != gpid)
111 setpgid (0, gpid);
112 execvp (argv[2],
113 &argv[2]);
114 exit (-1);
115 }
116 if (child > 0)
117 {
118 sleep (timeout);
119 printf ("Child processes were killed after timeout of %u seconds\n",
120 timeout);
121 kill (0,
122 SIGTERM);
123 exit (3);
124 }
125 exit (-1);
126}
127
128/* end of timeout_watchdog.c */
diff --git a/src/util/os_priority.c b/src/util/os_priority.c
index d13991334..a758f24f1 100644
--- a/src/util/os_priority.c
+++ b/src/util/os_priority.c
@@ -1588,7 +1588,7 @@ GNUNET_OS_start_process_s (int pipe_control,
1588 1588
1589 1589
1590/** 1590/**
1591 * Retrieve the status of a process, waiting on him if dead. 1591 * Retrieve the status of a process, waiting on it if dead.
1592 * Nonblocking version. 1592 * Nonblocking version.
1593 * 1593 *
1594 * @param proc process ID 1594 * @param proc process ID
@@ -1705,7 +1705,7 @@ process_status (struct GNUNET_OS_Process *proc,
1705 1705
1706 1706
1707/** 1707/**
1708 * Retrieve the status of a process, waiting on him if dead. 1708 * Retrieve the status of a process, waiting on it if dead.
1709 * Nonblocking version. 1709 * Nonblocking version.
1710 * 1710 *
1711 * @param proc process ID 1711 * @param proc process ID
@@ -1726,7 +1726,7 @@ GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1726 1726
1727 1727
1728/** 1728/**
1729 * Retrieve the status of a process, waiting on him if dead. 1729 * Retrieve the status of a process, waiting on it if dead.
1730 * Blocking version. 1730 * Blocking version.
1731 * 1731 *
1732 * @param proc pointer to process structure 1732 * @param proc pointer to process structure
diff --git a/src/util/regex.c b/src/util/regex.c
new file mode 100644
index 000000000..7565a9eac
--- /dev/null
+++ b/src/util/regex.c
@@ -0,0 +1,834 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012, 2013, 2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18/**
19 * @file src/tun/regex.c
20 * @brief functions to convert IP networks to regexes
21 * @author Maximilian Szengel
22 * @author Christian Grothoff
23 */
24#include "platform.h"
25#include "gnunet_util_lib.h"
26#include "gnunet_tun_lib.h"
27
28/**
29 * 'wildcard', matches all possible values (for HEX encoding).
30 */
31#define DOT "(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"
32
33
34/**
35 * Create a regex in @a rxstr from the given @a ip and @a netmask.
36 *
37 * @param ip IPv4 representation.
38 * @param port destination port
39 * @param rxstr generated regex, must be at least #GNUNET_TUN_IPV4_REGEXLEN
40 * bytes long.
41 */
42void
43GNUNET_TUN_ipv4toregexsearch (const struct in_addr *ip,
44 uint16_t port,
45 char *rxstr)
46{
47 GNUNET_snprintf (rxstr,
48 GNUNET_TUN_IPV4_REGEXLEN,
49 "4-%04X-%08X",
50 (unsigned int) port,
51 ntohl (ip->s_addr));
52}
53
54
55/**
56 * Create a regex in @a rxstr from the given @a ipv6 and @a prefixlen.
57 *
58 * @param ipv6 IPv6 representation.
59 * @param port destination port
60 * @param rxstr generated regex, must be at least #GNUNET_TUN_IPV6_REGEXLEN
61 * bytes long.
62 */
63void
64GNUNET_TUN_ipv6toregexsearch (const struct in6_addr *ipv6,
65 uint16_t port,
66 char *rxstr)
67{
68 const uint32_t *addr;
69
70 addr = (const uint32_t *) ipv6;
71 GNUNET_snprintf (rxstr,
72 GNUNET_TUN_IPV6_REGEXLEN,
73 "6-%04X-%08X%08X%08X%08X",
74 (unsigned int) port,
75 ntohl (addr[0]),
76 ntohl (addr[1]),
77 ntohl (addr[2]),
78 ntohl (addr[3]));
79}
80
81
82/**
83 * Convert the given 4-bit (!) number to a regex.
84 *
85 * @param value the value, only the lowest 4 bits will be looked at
86 * @param mask which bits in value are wildcards (any value)?
87 */
88static char *
89nibble_to_regex (uint8_t value,
90 uint8_t mask)
91{
92 char *ret;
93
94 value &= mask;
95 switch (mask)
96 {
97 case 0:
98 return GNUNET_strdup (DOT);
99 case 8:
100 GNUNET_asprintf (&ret,
101 "(%X|%X|%X|%X|%X|%X|%X|%X)",
102 value,
103 value + 1,
104 value + 2,
105 value + 3,
106 value + 4,
107 value + 5,
108 value + 6,
109 value + 7);
110 return ret;
111 case 12:
112 GNUNET_asprintf (&ret,
113 "(%X|%X|%X|%X)",
114 value,
115 value + 1,
116 value + 2,
117 value + 3);
118 return ret;
119 case 14:
120 GNUNET_asprintf (&ret,
121 "(%X|%X)",
122 value,
123 value + 1);
124 return ret;
125 case 15:
126 GNUNET_asprintf (&ret,
127 "%X",
128 value);
129 return ret;
130 default:
131 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
132 _("Bad mask: %d\n"),
133 mask);
134 GNUNET_break (0);
135 return NULL;
136 }
137}
138
139
140/**
141 * Convert the given 16-bit number to a regex.
142 *
143 * @param value the value
144 * @param mask which bits in value are wildcards (any value)?
145 */
146static char *
147num_to_regex (uint16_t value,
148 uint16_t mask)
149{
150 const uint8_t *v = (const uint8_t *) &value;
151 const uint8_t *m = (const uint8_t *) &mask;
152 char *a;
153 char *b;
154 char *c;
155 char *d;
156 char *ret;
157
158 a = nibble_to_regex (v[0] >> 4, m[0] >> 4);
159 b = nibble_to_regex (v[0] & 15, m[0] & 15);
160 c = nibble_to_regex (v[1] >> 4, m[1] >> 4);
161 d = nibble_to_regex (v[1] & 15, m[1] & 15);
162 ret = NULL;
163 if ( (NULL != a) &&
164 (NULL != b) &&
165 (NULL != c) &&
166 (NULL != d) )
167 GNUNET_asprintf (&ret,
168 "%s%s%s%s",
169 a, b, c, d);
170 GNUNET_free_non_null (a);
171 GNUNET_free_non_null (b);
172 GNUNET_free_non_null (c);
173 GNUNET_free_non_null (d);
174 return ret;
175}
176
177
178/**
179 * Do we need to put parents around the given argument?
180 *
181 * @param arg part of a regular expression
182 * @return #GNUNET_YES if we should parens,
183 * #GNUNET_NO if not
184 */
185static int
186needs_parens (const char *arg)
187{
188 size_t off;
189 size_t len;
190 unsigned int op;
191
192 op = 0;
193 len = strlen (arg);
194 for (off=0;off<len;off++)
195 {
196 switch (arg[off])
197 {
198 case '(':
199 op++;
200 break;
201 case ')':
202 GNUNET_assert (op > 0);
203 op--;
204 break;
205 case '|':
206 if (0 == op)
207 return GNUNET_YES;
208 break;
209 default:
210 break;
211 }
212 }
213 return GNUNET_NO;
214}
215
216
217/**
218 * Compute port policy for the given range of
219 * port numbers.
220 *
221 * @param start starting offset
222 * @param end end offset
223 * @param step increment level (power of 16)
224 * @param pp port policy to convert
225 * @return corresponding regex
226 */
227static char *
228compute_policy (unsigned int start,
229 unsigned int end,
230 unsigned int step,
231 const struct GNUNET_STRINGS_PortPolicy *pp)
232{
233 unsigned int i;
234 char before[36]; /* 16 * 2 + 3 dots + 0-terminator */
235 char middlel[33]; /* 16 * 2 + 0-terminator */
236 char middleh[33]; /* 16 * 2 + 0-terminator */
237 char after[36]; /* 16 * 2 + 3 dots + 0-terminator */
238 char beforep[36+2]; /* 16 * 2 + 3 dots + 0-terminator + ()*/
239 char middlehp[33+2]; /* 16 * 2 + 0-terminator + () */
240 char middlelp[33+2]; /* 16 * 2 + 0-terminator + () */
241 char afterp[36+2]; /* 16 * 2 + 3 dots + 0-terminator + () */
242 char dots[5 * strlen (DOT)];
243 char buf[3];
244 char *middle;
245 char *ret;
246 unsigned int xstep;
247 char *recl;
248 char *rech;
249 char *reclp;
250 char *rechp;
251 unsigned int start_port;
252 unsigned int end_port;
253
254 GNUNET_assert (GNUNET_YES == pp->negate_portrange);
255 start_port = pp->start_port;
256 if (1 == start_port)
257 start_port = 0;
258 end_port = pp->end_port;
259 GNUNET_assert ((end - start) / step <= 0xF);
260 before[0] = '\0';
261 middlel[0] = '\0';
262 middleh[0] = '\0';
263 after[0] = '\0';
264 for (i=start;i<=end;i+=step)
265 {
266 GNUNET_snprintf (buf,
267 sizeof (buf),
268 "%X|",
269 (i - start) / step);
270 if (i / step < start_port / step)
271 strcat (before, buf);
272 else if (i / step > end_port / step)
273 strcat (after, buf);
274 else if (i / step == start_port / step)
275 strcat (middlel, buf);
276 else if (i / step == end_port / step)
277 strcat (middleh, buf);
278 }
279 if (strlen (before) > 0)
280 before[strlen (before)-1] = '\0';
281 if (strlen (middlel) > 0)
282 middlel[strlen (middlel)-1] = '\0';
283 if (strlen (middleh) > 0)
284 middleh[strlen (middleh)-1] = '\0';
285 if (strlen (after) > 0)
286 after[strlen (after)-1] = '\0';
287 if (needs_parens (before))
288 GNUNET_snprintf (beforep,
289 sizeof (beforep),
290 "(%s)",
291 before);
292 else
293 strcpy (beforep, before);
294 if (needs_parens (middlel))
295 GNUNET_snprintf (middlelp,
296 sizeof (middlelp),
297 "(%s)",
298 middlel);
299 else
300 strcpy (middlelp, middlel);
301 if (needs_parens (middleh))
302 GNUNET_snprintf (middlehp,
303 sizeof (middlehp),
304 "(%s)",
305 middleh);
306 else
307 strcpy (middlehp, middleh);
308 if (needs_parens (after))
309 GNUNET_snprintf (afterp,
310 sizeof (afterp),
311 "(%s)",
312 after);
313 else
314 strcpy (afterp, after);
315 dots[0] = '\0';
316 for (xstep=step/16;xstep>0;xstep/=16)
317 strcat (dots, DOT);
318 if (step >= 16)
319 {
320 if (strlen (middlel) > 0)
321 recl = compute_policy ((start_port / step) * step,
322 (start_port / step) * step + step - 1,
323 step / 16,
324 pp);
325 else
326 recl = GNUNET_strdup ("");
327 if (strlen (middleh) > 0)
328 rech = compute_policy ((end_port / step) * step,
329 (end_port / step) * step + step - 1,
330 step / 16,
331 pp);
332 else
333 rech = GNUNET_strdup ("");
334 }
335 else
336 {
337 recl = GNUNET_strdup ("");
338 rech = GNUNET_strdup ("");
339 middlel[0] = '\0';
340 middlelp[0] = '\0';
341 middleh[0] = '\0';
342 middlehp[0] = '\0';
343 }
344 if (needs_parens (recl))
345 GNUNET_asprintf (&reclp,
346 "(%s)",
347 recl);
348 else
349 reclp = GNUNET_strdup (recl);
350 if (needs_parens (rech))
351 GNUNET_asprintf (&rechp,
352 "(%s)",
353 rech);
354 else
355 rechp = GNUNET_strdup (rech);
356
357 if ( (strlen (middleh) > 0) &&
358 (strlen (rech) > 0) &&
359 (strlen (middlel) > 0) &&
360 (strlen (recl) > 0) )
361 {
362 GNUNET_asprintf (&middle,
363 "%s%s|%s%s",
364 middlel,
365 reclp,
366 middleh,
367 rechp);
368 }
369 else if ( (strlen (middleh) > 0) &&
370 (strlen (rech) > 0) )
371 {
372 GNUNET_asprintf (&middle,
373 "%s%s",
374 middleh,
375 rechp);
376 }
377 else if ( (strlen (middlel) > 0) &&
378 (strlen (recl) > 0) )
379 {
380 GNUNET_asprintf (&middle,
381 "%s%s",
382 middlel,
383 reclp);
384 }
385 else
386 {
387 middle = GNUNET_strdup ("");
388 }
389 if ( (strlen(before) > 0) &&
390 (strlen(after) > 0) )
391 {
392 if (strlen (dots) > 0)
393 {
394 if (strlen (middle) > 0)
395 GNUNET_asprintf (&ret,
396 "(%s%s|%s|%s%s)",
397 beforep, dots,
398 middle,
399 afterp, dots);
400 else
401 GNUNET_asprintf (&ret,
402 "(%s|%s)%s",
403 beforep,
404 afterp,
405 dots);
406 }
407 else
408 {
409 if (strlen (middle) > 0)
410 GNUNET_asprintf (&ret,
411 "(%s|%s|%s)",
412 before,
413 middle,
414 after);
415 else if (1 == step)
416 GNUNET_asprintf (&ret,
417 "%s|%s",
418 before,
419 after);
420 else
421 GNUNET_asprintf (&ret,
422 "(%s|%s)",
423 before,
424 after);
425 }
426 }
427 else if (strlen (before) > 0)
428 {
429 if (strlen (dots) > 0)
430 {
431 if (strlen (middle) > 0)
432 GNUNET_asprintf (&ret,
433 "(%s%s|%s)",
434 beforep, dots,
435 middle);
436 else
437 GNUNET_asprintf (&ret,
438 "%s%s",
439 beforep, dots);
440 }
441 else
442 {
443 if (strlen (middle) > 0)
444 GNUNET_asprintf (&ret,
445 "(%s|%s)",
446 before,
447 middle);
448 else
449 GNUNET_asprintf (&ret,
450 "%s",
451 before);
452 }
453 }
454 else if (strlen (after) > 0)
455 {
456 if (strlen (dots) > 0)
457 {
458 if (strlen (middle) > 0)
459 GNUNET_asprintf (&ret,
460 "(%s|%s%s)",
461 middle,
462 afterp, dots);
463 else
464 GNUNET_asprintf (&ret,
465 "%s%s",
466 afterp, dots);
467 }
468 else
469 {
470 if (strlen (middle) > 0)
471 GNUNET_asprintf (&ret,
472 "%s|%s",
473 middle,
474 after);
475 else
476 GNUNET_asprintf (&ret,
477 "%s",
478 after);
479 }
480 }
481 else if (strlen (middle) > 0)
482 {
483 GNUNET_asprintf (&ret,
484 "%s",
485 middle);
486 }
487 else
488 {
489 ret = GNUNET_strdup ("");
490 }
491 GNUNET_free (middle);
492 GNUNET_free (reclp);
493 GNUNET_free (rechp);
494 GNUNET_free (recl);
495 GNUNET_free (rech);
496 return ret;
497}
498
499
500/**
501 * Convert a port policy to a regular expression. Note: this is a
502 * very simplistic implementation, we might want to consider doing
503 * something more sophisiticated (resulting in smaller regular
504 * expressions) at a later time.
505 *
506 * @param pp port policy to convert
507 * @return NULL on error
508 */
509static char *
510port_to_regex (const struct GNUNET_STRINGS_PortPolicy *pp)
511{
512 char *reg;
513 char *ret;
514 char *pos;
515 unsigned int i;
516 unsigned int cnt;
517
518 if ( (0 == pp->start_port) ||
519 ( (1 == pp->start_port) &&
520 (0xFFFF == pp->end_port) &&
521 (GNUNET_NO == pp->negate_portrange)) )
522 return GNUNET_strdup (DOT DOT DOT DOT);
523 if ( (pp->start_port == pp->end_port) &&
524 (GNUNET_NO == pp->negate_portrange))
525 {
526 GNUNET_asprintf (&ret,
527 "%04X",
528 pp->start_port);
529 return ret;
530 }
531 if (pp->end_port < pp->start_port)
532 return NULL;
533
534 if (GNUNET_YES == pp->negate_portrange)
535 {
536 ret = compute_policy (0, 0xFFFF, 0x1000, pp);
537 }
538 else
539 {
540 cnt = pp->end_port - pp->start_port + 1;
541 reg = GNUNET_malloc (cnt * 5 + 1);
542 pos = reg;
543 for (i=1;i<=0xFFFF;i++)
544 {
545 if ( (i >= pp->start_port) && (i <= pp->end_port) )
546 {
547 if (pos == reg)
548 {
549 GNUNET_snprintf (pos,
550 5,
551 "%04X",
552 i);
553 }
554 else
555 {
556 GNUNET_snprintf (pos,
557 6,
558 "|%04X",
559 i);
560 }
561 pos += strlen (pos);
562 }
563 }
564 GNUNET_asprintf (&ret,
565 "(%s)",
566 reg);
567 GNUNET_free (reg);
568 }
569 return ret;
570}
571
572
573/**
574 * Convert an address (IPv4 or IPv6) to a regex.
575 *
576 * @param addr address
577 * @param mask network mask
578 * @param len number of bytes in @a addr and @a mask
579 * @return NULL on error, otherwise regex for the address
580 */
581static char *
582address_to_regex (const void *addr,
583 const void *mask,
584 size_t len)
585{
586 const uint16_t *a = addr;
587 const uint16_t *m = mask;
588 char *ret;
589 char *tmp;
590 char *reg;
591 unsigned int i;
592
593 ret = NULL;
594 GNUNET_assert (1 != (len % 2));
595 for (i=0;i<len / 2;i++)
596 {
597 reg = num_to_regex (a[i], m[i]);
598 if (NULL == reg)
599 {
600 GNUNET_free_non_null (ret);
601 return NULL;
602 }
603 if (NULL == ret)
604 {
605 ret = reg;
606 }
607 else
608 {
609 GNUNET_asprintf (&tmp,
610 "%s%s",
611 ret, reg);
612 GNUNET_free (ret);
613 GNUNET_free (reg);
614 ret = tmp;
615 }
616 }
617 return ret;
618}
619
620
621/**
622 * Convert a single line of an IPv4 policy to a regular expression.
623 *
624 * @param v4 line to convert
625 * @return NULL on error
626 */
627static char *
628ipv4_to_regex (const struct GNUNET_STRINGS_IPv4NetworkPolicy *v4)
629{
630 char *reg;
631 char *pp;
632 char *ret;
633
634 reg = address_to_regex (&v4->network,
635 &v4->netmask,
636 sizeof (struct in_addr));
637 if (NULL == reg)
638 return NULL;
639 pp = port_to_regex (&v4->pp);
640 if (NULL == pp)
641 {
642 GNUNET_free (reg);
643 return NULL;
644 }
645 GNUNET_asprintf (&ret,
646 "4-%s-%s",
647 pp, reg);
648 GNUNET_free (pp);
649 GNUNET_free (reg);
650 return ret;
651}
652
653
654/**
655 * Convert a single line of an IPv4 policy to a regular expression.
656 *
657 * @param v6 line to convert
658 * @return NULL on error
659 */
660static char *
661ipv6_to_regex (const struct GNUNET_STRINGS_IPv6NetworkPolicy *v6)
662{
663 char *reg;
664 char *pp;
665 char *ret;
666
667 reg = address_to_regex (&v6->network,
668 &v6->netmask,
669 sizeof (struct in6_addr));
670 if (NULL == reg)
671 return NULL;
672 pp = port_to_regex (&v6->pp);
673 if (NULL == pp)
674 {
675 GNUNET_free (reg);
676 return NULL;
677 }
678 GNUNET_asprintf (&ret,
679 "6-%s-%s",
680 pp, reg);
681 GNUNET_free (pp);
682 GNUNET_free (reg);
683 return ret;
684}
685
686
687/**
688 * Convert an exit policy to a regular expression. The exit policy
689 * specifies a set of subnets this peer is willing to serve as an
690 * exit for; the resulting regular expression will match the
691 * IPv4 address strings as returned by #GNUNET_TUN_ipv4toregexsearch().
692 *
693 * @param policy exit policy specification
694 * @return regular expression, NULL on error
695 */
696char *
697GNUNET_TUN_ipv4policy2regex (const char *policy)
698{
699 struct GNUNET_STRINGS_IPv4NetworkPolicy *np;
700 char *reg;
701 char *tmp;
702 char *line;
703 unsigned int i;
704
705 np = GNUNET_STRINGS_parse_ipv4_policy (policy);
706 if (NULL == np)
707 return NULL;
708 reg = NULL;
709 for (i=0; (0 == i) || (0 != np[i].network.s_addr); i++)
710 {
711 line = ipv4_to_regex (&np[i]);
712 if (NULL == line)
713 {
714 GNUNET_free_non_null (reg);
715 GNUNET_free (np);
716 return NULL;
717 }
718 if (NULL == reg)
719 {
720 reg = line;
721 }
722 else
723 {
724 GNUNET_asprintf (&tmp,
725 "%s|(%s)",
726 reg, line);
727 GNUNET_free (reg);
728 GNUNET_free (line);
729 reg = tmp;
730 }
731 if (0 == np[i].network.s_addr)
732 break;
733 }
734 GNUNET_free (np);
735 return reg;
736}
737
738
739/**
740 * Convert an exit policy to a regular expression. The exit policy
741 * specifies a set of subnets this peer is willing to serve as an
742 * exit for; the resulting regular expression will match the
743 * IPv6 address strings as returned by #GNUNET_TUN_ipv6toregexsearch().
744 *
745 * @param policy exit policy specification
746 * @return regular expression, NULL on error
747 */
748char *
749GNUNET_TUN_ipv6policy2regex (const char *policy)
750{
751 struct in6_addr zero;
752 struct GNUNET_STRINGS_IPv6NetworkPolicy *np;
753 char *reg;
754 char *tmp;
755 char *line;
756 unsigned int i;
757
758 np = GNUNET_STRINGS_parse_ipv6_policy (policy);
759 if (NULL == np)
760 return NULL;
761 reg = NULL;
762 memset (&zero, 0, sizeof (struct in6_addr));
763 for (i=0; (0 == i) || (0 != memcmp (&zero, &np[i].network, sizeof (struct in6_addr))); i++)
764 {
765 line = ipv6_to_regex (&np[i]);
766 if (NULL == line)
767 {
768 GNUNET_free_non_null (reg);
769 GNUNET_free (np);
770 return NULL;
771 }
772 if (NULL == reg)
773 {
774 reg = line;
775 }
776 else
777 {
778 GNUNET_asprintf (&tmp,
779 "%s|(%s)",
780 reg, line);
781 GNUNET_free (reg);
782 GNUNET_free (line);
783 reg = tmp;
784 }
785 if (0 == memcmp (&zero, &np[i].network, sizeof (struct in6_addr)))
786 break;
787 }
788 GNUNET_free (np);
789 return reg;
790}
791
792
793/**
794 * Hash the service name of a hosted service to the
795 * hash code that is used to identify the service on
796 * the network.
797 *
798 * @param service_name a string
799 * @param hc corresponding hash
800 */
801void
802GNUNET_TUN_service_name_to_hash (const char *service_name,
803 struct GNUNET_HashCode *hc)
804{
805 GNUNET_CRYPTO_hash (service_name,
806 strlen (service_name),
807 hc);
808}
809
810
811/**
812 * Compute the CADET port given a service descriptor
813 * (returned from #GNUNET_TUN_service_name_to_hash) and
814 * a TCP/UDP port @a ip_port.
815 *
816 * @param desc service shared secret
817 * @param ip_port TCP/UDP port, use 0 for ICMP
818 * @param[out] cadet_port CADET port to use
819 */
820void
821GNUNET_TUN_compute_service_cadet_port (const struct GNUNET_HashCode *desc,
822 uint16_t ip_port,
823 struct GNUNET_HashCode *cadet_port)
824{
825 uint16_t be_port = htons (ip_port);
826
827 *cadet_port = *desc;
828 GNUNET_memcpy (cadet_port,
829 &be_port,
830 sizeof (uint16_t));
831}
832
833
834/* end of regex.c */
diff --git a/src/util/resolver.h b/src/util/resolver.h
index a0f105afa..07851d052 100644
--- a/src/util/resolver.h
+++ b/src/util/resolver.h
@@ -60,7 +60,7 @@ struct GNUNET_RESOLVER_GetMessage
60 * identifies the request and is contained in the response message. The 60 * identifies the request and is contained in the response message. The
61 * client has to match response to request by this identifier. 61 * client has to match response to request by this identifier.
62 */ 62 */
63 uint32_t id GNUNET_PACKED; 63 uint16_t id GNUNET_PACKED;
64 64
65 /* followed by 0-terminated string for A/AAAA-lookup or 65 /* followed by 0-terminated string for A/AAAA-lookup or
66 by 'struct in_addr' / 'struct in6_addr' for reverse lookup */ 66 by 'struct in_addr' / 'struct in6_addr' for reverse lookup */
@@ -79,7 +79,7 @@ struct GNUNET_RESOLVER_ResponseMessage
79 * identifies the request this message responds to. The client 79 * identifies the request this message responds to. The client
80 * has to match response to request by this identifier. 80 * has to match response to request by this identifier.
81 */ 81 */
82 uint32_t id GNUNET_PACKED; 82 uint16_t id GNUNET_PACKED;
83 83
84 /* followed by 0-terminated string for response to a reverse lookup 84 /* followed by 0-terminated string for response to a reverse lookup
85 * or by 'struct in_addr' / 'struct in6_addr' for response to 85 * or by 'struct in_addr' / 'struct in6_addr' for response to
diff --git a/src/util/resolver_api.c b/src/util/resolver_api.c
index b94819f06..8a054327b 100644
--- a/src/util/resolver_api.c
+++ b/src/util/resolver_api.c
@@ -68,10 +68,10 @@ static struct GNUNET_RESOLVER_RequestHandle *req_head;
68 */ 68 */
69static struct GNUNET_RESOLVER_RequestHandle *req_tail; 69static struct GNUNET_RESOLVER_RequestHandle *req_tail;
70 70
71/** 71///**
72 * ID of the last request we sent to the service 72// * ID of the last request we sent to the service
73 */ 73// */
74static uint32_t last_request_id; 74//static uint16_t last_request_id;
75 75
76/** 76/**
77 * How long should we wait to reconnect? 77 * How long should we wait to reconnect?
@@ -445,7 +445,7 @@ process_requests ()
445 GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST); 445 GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
446 msg->direction = htonl (rh->direction); 446 msg->direction = htonl (rh->direction);
447 msg->af = htonl (rh->af); 447 msg->af = htonl (rh->af);
448 msg->id = htonl (rh->id); 448 msg->id = htons (rh->id);
449 GNUNET_memcpy (&msg[1], 449 GNUNET_memcpy (&msg[1],
450 &rh[1], 450 &rh[1],
451 rh->data_len); 451 rh->data_len);
@@ -491,7 +491,7 @@ handle_response (void *cls,
491 struct GNUNET_RESOLVER_RequestHandle *rh = req_head; 491 struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
492 uint16_t size; 492 uint16_t size;
493 char *nret; 493 char *nret;
494 uint32_t request_id = msg->id; 494 uint16_t request_id = msg->id;
495 495
496 for (; rh != NULL; rh = rh->next) 496 for (; rh != NULL; rh = rh->next)
497 { 497 {
@@ -911,6 +911,14 @@ handle_lookup_timeout (void *cls)
911} 911}
912 912
913 913
914static uint16_t
915get_request_id ()
916{
917 return (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
918 UINT16_MAX);
919}
920
921
914/** 922/**
915 * Convert a string to one or more IP addresses. 923 * Convert a string to one or more IP addresses.
916 * 924 *
@@ -945,7 +953,8 @@ GNUNET_RESOLVER_ip_get (const char *hostname,
945 hostname); 953 hostname);
946 rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen); 954 rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
947 rh->af = af; 955 rh->af = af;
948 rh->id = ++last_request_id; 956 //rh->id = ++last_request_id;
957 rh->id = get_request_id ();
949 rh->addr_callback = callback; 958 rh->addr_callback = callback;
950 rh->cls = callback_cls; 959 rh->cls = callback_cls;
951 GNUNET_memcpy (&rh[1], 960 GNUNET_memcpy (&rh[1],
@@ -1092,7 +1101,8 @@ GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
1092 rh->name_callback = callback; 1101 rh->name_callback = callback;
1093 rh->cls = cls; 1102 rh->cls = cls;
1094 rh->af = sa->sa_family; 1103 rh->af = sa->sa_family;
1095 rh->id = ++last_request_id; 1104 //rh->id = ++last_request_id;
1105 rh->id = get_request_id ();
1096 rh->timeout = GNUNET_TIME_relative_to_absolute (timeout); 1106 rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1097 GNUNET_memcpy (&rh[1], 1107 GNUNET_memcpy (&rh[1],
1098 ip, 1108 ip,
diff --git a/src/util/strings.c b/src/util/strings.c
index 5ed195933..ea3c8cfb9 100644
--- a/src/util/strings.c
+++ b/src/util/strings.c
@@ -11,7 +11,7 @@
11 WITHOUT ANY WARRANTY; without even the implied warranty of 11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details. 13 Affero General Public License for more details.
14 14
15 You should have received a copy of the GNU Affero General Public License 15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/ 17*/
@@ -1947,27 +1947,27 @@ static char *cvt =
1947/** 1947/**
1948 * Encode into Base64. 1948 * Encode into Base64.
1949 * 1949 *
1950 * @param data the data to encode 1950 * @param in the data to encode
1951 * @param len the length of the input 1951 * @param len the length of the input
1952 * @param output where to write the output (*output should be NULL, 1952 * @param output where to write the output (*output should be NULL,
1953 * is allocated) 1953 * is allocated)
1954 * @return the size of the output 1954 * @return the size of the output
1955 */ 1955 */
1956size_t 1956size_t
1957GNUNET_STRINGS_base64_encode (const char *data, 1957GNUNET_STRINGS_base64_encode (const void *in,
1958 size_t len, 1958 size_t len,
1959 char **output) 1959 char **output)
1960{ 1960{
1961 size_t i; 1961 const char *data = in;
1962 char c;
1963 size_t ret; 1962 size_t ret;
1964 char *opt; 1963 char *opt;
1965 1964
1966 ret = 0; 1965 ret = 0;
1967 opt = GNUNET_malloc (2 + (len * 4 / 3) + 8); 1966 opt = GNUNET_malloc (2 + (len * 4 / 3) + 8);
1968 *output = opt; 1967 for (size_t i = 0; i < len; ++i)
1969 for (i = 0; i < len; ++i)
1970 { 1968 {
1969 char c;
1970
1971 c = (data[i] >> 2) & 0x3f; 1971 c = (data[i] >> 2) & 0x3f;
1972 opt[ret++] = cvt[(int) c]; 1972 opt[ret++] = cvt[(int) c];
1973 c = (data[i] << 4) & 0x3f; 1973 c = (data[i] << 4) & 0x3f;
@@ -1997,6 +1997,7 @@ GNUNET_STRINGS_base64_encode (const char *data,
1997 } 1997 }
1998 } 1998 }
1999 opt[ret++] = FILLCHAR; 1999 opt[ret++] = FILLCHAR;
2000 *output = opt;
2000 return ret; 2001 return ret;
2001} 2002}
2002 2003
@@ -2018,11 +2019,10 @@ GNUNET_STRINGS_base64_encode (const char *data,
2018 */ 2019 */
2019size_t 2020size_t
2020GNUNET_STRINGS_base64_decode (const char *data, 2021GNUNET_STRINGS_base64_decode (const char *data,
2021 size_t len, char **output) 2022 size_t len,
2023 void **out)
2022{ 2024{
2023 size_t i; 2025 char *output;
2024 char c;
2025 char c1;
2026 size_t ret = 0; 2026 size_t ret = 0;
2027 2027
2028#define CHECK_CRLF while (data[i] == '\r' || data[i] == '\n') {\ 2028#define CHECK_CRLF while (data[i] == '\r' || data[i] == '\n') {\
@@ -2031,12 +2031,15 @@ GNUNET_STRINGS_base64_decode (const char *data,
2031 if (i >= len) goto END; \ 2031 if (i >= len) goto END; \
2032 } 2032 }
2033 2033
2034 *output = GNUNET_malloc ((len * 3 / 4) + 8); 2034 output = GNUNET_malloc ((len * 3 / 4) + 8);
2035 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2035 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2036 "base64_decode decoding len=%d\n", 2036 "base64_decode decoding len=%d\n",
2037 (int) len); 2037 (int) len);
2038 for (i = 0; i < len; ++i) 2038 for (size_t i = 0; i < len; ++i)
2039 { 2039 {
2040 char c;
2041 char c1;
2042
2040 CHECK_CRLF; 2043 CHECK_CRLF;
2041 if (FILLCHAR == data[i]) 2044 if (FILLCHAR == data[i])
2042 break; 2045 break;
@@ -2045,7 +2048,7 @@ GNUNET_STRINGS_base64_decode (const char *data,
2045 CHECK_CRLF; 2048 CHECK_CRLF;
2046 c1 = (char) cvtfind (data[i]); 2049 c1 = (char) cvtfind (data[i]);
2047 c = (c << 2) | ((c1 >> 4) & 0x3); 2050 c = (c << 2) | ((c1 >> 4) & 0x3);
2048 (*output)[ret++] = c; 2051 output[ret++] = c;
2049 if (++i < len) 2052 if (++i < len)
2050 { 2053 {
2051 CHECK_CRLF; 2054 CHECK_CRLF;
@@ -2054,7 +2057,7 @@ GNUNET_STRINGS_base64_decode (const char *data,
2054 break; 2057 break;
2055 c = (char) cvtfind (c); 2058 c = (char) cvtfind (c);
2056 c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf); 2059 c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
2057 (*output)[ret++] = c1; 2060 output[ret++] = c1;
2058 } 2061 }
2059 if (++i < len) 2062 if (++i < len)
2060 { 2063 {
@@ -2065,15 +2068,13 @@ GNUNET_STRINGS_base64_decode (const char *data,
2065 2068
2066 c1 = (char) cvtfind (c1); 2069 c1 = (char) cvtfind (c1);
2067 c = ((c << 6) & 0xc0) | c1; 2070 c = ((c << 6) & 0xc0) | c1;
2068 (*output)[ret++] = c; 2071 output[ret++] = c;
2069 } 2072 }
2070 } 2073 }
2071END: 2074END:
2075 *out = output;
2072 return ret; 2076 return ret;
2073} 2077}
2074 2078
2075 2079
2076
2077
2078
2079/* end of strings.c */ 2080/* end of strings.c */
diff --git a/src/util/test_hexcoder.c b/src/util/test_hexcoder.c
new file mode 100644
index 000000000..441d7e200
--- /dev/null
+++ b/src/util/test_hexcoder.c
@@ -0,0 +1,54 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18*/
19/**
20 * @author Christian Grothoff
21 * @file dns/test_hexcoder.c
22 * @brief test for #GNUNET_DNSPARSER_hex_to_bin() and
23 * #GNUNET_DNSPARSER_bin_to_hex()
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_dnsparser_lib.h"
28
29#define TESTSTRING "Hello World!"
30
31
32int
33main (int argc,
34 char *argv[])
35{
36 char buf[strlen (TESTSTRING) + 1];
37 char *ret;
38
39 GNUNET_log_setup ("test-hexcoder", "WARNING", NULL);
40 ret = GNUNET_DNSPARSER_bin_to_hex (TESTSTRING,
41 strlen (TESTSTRING) + 1);
42 GNUNET_assert (NULL != ret);
43 GNUNET_assert (sizeof (buf) ==
44 GNUNET_DNSPARSER_hex_to_bin (ret,
45 buf));
46 GNUNET_assert (0 == memcmp (TESTSTRING,
47 buf,
48 sizeof (buf)));
49 GNUNET_free (ret);
50 return 0;
51}
52
53
54/* end of test_hexcoder.c */
diff --git a/src/util/test_regex.c b/src/util/test_regex.c
new file mode 100644
index 000000000..2e7d52828
--- /dev/null
+++ b/src/util/test_regex.c
@@ -0,0 +1,179 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012, 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18/**
19 * @file tun/test_regex.c
20 * @brief simple test for regex.c iptoregex functions
21 * @author Maximilian Szengel
22 */
23#include "platform.h"
24#include "gnunet_tun_lib.h"
25
26/**
27 * 'wildcard', matches all possible values (for HEX encoding).
28 */
29#define DOT "(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"
30
31
32static int
33test_iptoregex (const char *ipv4,
34 uint16_t port,
35 const char *expectedv4,
36 const char *ipv6,
37 uint16_t port6,
38 const char *expectedv6)
39{
40 int error = 0;
41
42 struct in_addr a;
43 struct in6_addr b;
44 char rxv4[GNUNET_TUN_IPV4_REGEXLEN];
45 char rxv6[GNUNET_TUN_IPV6_REGEXLEN];
46
47 GNUNET_assert (1 == inet_pton (AF_INET, ipv4, &a));
48 GNUNET_TUN_ipv4toregexsearch (&a, port, rxv4);
49
50 if (0 != strcmp (rxv4, expectedv4))
51 {
52 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
53 "Expected: %s but got: %s\n",
54 expectedv4,
55 rxv4);
56 error++;
57 }
58
59 GNUNET_assert (1 == inet_pton (AF_INET6, ipv6, &b));
60 GNUNET_TUN_ipv6toregexsearch (&b, port6, rxv6);
61 if (0 != strcmp (rxv6, expectedv6))
62 {
63 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
64 "Expected: %s but got: %s\n",
65 expectedv6, rxv6);
66 error++;
67 }
68 return error;
69}
70
71
72static int
73test_policy4toregex (const char *policy,
74 const char *regex)
75{
76 char *r;
77 int ret;
78
79 ret = 0;
80 r = GNUNET_TUN_ipv4policy2regex (policy);
81 if (NULL == r)
82 {
83 GNUNET_break (0);
84 return 1;
85 }
86 if (0 != strcmp (regex, r))
87 {
88 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
89 "Expected: `%s' but got: `%s'\n",
90 regex, r);
91 ret = 2;
92 }
93 GNUNET_free (r);
94 return ret;
95}
96
97
98static int
99test_policy6toregex (const char *policy,
100 const char *regex)
101{
102 char *r;
103 int ret;
104
105 ret = 0;
106 r = GNUNET_TUN_ipv6policy2regex (policy);
107 if (NULL == r)
108 {
109 GNUNET_break (0);
110 return 1;
111 }
112 if (0 != strcmp (regex, r))
113 {
114 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
115 "Expected: `%s' but got: `%s'\n",
116 regex, r);
117 ret = 2;
118 }
119 GNUNET_free (r);
120 return ret;
121}
122
123
124int
125main (int argc, char *argv[])
126{
127 int error;
128 char *r;
129
130 GNUNET_log_setup ("test-regex", "WARNING", NULL);
131 error = 0;
132
133 /* this is just a performance test ... */
134 r = GNUNET_TUN_ipv4policy2regex ("1.2.3.4/16:!25;");
135 GNUNET_break (NULL != r);
136 GNUNET_free (r);
137
138 error +=
139 test_iptoregex ("192.1.2.3", 2086,
140 "4-0826-C0010203",
141 "FFFF::1", 8080,
142 "6-1F90-FFFF0000000000000000000000000001");
143 error +=
144 test_iptoregex ("187.238.255.0", 80,
145 "4-0050-BBEEFF00",
146 "E1E1:73F9:51BE::0", 49,
147 "6-0031-E1E173F951BE00000000000000000000");
148 error +=
149 test_policy4toregex ("192.1.2.0/24:80;",
150 "4-0050-C00102" DOT DOT);
151 error +=
152 test_policy4toregex ("192.1.0.0/16;",
153 "4-" DOT DOT DOT DOT "-C001" DOT DOT DOT DOT);
154 error +=
155 test_policy4toregex ("192.1.0.0/16:80-81;",
156 "4-(0050|0051)-C001" DOT DOT DOT DOT);
157 error +=
158 test_policy4toregex ("192.1.0.0/8:!3-65535;",
159 "4-000(0|1|2)-C0" DOT DOT DOT DOT DOT DOT);
160 error +=
161 test_policy4toregex ("192.1.0.0/8:!25-56;",
162 "4-(0(0(0"DOT"|1(0|1|2|3|4|5|6|7|8)|3(9|A|B|C|D|E|F)|(4|5|6|7|8|9|A|B|C|D|E|F)"DOT")|(1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"DOT DOT")|(1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"DOT DOT DOT")-C0"DOT DOT DOT DOT DOT DOT);
163 error +=
164 test_policy6toregex ("E1E1::1;",
165 "6-"DOT DOT DOT DOT"-E1E10000000000000000000000000001");
166 error +=
167 test_policy6toregex ("E1E1:ABCD::1/120;",
168 "6-"DOT DOT DOT DOT"-E1E1ABCD0000000000000000000000" DOT DOT);
169 error +=
170 test_policy6toregex ("E1E1:ABCD::ABCD/126;",
171 "6-"DOT DOT DOT DOT"-E1E1ABCD00000000000000000000ABC(C|D|E|F)");
172 error +=
173 test_policy6toregex ("E1E1:ABCD::ABCD/127;",
174 "6-"DOT DOT DOT DOT"-E1E1ABCD00000000000000000000ABC(C|D)");
175 error +=
176 test_policy6toregex ("E1E1:ABCD::ABCD/128:80;",
177 "6-0050-E1E1ABCD00000000000000000000ABCD");
178 return error;
179}
diff --git a/src/util/test_tun.c b/src/util/test_tun.c
new file mode 100644
index 000000000..edbd4c05d
--- /dev/null
+++ b/src/util/test_tun.c
@@ -0,0 +1,72 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2011, 2012 Christian Grothoff
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file tun/test_tun.c
21 * @brief test for tun.c
22 * @author Christian Grothoff
23 */
24#include "platform.h"
25#include "gnunet_tun_lib.h"
26
27static int ret;
28
29static void
30test_udp (size_t pll,
31 int pl_fill,
32 uint16_t crc)
33{
34 struct GNUNET_TUN_IPv4Header ip;
35 struct GNUNET_TUN_UdpHeader udp;
36 char payload[pll];
37 struct in_addr src;
38 struct in_addr dst;
39
40 GNUNET_assert (1 == inet_pton (AF_INET, "1.2.3.4", &src));
41 GNUNET_assert (1 == inet_pton (AF_INET, "122.2.3.5", &dst));
42 memset (payload, pl_fill, sizeof (payload));
43 GNUNET_TUN_initialize_ipv4_header (&ip,
44 IPPROTO_UDP,
45 pll + sizeof (udp),
46 &src,
47 &dst);
48 udp.source_port = htons (4242);
49 udp.destination_port = htons (4242);
50 udp.len = htons (pll);
51 GNUNET_TUN_calculate_udp4_checksum (&ip,
52 &udp,
53 payload,
54 pll);
55 if (crc != ntohs (udp.crc))
56 {
57 fprintf (stderr, "Got CRC: %u, wanted: %u\n",
58 ntohs (udp.crc),
59 crc);
60 ret = 1;
61 }
62}
63
64int main (int argc,
65 char **argv)
66{
67 test_udp (4, 3, 22439);
68 test_udp (4, 1, 23467);
69 test_udp (7, 17, 6516);
70 test_udp (12451, 251, 42771);
71 return ret;
72}
diff --git a/src/util/tun.c b/src/util/tun.c
new file mode 100644
index 000000000..25e729e58
--- /dev/null
+++ b/src/util/tun.c
@@ -0,0 +1,309 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2011, 2012 Christian Grothoff
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file tun/tun.c
21 * @brief standard IP calculations for TUN interaction
22 * @author Philipp Toelke
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28/**
29 * IP TTL we use for packets that we assemble (8 bit unsigned integer)
30 */
31#define FRESH_TTL 64
32
33
34/**
35 * Initialize an IPv4 header.
36 *
37 * @param ip header to initialize
38 * @param protocol protocol to use (i.e. IPPROTO_UDP)
39 * @param payload_length number of bytes of payload that follow (excluding IPv4 header)
40 * @param src source IP address to use
41 * @param dst destination IP address to use
42 */
43void
44GNUNET_TUN_initialize_ipv4_header (struct GNUNET_TUN_IPv4Header *ip,
45 uint8_t protocol,
46 uint16_t payload_length,
47 const struct in_addr *src,
48 const struct in_addr *dst)
49{
50 GNUNET_assert (20 == sizeof (struct GNUNET_TUN_IPv4Header));
51 GNUNET_assert (payload_length <= UINT16_MAX - sizeof (struct GNUNET_TUN_IPv4Header));
52 memset (ip, 0, sizeof (struct GNUNET_TUN_IPv4Header));
53 ip->header_length = sizeof (struct GNUNET_TUN_IPv4Header) / 4;
54 ip->version = 4;
55 ip->total_length = htons (sizeof (struct GNUNET_TUN_IPv4Header) + payload_length);
56 ip->identification = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
57 65536);
58 ip->ttl = FRESH_TTL;
59 ip->protocol = protocol;
60 ip->source_address = *src;
61 ip->destination_address = *dst;
62 ip->checksum = GNUNET_CRYPTO_crc16_n (ip, sizeof (struct GNUNET_TUN_IPv4Header));
63}
64
65
66/**
67 * Initialize an IPv6 header.
68 *
69 * @param ip header to initialize
70 * @param protocol protocol to use (i.e. IPPROTO_UDP), technically "next_header" for IPv6
71 * @param payload_length number of bytes of payload that follow (excluding IPv6 header)
72 * @param src source IP address to use
73 * @param dst destination IP address to use
74 */
75void
76GNUNET_TUN_initialize_ipv6_header (struct GNUNET_TUN_IPv6Header *ip,
77 uint8_t protocol,
78 uint16_t payload_length,
79 const struct in6_addr *src,
80 const struct in6_addr *dst)
81{
82 GNUNET_assert (40 == sizeof (struct GNUNET_TUN_IPv6Header));
83 GNUNET_assert (payload_length <= UINT16_MAX - sizeof (struct GNUNET_TUN_IPv6Header));
84 memset (ip, 0, sizeof (struct GNUNET_TUN_IPv6Header));
85 ip->version = 6;
86 ip->next_header = protocol;
87 ip->payload_length = htons ((uint16_t) payload_length);
88 ip->hop_limit = FRESH_TTL;
89 ip->destination_address = *dst;
90 ip->source_address = *src;
91}
92
93
94/**
95 * Calculate IPv4 TCP checksum.
96 *
97 * @param ip ipv4 header fully initialized
98 * @param tcp TCP header (initialized except for CRC)
99 * @param payload the TCP payload
100 * @param payload_length number of bytes of TCP payload
101 */
102void
103GNUNET_TUN_calculate_tcp4_checksum (const struct GNUNET_TUN_IPv4Header *ip,
104 struct GNUNET_TUN_TcpHeader *tcp,
105 const void *payload,
106 uint16_t payload_length)
107{
108 uint32_t sum;
109 uint16_t tmp;
110
111 GNUNET_assert (20 == sizeof (struct GNUNET_TUN_TcpHeader));
112 GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_IPv4Header) + sizeof (struct GNUNET_TUN_TcpHeader) ==
113 ntohs (ip->total_length));
114 GNUNET_assert (IPPROTO_TCP == ip->protocol);
115
116 tcp->crc = 0;
117 sum = GNUNET_CRYPTO_crc16_step (0,
118 &ip->source_address,
119 sizeof (struct in_addr) * 2);
120 tmp = htons (IPPROTO_TCP);
121 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint16_t));
122 tmp = htons (payload_length + sizeof (struct GNUNET_TUN_TcpHeader));
123 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint16_t));
124 sum = GNUNET_CRYPTO_crc16_step (sum, tcp, sizeof (struct GNUNET_TUN_TcpHeader));
125 sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
126 tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
127}
128
129
130/**
131 * Calculate IPv6 TCP checksum.
132 *
133 * @param ip ipv6 header fully initialized
134 * @param tcp header (initialized except for CRC)
135 * @param payload the TCP payload
136 * @param payload_length number of bytes of TCP payload
137 */
138void
139GNUNET_TUN_calculate_tcp6_checksum (const struct GNUNET_TUN_IPv6Header *ip,
140 struct GNUNET_TUN_TcpHeader *tcp,
141 const void *payload,
142 uint16_t payload_length)
143{
144 uint32_t sum;
145 uint32_t tmp;
146
147 GNUNET_assert (20 == sizeof (struct GNUNET_TUN_TcpHeader));
148 GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_TcpHeader) ==
149 ntohs (ip->payload_length));
150 GNUNET_assert (IPPROTO_TCP == ip->next_header);
151 tcp->crc = 0;
152 sum = GNUNET_CRYPTO_crc16_step (0, &ip->source_address, 2 * sizeof (struct in6_addr));
153 tmp = htonl (sizeof (struct GNUNET_TUN_TcpHeader) + payload_length);
154 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
155 tmp = htonl (IPPROTO_TCP);
156 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
157 sum = GNUNET_CRYPTO_crc16_step (sum, tcp,
158 sizeof (struct GNUNET_TUN_TcpHeader));
159 sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
160 tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
161}
162
163
164/**
165 * Calculate IPv4 UDP checksum.
166 *
167 * @param ip ipv4 header fully initialized
168 * @param udp UDP header (initialized except for CRC)
169 * @param payload the UDP payload
170 * @param payload_length number of bytes of UDP payload
171 */
172void
173GNUNET_TUN_calculate_udp4_checksum (const struct GNUNET_TUN_IPv4Header *ip,
174 struct GNUNET_TUN_UdpHeader *udp,
175 const void *payload,
176 uint16_t payload_length)
177{
178 uint32_t sum;
179 uint16_t tmp;
180
181 GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
182 GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_IPv4Header) + sizeof (struct GNUNET_TUN_UdpHeader) ==
183 ntohs (ip->total_length));
184 GNUNET_assert (IPPROTO_UDP == ip->protocol);
185
186 udp->crc = 0; /* technically optional, but we calculate it anyway, just to be sure */
187 sum = GNUNET_CRYPTO_crc16_step (0,
188 &ip->source_address,
189 sizeof (struct in_addr) * 2);
190 tmp = htons (IPPROTO_UDP);
191 sum = GNUNET_CRYPTO_crc16_step (sum,
192 &tmp,
193 sizeof (uint16_t));
194 tmp = htons (sizeof (struct GNUNET_TUN_UdpHeader) + payload_length);
195 sum = GNUNET_CRYPTO_crc16_step (sum,
196 &tmp,
197 sizeof (uint16_t));
198 sum = GNUNET_CRYPTO_crc16_step (sum,
199 udp,
200 sizeof (struct GNUNET_TUN_UdpHeader));
201 sum = GNUNET_CRYPTO_crc16_step (sum,
202 payload,
203 payload_length);
204 udp->crc = GNUNET_CRYPTO_crc16_finish (sum);
205}
206
207
208/**
209 * Calculate IPv6 UDP checksum.
210 *
211 * @param ip ipv6 header fully initialized
212 * @param udp UDP header (initialized except for CRC)
213 * @param payload the UDP payload
214 * @param payload_length number of bytes of UDP payload
215 */
216void
217GNUNET_TUN_calculate_udp6_checksum (const struct GNUNET_TUN_IPv6Header *ip,
218 struct GNUNET_TUN_UdpHeader *udp,
219 const void *payload,
220 uint16_t payload_length)
221{
222 uint32_t sum;
223 uint32_t tmp;
224
225 GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_UdpHeader) ==
226 ntohs (ip->payload_length));
227 GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_UdpHeader) ==
228 ntohs (udp->len));
229 GNUNET_assert (IPPROTO_UDP == ip->next_header);
230
231 udp->crc = 0;
232 sum = GNUNET_CRYPTO_crc16_step (0,
233 &ip->source_address,
234 sizeof (struct in6_addr) * 2);
235 tmp = htons (sizeof (struct GNUNET_TUN_UdpHeader) + payload_length); /* aka udp->len */
236 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
237 tmp = htons (ip->next_header);
238 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
239 sum = GNUNET_CRYPTO_crc16_step (sum, udp, sizeof (struct GNUNET_TUN_UdpHeader));
240 sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
241 udp->crc = GNUNET_CRYPTO_crc16_finish (sum);
242}
243
244
245/**
246 * Calculate ICMP checksum.
247 *
248 * @param icmp IMCP header (initialized except for CRC)
249 * @param payload the ICMP payload
250 * @param payload_length number of bytes of ICMP payload
251 */
252void
253GNUNET_TUN_calculate_icmp_checksum (struct GNUNET_TUN_IcmpHeader *icmp,
254 const void *payload,
255 uint16_t payload_length)
256{
257 uint32_t sum;
258
259 GNUNET_assert (8 == sizeof (struct GNUNET_TUN_IcmpHeader));
260 icmp->crc = 0;
261 sum = GNUNET_CRYPTO_crc16_step (0,
262 icmp,
263 sizeof (struct GNUNET_TUN_IcmpHeader));
264 sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
265 icmp->crc = GNUNET_CRYPTO_crc16_finish (sum);
266}
267
268
269/**
270 * Check if two sockaddrs are equal.
271 *
272 * @param sa one address
273 * @param sb another address
274 * @param include_port also check ports
275 * @return #GNUNET_YES if they are equal
276 */
277int
278GNUNET_TUN_sockaddr_cmp (const struct sockaddr *sa,
279 const struct sockaddr *sb,
280 int include_port)
281{
282 if (sa->sa_family != sb->sa_family)
283 return GNUNET_NO;
284
285 switch (sa->sa_family)
286 {
287 case AF_INET:
288 {
289 const struct sockaddr_in *sa4 = (const struct sockaddr_in *) sa;
290 const struct sockaddr_in *sb4 = (const struct sockaddr_in *) sb;
291 return (sa4->sin_addr.s_addr == sb4->sin_addr.s_addr);
292 }
293 case AF_INET6:
294 {
295 const struct sockaddr_in6 *sa6 = (const struct sockaddr_in6 *) sa;
296 const struct sockaddr_in6 *sb6 = (const struct sockaddr_in6 *) sb;
297
298 return (0 == memcmp(&sa6->sin6_addr,
299 &sb6->sin6_addr,
300 sizeof (struct in6_addr)));
301 }
302 default:
303 GNUNET_break (0);
304 return GNUNET_SYSERR;
305 }
306}
307
308
309/* end of tun.c */