diff options
-rw-r--r-- | src/arm/gnunet-service-arm.c | 395 |
1 files changed, 391 insertions, 4 deletions
diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c index 5bf823656..7af3659a4 100644 --- a/src/arm/gnunet-service-arm.c +++ b/src/arm/gnunet-service-arm.c | |||
@@ -29,6 +29,11 @@ | |||
29 | #include "gnunet_protocols.h" | 29 | #include "gnunet_protocols.h" |
30 | #include "arm.h" | 30 | #include "arm.h" |
31 | 31 | ||
32 | #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) | ||
33 | |||
34 | #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall) | ||
35 | |||
36 | |||
32 | #if HAVE_WAIT4 | 37 | #if HAVE_WAIT4 |
33 | /** | 38 | /** |
34 | * Name of the file for writing resource utilization summaries to. | 39 | * Name of the file for writing resource utilization summaries to. |
@@ -260,6 +265,388 @@ static struct GNUNET_NotificationContext *notifier; | |||
260 | 265 | ||
261 | 266 | ||
262 | /** | 267 | /** |
268 | * Add the given UNIX domain path as an address to the | ||
269 | * list (as the first entry). | ||
270 | * | ||
271 | * @param saddrs array to update | ||
272 | * @param saddrlens where to store the address length | ||
273 | * @param unixpath path to add | ||
274 | * @param abstract #GNUNET_YES to add an abstract UNIX domain socket. This | ||
275 | * parameter is ignore on systems other than LINUX | ||
276 | */ | ||
277 | static void | ||
278 | add_unixpath (struct sockaddr **saddrs, | ||
279 | socklen_t *saddrlens, | ||
280 | const char *unixpath, | ||
281 | int abstract) | ||
282 | { | ||
283 | #ifdef AF_UNIX | ||
284 | struct sockaddr_un *un; | ||
285 | |||
286 | un = GNUNET_new (struct sockaddr_un); | ||
287 | un->sun_family = AF_UNIX; | ||
288 | strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1); | ||
289 | #ifdef LINUX | ||
290 | if (GNUNET_YES == abstract) | ||
291 | un->sun_path[0] = '\0'; | ||
292 | #endif | ||
293 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
294 | un->sun_len = (u_char) sizeof (struct sockaddr_un); | ||
295 | #endif | ||
296 | *saddrs = (struct sockaddr *) un; | ||
297 | *saddrlens = sizeof (struct sockaddr_un); | ||
298 | #else | ||
299 | /* this function should never be called | ||
300 | * unless AF_UNIX is defined! */ | ||
301 | GNUNET_assert (0); | ||
302 | #endif | ||
303 | } | ||
304 | |||
305 | |||
306 | /** | ||
307 | * Get the list of addresses that a server for the given service | ||
308 | * should bind to. | ||
309 | * | ||
310 | * @param service_name name of the service | ||
311 | * @param cfg configuration (which specifies the addresses) | ||
312 | * @param addrs set (call by reference) to an array of pointers to the | ||
313 | * addresses the server should bind to and listen on; the | ||
314 | * array will be NULL-terminated (on success) | ||
315 | * @param addr_lens set (call by reference) to an array of the lengths | ||
316 | * of the respective `struct sockaddr` struct in the @a addrs | ||
317 | * array (on success) | ||
318 | * @return number of addresses found on success, | ||
319 | * #GNUNET_SYSERR if the configuration | ||
320 | * did not specify reasonable finding information or | ||
321 | * if it specified a hostname that could not be resolved; | ||
322 | * #GNUNET_NO if the number of addresses configured is | ||
323 | * zero (in this case, `*addrs` and `*addr_lens` will be | ||
324 | * set to NULL). | ||
325 | */ | ||
326 | static int | ||
327 | get_server_addresses (const char *service_name, | ||
328 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
329 | struct sockaddr ***addrs, | ||
330 | socklen_t ** addr_lens) | ||
331 | { | ||
332 | int disablev6; | ||
333 | struct GNUNET_NETWORK_Handle *desc; | ||
334 | unsigned long long port; | ||
335 | char *unixpath; | ||
336 | struct addrinfo hints; | ||
337 | struct addrinfo *res; | ||
338 | struct addrinfo *pos; | ||
339 | struct addrinfo *next; | ||
340 | unsigned int i; | ||
341 | int resi; | ||
342 | int ret; | ||
343 | int abstract; | ||
344 | struct sockaddr **saddrs; | ||
345 | socklen_t *saddrlens; | ||
346 | char *hostname; | ||
347 | |||
348 | *addrs = NULL; | ||
349 | *addr_lens = NULL; | ||
350 | desc = NULL; | ||
351 | if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "DISABLEV6")) | ||
352 | { | ||
353 | if (GNUNET_SYSERR == | ||
354 | (disablev6 = | ||
355 | GNUNET_CONFIGURATION_get_value_yesno (cfg, service_name, "DISABLEV6"))) | ||
356 | return GNUNET_SYSERR; | ||
357 | } | ||
358 | else | ||
359 | disablev6 = GNUNET_NO; | ||
360 | |||
361 | if (! disablev6) | ||
362 | { | ||
363 | /* probe IPv6 support */ | ||
364 | desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); | ||
365 | if (NULL == desc) | ||
366 | { | ||
367 | if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) || | ||
368 | (EACCES == errno)) | ||
369 | { | ||
370 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); | ||
371 | return GNUNET_SYSERR; | ||
372 | } | ||
373 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
374 | _("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"), | ||
375 | service_name, STRERROR (errno)); | ||
376 | disablev6 = GNUNET_YES; | ||
377 | } | ||
378 | else | ||
379 | { | ||
380 | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); | ||
381 | desc = NULL; | ||
382 | } | ||
383 | } | ||
384 | |||
385 | port = 0; | ||
386 | if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "PORT")) | ||
387 | { | ||
388 | if (GNUNET_OK != | ||
389 | GNUNET_CONFIGURATION_get_value_number (cfg, service_name, | ||
390 | "PORT", &port)) | ||
391 | { | ||
392 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
393 | _("Require valid port number for service `%s' in configuration!\n"), | ||
394 | service_name); | ||
395 | } | ||
396 | if (port > 65535) | ||
397 | { | ||
398 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
399 | _("Require valid port number for service `%s' in configuration!\n"), | ||
400 | service_name); | ||
401 | return GNUNET_SYSERR; | ||
402 | } | ||
403 | } | ||
404 | |||
405 | if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "BINDTO")) | ||
406 | { | ||
407 | GNUNET_break (GNUNET_OK == | ||
408 | GNUNET_CONFIGURATION_get_value_string (cfg, service_name, | ||
409 | "BINDTO", &hostname)); | ||
410 | } | ||
411 | else | ||
412 | hostname = NULL; | ||
413 | |||
414 | unixpath = NULL; | ||
415 | abstract = GNUNET_NO; | ||
416 | #ifdef AF_UNIX | ||
417 | if ((GNUNET_YES == | ||
418 | GNUNET_CONFIGURATION_have_value (cfg, service_name, "UNIXPATH")) && | ||
419 | (GNUNET_OK == | ||
420 | GNUNET_CONFIGURATION_get_value_filename (cfg, service_name, "UNIXPATH", | ||
421 | &unixpath)) && | ||
422 | (0 < strlen (unixpath))) | ||
423 | { | ||
424 | /* probe UNIX support */ | ||
425 | struct sockaddr_un s_un; | ||
426 | |||
427 | if (strlen (unixpath) >= sizeof (s_un.sun_path)) | ||
428 | { | ||
429 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
430 | _("UNIXPATH `%s' too long, maximum length is %llu\n"), unixpath, | ||
431 | (unsigned long long) sizeof (s_un.sun_path)); | ||
432 | unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath); | ||
433 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
434 | _("Using `%s' instead\n"), | ||
435 | unixpath); | ||
436 | } | ||
437 | #ifdef LINUX | ||
438 | abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
439 | "TESTING", | ||
440 | "USE_ABSTRACT_SOCKETS"); | ||
441 | if (GNUNET_SYSERR == abstract) | ||
442 | abstract = GNUNET_NO; | ||
443 | #endif | ||
444 | if ((GNUNET_YES != abstract) | ||
445 | && (GNUNET_OK != | ||
446 | GNUNET_DISK_directory_create_for_file (unixpath))) | ||
447 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
448 | "mkdir", | ||
449 | unixpath); | ||
450 | } | ||
451 | if (NULL != unixpath) | ||
452 | { | ||
453 | desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0); | ||
454 | if (NULL == desc) | ||
455 | { | ||
456 | if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) || | ||
457 | (EACCES == errno)) | ||
458 | { | ||
459 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); | ||
460 | GNUNET_free_non_null (hostname); | ||
461 | GNUNET_free (unixpath); | ||
462 | return GNUNET_SYSERR; | ||
463 | } | ||
464 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
465 | _("Disabling UNIX domain socket support for service `%s', failed to create UNIX domain socket: %s\n"), | ||
466 | service_name, | ||
467 | STRERROR (errno)); | ||
468 | GNUNET_free (unixpath); | ||
469 | unixpath = NULL; | ||
470 | } | ||
471 | else | ||
472 | { | ||
473 | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); | ||
474 | desc = NULL; | ||
475 | } | ||
476 | } | ||
477 | #endif | ||
478 | |||
479 | if ((0 == port) && (NULL == unixpath)) | ||
480 | { | ||
481 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
482 | _("Have neither PORT nor UNIXPATH for service `%s', but one is required\n"), | ||
483 | service_name); | ||
484 | GNUNET_free_non_null (hostname); | ||
485 | return GNUNET_SYSERR; | ||
486 | } | ||
487 | if (0 == port) | ||
488 | { | ||
489 | saddrs = GNUNET_malloc (2 * sizeof (struct sockaddr *)); | ||
490 | saddrlens = GNUNET_malloc (2 * sizeof (socklen_t)); | ||
491 | add_unixpath (saddrs, saddrlens, unixpath, abstract); | ||
492 | GNUNET_free_non_null (unixpath); | ||
493 | GNUNET_free_non_null (hostname); | ||
494 | *addrs = saddrs; | ||
495 | *addr_lens = saddrlens; | ||
496 | return 1; | ||
497 | } | ||
498 | |||
499 | if (NULL != hostname) | ||
500 | { | ||
501 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
502 | "Resolving `%s' since that is where `%s' will bind to.\n", | ||
503 | hostname, | ||
504 | service_name); | ||
505 | memset (&hints, 0, sizeof (struct addrinfo)); | ||
506 | if (disablev6) | ||
507 | hints.ai_family = AF_INET; | ||
508 | hints.ai_protocol = IPPROTO_TCP; | ||
509 | if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) || | ||
510 | (NULL == res)) | ||
511 | { | ||
512 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
513 | _("Failed to resolve `%s': %s\n"), | ||
514 | hostname, | ||
515 | gai_strerror (ret)); | ||
516 | GNUNET_free (hostname); | ||
517 | GNUNET_free_non_null (unixpath); | ||
518 | return GNUNET_SYSERR; | ||
519 | } | ||
520 | next = res; | ||
521 | i = 0; | ||
522 | while (NULL != (pos = next)) | ||
523 | { | ||
524 | next = pos->ai_next; | ||
525 | if ((disablev6) && (pos->ai_family == AF_INET6)) | ||
526 | continue; | ||
527 | i++; | ||
528 | } | ||
529 | if (0 == i) | ||
530 | { | ||
531 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
532 | _("Failed to find %saddress for `%s'.\n"), | ||
533 | disablev6 ? "IPv4 " : "", | ||
534 | hostname); | ||
535 | freeaddrinfo (res); | ||
536 | GNUNET_free (hostname); | ||
537 | GNUNET_free_non_null (unixpath); | ||
538 | return GNUNET_SYSERR; | ||
539 | } | ||
540 | resi = i; | ||
541 | if (NULL != unixpath) | ||
542 | resi++; | ||
543 | saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); | ||
544 | saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); | ||
545 | i = 0; | ||
546 | if (NULL != unixpath) | ||
547 | { | ||
548 | add_unixpath (saddrs, saddrlens, unixpath, abstract); | ||
549 | i++; | ||
550 | } | ||
551 | next = res; | ||
552 | while (NULL != (pos = next)) | ||
553 | { | ||
554 | next = pos->ai_next; | ||
555 | if ((disablev6) && (AF_INET6 == pos->ai_family)) | ||
556 | continue; | ||
557 | if ((IPPROTO_TCP != pos->ai_protocol) && (0 != pos->ai_protocol)) | ||
558 | continue; /* not TCP */ | ||
559 | if ((SOCK_STREAM != pos->ai_socktype) && (0 != pos->ai_socktype)) | ||
560 | continue; /* huh? */ | ||
561 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Service `%s' will bind to `%s'\n", | ||
562 | service_name, GNUNET_a2s (pos->ai_addr, pos->ai_addrlen)); | ||
563 | if (AF_INET == pos->ai_family) | ||
564 | { | ||
565 | GNUNET_assert (sizeof (struct sockaddr_in) == pos->ai_addrlen); | ||
566 | saddrlens[i] = pos->ai_addrlen; | ||
567 | saddrs[i] = GNUNET_malloc (saddrlens[i]); | ||
568 | GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); | ||
569 | ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); | ||
570 | } | ||
571 | else | ||
572 | { | ||
573 | GNUNET_assert (AF_INET6 == pos->ai_family); | ||
574 | GNUNET_assert (sizeof (struct sockaddr_in6) == pos->ai_addrlen); | ||
575 | saddrlens[i] = pos->ai_addrlen; | ||
576 | saddrs[i] = GNUNET_malloc (saddrlens[i]); | ||
577 | GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); | ||
578 | ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); | ||
579 | } | ||
580 | i++; | ||
581 | } | ||
582 | GNUNET_free (hostname); | ||
583 | freeaddrinfo (res); | ||
584 | resi = i; | ||
585 | } | ||
586 | else | ||
587 | { | ||
588 | /* will bind against everything, just set port */ | ||
589 | if (disablev6) | ||
590 | { | ||
591 | /* V4-only */ | ||
592 | resi = 1; | ||
593 | if (NULL != unixpath) | ||
594 | resi++; | ||
595 | i = 0; | ||
596 | saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); | ||
597 | saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); | ||
598 | if (NULL != unixpath) | ||
599 | { | ||
600 | add_unixpath (saddrs, saddrlens, unixpath, abstract); | ||
601 | i++; | ||
602 | } | ||
603 | saddrlens[i] = sizeof (struct sockaddr_in); | ||
604 | saddrs[i] = GNUNET_malloc (saddrlens[i]); | ||
605 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
606 | ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i]; | ||
607 | #endif | ||
608 | ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; | ||
609 | ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); | ||
610 | } | ||
611 | else | ||
612 | { | ||
613 | /* dual stack */ | ||
614 | resi = 2; | ||
615 | if (NULL != unixpath) | ||
616 | resi++; | ||
617 | saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); | ||
618 | saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); | ||
619 | i = 0; | ||
620 | if (NULL != unixpath) | ||
621 | { | ||
622 | add_unixpath (saddrs, saddrlens, unixpath, abstract); | ||
623 | i++; | ||
624 | } | ||
625 | saddrlens[i] = sizeof (struct sockaddr_in6); | ||
626 | saddrs[i] = GNUNET_malloc (saddrlens[i]); | ||
627 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
628 | ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0]; | ||
629 | #endif | ||
630 | ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6; | ||
631 | ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); | ||
632 | i++; | ||
633 | saddrlens[i] = sizeof (struct sockaddr_in); | ||
634 | saddrs[i] = GNUNET_malloc (saddrlens[i]); | ||
635 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
636 | ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1]; | ||
637 | #endif | ||
638 | ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; | ||
639 | ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); | ||
640 | } | ||
641 | } | ||
642 | GNUNET_free_non_null (unixpath); | ||
643 | *addrs = saddrs; | ||
644 | *addr_lens = saddrlens; | ||
645 | return resi; | ||
646 | } | ||
647 | |||
648 | |||
649 | /** | ||
263 | * Signal our client that we will start or stop the | 650 | * Signal our client that we will start or stop the |
264 | * service. | 651 | * service. |
265 | * | 652 | * |
@@ -1619,10 +2006,10 @@ setup_service (void *cls, | |||
1619 | "AUTOSTART")) | 2006 | "AUTOSTART")) |
1620 | return; | 2007 | return; |
1621 | } | 2008 | } |
1622 | if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, | 2009 | if (0 >= (ret = get_server_addresses (section, |
1623 | cfg, | 2010 | cfg, |
1624 | &addrs, | 2011 | &addrs, |
1625 | &addr_lens))) | 2012 | &addr_lens))) |
1626 | return; | 2013 | return; |
1627 | /* this will free (or capture) addrs[i] */ | 2014 | /* this will free (or capture) addrs[i] */ |
1628 | for (i = 0; i < ret; i++) | 2015 | for (i = 0; i < ret; i++) |