diff options
Diffstat (limited to 'src/hello/hello.c')
-rw-r--r-- | src/hello/hello.c | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/src/hello/hello.c b/src/hello/hello.c new file mode 100644 index 000000000..5a21bd41f --- /dev/null +++ b/src/hello/hello.c | |||
@@ -0,0 +1,482 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2009 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | 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 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file hello/hello.c | ||
23 | * @brief helper library for handling HELLOs | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_hello_lib.h" | ||
28 | #include "gnunet_protocols.h" | ||
29 | #include "gnunet_server_lib.h" | ||
30 | |||
31 | /** | ||
32 | * A HELLO message is used to exchange information about | ||
33 | * transports with other peers. This struct is always | ||
34 | * followed by the actual network addresses which have | ||
35 | * the format: | ||
36 | * | ||
37 | * 1) transport-name (0-terminated) | ||
38 | * 2) address-length (uint32_t, network byte order; possibly | ||
39 | * unaligned!) | ||
40 | * 3) address expiration (GNUNET_TIME_AbsoluteNBO); possibly | ||
41 | * unaligned!) | ||
42 | * 4) address (address-length bytes; possibly unaligned!) | ||
43 | */ | ||
44 | struct GNUNET_HELLO_Message | ||
45 | { | ||
46 | /** | ||
47 | * Type will be GNUNET_MESSAGE_TYPE_HELLO. | ||
48 | */ | ||
49 | struct GNUNET_MessageHeader header; | ||
50 | |||
51 | /** | ||
52 | * Always zero (for alignment). | ||
53 | */ | ||
54 | uint32_t reserved GNUNET_PACKED; | ||
55 | |||
56 | /** | ||
57 | * The public key of the peer. | ||
58 | */ | ||
59 | struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey; | ||
60 | |||
61 | }; | ||
62 | |||
63 | |||
64 | /** | ||
65 | * Copy the given address information into | ||
66 | * the given buffer using the format of HELLOs. | ||
67 | * | ||
68 | * @param tname name of the transport plugin | ||
69 | * @param expiration expiration for the address | ||
70 | * @param addr the address | ||
71 | * @param addr_len length of the address in bytes | ||
72 | * @param target where to copy the address | ||
73 | * @param max maximum number of bytes to copy to target | ||
74 | * @return number of bytes copied, 0 if | ||
75 | * the target buffer was not big enough. | ||
76 | */ | ||
77 | size_t | ||
78 | GNUNET_HELLO_add_address (const char *tname, | ||
79 | struct GNUNET_TIME_Absolute expiration, | ||
80 | const void *addr, | ||
81 | size_t addr_len, char *target, size_t max) | ||
82 | { | ||
83 | uint32_t alen; | ||
84 | size_t slen; | ||
85 | struct GNUNET_TIME_AbsoluteNBO exp; | ||
86 | |||
87 | slen = strlen (tname) + 1; | ||
88 | if (slen + sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) + | ||
89 | addr_len > max) | ||
90 | return 0; | ||
91 | exp = GNUNET_TIME_absolute_hton (expiration); | ||
92 | alen = htonl ((uint32_t) addr_len); | ||
93 | memcpy (target, tname, slen); | ||
94 | memcpy (&target[slen], &alen, sizeof (uint32_t)); | ||
95 | slen += sizeof (uint32_t); | ||
96 | memcpy (&target[slen], &exp, sizeof (struct GNUNET_TIME_AbsoluteNBO)); | ||
97 | slen += sizeof (struct GNUNET_TIME_AbsoluteNBO); | ||
98 | memcpy (&target[slen], addr, addr_len); | ||
99 | slen += addr_len; | ||
100 | return slen; | ||
101 | } | ||
102 | |||
103 | |||
104 | /** | ||
105 | * Get the size of an address entry in a HELLO message. | ||
106 | * | ||
107 | * @param buf pointer to the start of the address entry | ||
108 | * @param max maximum size of the entry (end of buf) | ||
109 | * @param ralen set to the address length | ||
110 | * @return size of the entry, or 0 if max is not large enough | ||
111 | */ | ||
112 | static size_t | ||
113 | get_hello_address_size (const char *buf, size_t max, uint32_t * ralen) | ||
114 | { | ||
115 | const char *pos; | ||
116 | uint32_t alen; | ||
117 | size_t left; | ||
118 | size_t slen; | ||
119 | |||
120 | left = max; | ||
121 | pos = buf; | ||
122 | slen = 1; | ||
123 | while ((left > 0) && ('\0' != *pos)) | ||
124 | { | ||
125 | left--; | ||
126 | pos++; | ||
127 | slen++; | ||
128 | } | ||
129 | if ('\0' != *pos) | ||
130 | { | ||
131 | /* 0-termination not found */ | ||
132 | GNUNET_break_op (0); | ||
133 | return 0; | ||
134 | } | ||
135 | pos++; | ||
136 | if (left < sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO)) | ||
137 | { | ||
138 | /* not enough space for addrlen */ | ||
139 | GNUNET_break_op (0); | ||
140 | return 0; | ||
141 | } | ||
142 | memcpy (&alen, pos, sizeof (uint32_t)); | ||
143 | alen = ntohl (alen); | ||
144 | *ralen = alen; | ||
145 | slen += alen + sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO); | ||
146 | if (max < slen) | ||
147 | { | ||
148 | /* not enough space for addr */ | ||
149 | GNUNET_break_op (0); | ||
150 | return 0; | ||
151 | } | ||
152 | return slen; | ||
153 | } | ||
154 | |||
155 | |||
156 | /** | ||
157 | * Construct a HELLO message given the public key, | ||
158 | * expiration time and an iterator that spews the | ||
159 | * transport addresses. | ||
160 | * | ||
161 | * @return the hello message | ||
162 | */ | ||
163 | struct GNUNET_HELLO_Message * | ||
164 | GNUNET_HELLO_create (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded | ||
165 | *publicKey, | ||
166 | GNUNET_HELLO_GenerateAddressListCallback addrgen, | ||
167 | void *addrgen_cls) | ||
168 | { | ||
169 | char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 256 - | ||
170 | sizeof (struct GNUNET_HELLO_Message)]; | ||
171 | size_t max; | ||
172 | size_t used; | ||
173 | size_t ret; | ||
174 | struct GNUNET_HELLO_Message *hello; | ||
175 | |||
176 | max = sizeof (buffer); | ||
177 | used = 0; | ||
178 | while (0 != (ret = addrgen (addrgen_cls, max, &buffer[used]))) | ||
179 | { | ||
180 | max -= ret; | ||
181 | used += ret; | ||
182 | } | ||
183 | hello = GNUNET_malloc (sizeof (struct GNUNET_HELLO_Message) + used); | ||
184 | hello->header.type = htons (GNUNET_MESSAGE_TYPE_HELLO); | ||
185 | hello->header.size = htons (sizeof (struct GNUNET_HELLO_Message) + used); | ||
186 | memcpy (&hello->publicKey, publicKey, | ||
187 | sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); | ||
188 | memcpy (&hello[1], buffer, used); | ||
189 | return hello; | ||
190 | } | ||
191 | |||
192 | |||
193 | /** | ||
194 | * Iterate over all of the addresses in the HELLO. | ||
195 | * | ||
196 | * @param msg HELLO to iterate over | ||
197 | * @param return_modified if a modified copy should be returned, | ||
198 | * otherwise NULL will be returned | ||
199 | * @param it iterator to call on each address | ||
200 | * @param it_cls closure for it | ||
201 | */ | ||
202 | struct GNUNET_HELLO_Message * | ||
203 | GNUNET_HELLO_iterate_addresses (const struct GNUNET_HELLO_Message *msg, | ||
204 | int return_modified, | ||
205 | GNUNET_HELLO_AddressIterator it, void *it_cls) | ||
206 | { | ||
207 | uint16_t msize; | ||
208 | struct GNUNET_HELLO_Message *ret; | ||
209 | const char *inptr; | ||
210 | size_t insize; | ||
211 | size_t esize; | ||
212 | size_t wpos; | ||
213 | char *woff; | ||
214 | uint32_t alen; | ||
215 | struct GNUNET_TIME_AbsoluteNBO expire; | ||
216 | int iret; | ||
217 | |||
218 | msize = GNUNET_HELLO_size (msg); | ||
219 | if ((msize < sizeof (struct GNUNET_HELLO_Message)) || | ||
220 | (ntohs (msg->header.type) != GNUNET_MESSAGE_TYPE_HELLO)) | ||
221 | return NULL; | ||
222 | ret = NULL; | ||
223 | if (return_modified) | ||
224 | { | ||
225 | ret = GNUNET_malloc (msize); | ||
226 | memcpy (ret, msg, msize); | ||
227 | } | ||
228 | inptr = (const char *) &msg[1]; | ||
229 | insize = msize - sizeof (struct GNUNET_HELLO_Message); | ||
230 | wpos = 0; | ||
231 | woff = (ret != NULL) ? (char *) &ret[1] : NULL; | ||
232 | while (insize > 0) | ||
233 | { | ||
234 | esize = get_hello_address_size (inptr, insize, &alen); | ||
235 | if (esize == 0) | ||
236 | { | ||
237 | GNUNET_break (0); | ||
238 | GNUNET_free_non_null (ret); | ||
239 | return NULL; | ||
240 | } | ||
241 | memcpy (&expire, | ||
242 | &inptr[esize - alen - sizeof (struct GNUNET_TIME_AbsoluteNBO)], | ||
243 | sizeof (struct GNUNET_TIME_AbsoluteNBO)); | ||
244 | iret = it (it_cls, | ||
245 | inptr, | ||
246 | GNUNET_TIME_absolute_ntoh (expire), | ||
247 | &inptr[esize - alen], alen); | ||
248 | if (iret == GNUNET_SYSERR) | ||
249 | { | ||
250 | if (ret != NULL) | ||
251 | ret->header.size = | ||
252 | ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos); | ||
253 | return ret; | ||
254 | } | ||
255 | if ((iret == GNUNET_OK) && (ret != NULL)) | ||
256 | { | ||
257 | memcpy (woff, inptr, esize); | ||
258 | woff += esize; | ||
259 | wpos += esize; | ||
260 | } | ||
261 | insize -= esize; | ||
262 | inptr += esize; | ||
263 | } | ||
264 | if (ret != NULL) | ||
265 | ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos); | ||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | |||
270 | struct ExpireContext | ||
271 | { | ||
272 | const void *addr; | ||
273 | size_t addrlen; | ||
274 | int found; | ||
275 | struct GNUNET_TIME_Absolute expiration; | ||
276 | }; | ||
277 | |||
278 | |||
279 | static int | ||
280 | get_match_exp (void *cls, | ||
281 | const char *tname, | ||
282 | struct GNUNET_TIME_Absolute expiration, | ||
283 | const void *addr, size_t addrlen) | ||
284 | { | ||
285 | struct ExpireContext *ec = cls; | ||
286 | |||
287 | if ((addrlen == ec->addrlen) && (0 == memcmp (addr, ec->addr, addrlen))) | ||
288 | { | ||
289 | ec->found = GNUNET_YES; | ||
290 | ec->expiration = expiration; | ||
291 | return GNUNET_SYSERR; /* done here */ | ||
292 | } | ||
293 | return GNUNET_OK; | ||
294 | } | ||
295 | |||
296 | |||
297 | struct MergeContext | ||
298 | { | ||
299 | const struct GNUNET_HELLO_Message *h1; | ||
300 | const struct GNUNET_HELLO_Message *h2; | ||
301 | const struct GNUNET_HELLO_Message *other; | ||
302 | char *buf; | ||
303 | size_t max; | ||
304 | size_t ret; | ||
305 | int take_equal; | ||
306 | |||
307 | }; | ||
308 | |||
309 | |||
310 | static int | ||
311 | copy_latest (void *cls, | ||
312 | const char *tname, | ||
313 | struct GNUNET_TIME_Absolute expiration, | ||
314 | const void *addr, size_t addrlen) | ||
315 | { | ||
316 | struct MergeContext *mc = cls; | ||
317 | struct ExpireContext ec; | ||
318 | |||
319 | ec.addr = addr; | ||
320 | ec.addrlen = addrlen; | ||
321 | ec.found = GNUNET_NO; | ||
322 | GNUNET_HELLO_iterate_addresses (mc->other, GNUNET_NO, &get_match_exp, &ec); | ||
323 | if ((ec.found == GNUNET_NO) || | ||
324 | ((ec.expiration.value < expiration.value) || | ||
325 | ((ec.expiration.value == expiration.value) && | ||
326 | (mc->take_equal == GNUNET_YES)))) | ||
327 | mc->ret += GNUNET_HELLO_add_address (tname, | ||
328 | expiration, | ||
329 | addr, | ||
330 | addrlen, | ||
331 | &mc->buf[mc->ret], | ||
332 | mc->max - mc->ret); | ||
333 | return GNUNET_OK; | ||
334 | } | ||
335 | |||
336 | |||
337 | static size_t | ||
338 | merge_addr (void *cls, size_t max, void *buf) | ||
339 | { | ||
340 | struct MergeContext *mc = cls; | ||
341 | |||
342 | if (mc->h1 == NULL) | ||
343 | return 0; | ||
344 | mc->ret = 0; | ||
345 | mc->max = max; | ||
346 | mc->buf = buf; | ||
347 | mc->take_equal = GNUNET_NO; | ||
348 | mc->other = mc->h2; | ||
349 | GNUNET_HELLO_iterate_addresses (mc->h1, GNUNET_NO, ©_latest, mc); | ||
350 | mc->take_equal = GNUNET_YES; | ||
351 | mc->other = mc->h1; | ||
352 | GNUNET_HELLO_iterate_addresses (mc->h2, GNUNET_NO, ©_latest, mc); | ||
353 | mc->h1 = NULL; | ||
354 | return mc->ret; | ||
355 | } | ||
356 | |||
357 | |||
358 | /** | ||
359 | * Construct a HELLO message by merging the | ||
360 | * addresses in two existing HELLOs (which | ||
361 | * must be for the same peer). | ||
362 | * | ||
363 | * @param h1 first HELLO message | ||
364 | * @param h2 the second HELLO message | ||
365 | * @return the combined hello message | ||
366 | */ | ||
367 | struct GNUNET_HELLO_Message * | ||
368 | GNUNET_HELLO_merge (const struct GNUNET_HELLO_Message *h1, | ||
369 | const struct GNUNET_HELLO_Message *h2) | ||
370 | { | ||
371 | struct MergeContext mc = { h1, h2, NULL, NULL, 0, 0, 0 }; | ||
372 | |||
373 | return GNUNET_HELLO_create (&h1->publicKey, &merge_addr, &mc); | ||
374 | } | ||
375 | |||
376 | |||
377 | struct DeltaContext | ||
378 | { | ||
379 | struct GNUNET_TIME_Absolute expiration_limit; | ||
380 | |||
381 | GNUNET_HELLO_AddressIterator it; | ||
382 | |||
383 | void *it_cls; | ||
384 | |||
385 | const struct GNUNET_HELLO_Message *old_hello; | ||
386 | }; | ||
387 | |||
388 | |||
389 | static int | ||
390 | delta_match (void *cls, | ||
391 | const char *tname, | ||
392 | struct GNUNET_TIME_Absolute expiration, | ||
393 | const void *addr, size_t addrlen) | ||
394 | { | ||
395 | struct DeltaContext *dc = cls; | ||
396 | int ret; | ||
397 | struct ExpireContext ec; | ||
398 | |||
399 | ec.addr = addr; | ||
400 | ec.addrlen = addrlen; | ||
401 | ec.found = GNUNET_NO; | ||
402 | GNUNET_HELLO_iterate_addresses (dc->old_hello, | ||
403 | GNUNET_NO, &get_match_exp, &ec); | ||
404 | if ((ec.found == GNUNET_YES) && | ||
405 | ((ec.expiration.value > expiration.value) || | ||
406 | (ec.expiration.value >= dc->expiration_limit.value))) | ||
407 | return GNUNET_YES; /* skip */ | ||
408 | ret = dc->it (dc->it_cls, tname, expiration, addr, addrlen); | ||
409 | return ret; | ||
410 | } | ||
411 | |||
412 | |||
413 | /** | ||
414 | * Iterate over addresses in "new_hello" that | ||
415 | * are NOT already present in "old_hello". | ||
416 | * | ||
417 | * @param new_hello a HELLO message | ||
418 | * @param old_hello a HELLO message | ||
419 | * @param expiration_limit ignore addresses in old_hello | ||
420 | * that expired before the given time stamp | ||
421 | * @param it iterator to call on each address | ||
422 | * @param it_cls closure for it | ||
423 | */ | ||
424 | void | ||
425 | GNUNET_HELLO_iterate_new_addresses (const struct GNUNET_HELLO_Message | ||
426 | *new_hello, | ||
427 | const struct GNUNET_HELLO_Message | ||
428 | *old_hello, | ||
429 | struct GNUNET_TIME_Absolute | ||
430 | expiration_limit, | ||
431 | GNUNET_HELLO_AddressIterator it, | ||
432 | void *it_cls) | ||
433 | { | ||
434 | struct DeltaContext dc; | ||
435 | |||
436 | dc.expiration_limit = expiration_limit; | ||
437 | dc.it = it; | ||
438 | dc.it_cls = it_cls; | ||
439 | dc.old_hello = old_hello; | ||
440 | GNUNET_HELLO_iterate_addresses (new_hello, GNUNET_NO, &delta_match, &dc); | ||
441 | } | ||
442 | |||
443 | |||
444 | /** | ||
445 | * Return the size of the given HELLO message. | ||
446 | * @param hello to inspect | ||
447 | * @return the size, 0 if HELLO is invalid | ||
448 | */ | ||
449 | uint16_t | ||
450 | GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello) | ||
451 | { | ||
452 | uint16_t ret = ntohs (hello->header.size); | ||
453 | if ((ret < sizeof (struct GNUNET_HELLO_Message)) || | ||
454 | (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO)) | ||
455 | return 0; | ||
456 | return ret; | ||
457 | } | ||
458 | |||
459 | |||
460 | /** | ||
461 | * Get the public key from a HELLO message. | ||
462 | * | ||
463 | * @param hello the hello message | ||
464 | * @param publicKey where to copy the public key information, can be NULL | ||
465 | * @return GNUNET_SYSERR if the HELLO was malformed | ||
466 | */ | ||
467 | int | ||
468 | GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello, | ||
469 | struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded | ||
470 | *publicKey) | ||
471 | { | ||
472 | uint16_t ret = ntohs (hello->header.size); | ||
473 | if ((ret < sizeof (struct GNUNET_HELLO_Message)) || | ||
474 | (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO)) | ||
475 | return GNUNET_SYSERR; | ||
476 | *publicKey = hello->publicKey; | ||
477 | return GNUNET_OK; | ||
478 | } | ||
479 | |||
480 | |||
481 | |||
482 | /* end of hello.c */ | ||