aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2019-02-09 23:29:47 +0100
committerChristian Grothoff <christian@grothoff.org>2019-02-09 23:30:00 +0100
commitd974bae720beaea9223a8729f4e6c88c326c522b (patch)
treed8f9a5dedb46fcd6be7731914d86159fb564c2f8 /src/util
parent4f9169370608dd9c0c125455b651082f5acc7ea6 (diff)
downloadgnunet-d974bae720beaea9223a8729f4e6c88c326c522b.tar.gz
gnunet-d974bae720beaea9223a8729f4e6c88c326c522b.zip
untested fix for #5511
Diffstat (limited to 'src/util')
-rw-r--r--src/util/network.c3
-rw-r--r--src/util/service.c1078
2 files changed, 585 insertions, 496 deletions
diff --git a/src/util/network.c b/src/util/network.c
index 3e39374e8..a100be375 100644
--- a/src/util/network.c
+++ b/src/util/network.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
@@ -463,7 +463,6 @@ GNUNET_NETWORK_socket_accept (const struct GNUNET_NETWORK_Handle *desc,
463 (NULL != address) ? address->sa_family : desc->af, 463 (NULL != address) ? address->sa_family : desc->af,
464 SOCK_STREAM)) 464 SOCK_STREAM))
465 { 465 {
466
467 return NULL; 466 return NULL;
468 } 467 }
469 return ret; 468 return ret;
diff --git a/src/util/service.c b/src/util/service.c
index b481a4786..ee140f88e 100644
--- a/src/util/service.c
+++ b/src/util/service.c
@@ -79,6 +79,38 @@ struct ServiceListenContext
79 79
80 80
81/** 81/**
82 * Reasons why we might be suspended.
83 */
84enum SuspendReason
85{
86 /**
87 * We are running normally.
88 */
89 SUSPEND_STATE_NONE = 0,
90
91 /**
92 * Application requested it.
93 */
94 SUSPEND_STATE_APP = 1,
95
96 /**
97 * OS ran out of file descriptors.
98 */
99 SUSPEND_STATE_EMFILE = 2,
100
101 /**
102 * Both reasons, APP and EMFILE apply.
103 */
104 SUSPEND_STATE_APP_AND_EMFILE = 3,
105
106 /**
107 * Suspension because service was permanently shutdown.
108 */
109 SUSPEND_STATE_SHUTDOWN = 4
110};
111
112
113/**
82 * Handle to a service. 114 * Handle to a service.
83 */ 115 */
84struct GNUNET_SERVICE_Handle 116struct GNUNET_SERVICE_Handle
@@ -188,6 +220,11 @@ struct GNUNET_SERVICE_Handle
188 int got_shutdown; 220 int got_shutdown;
189 221
190 /** 222 /**
223 * Are we suspended, and if so, why?
224 */
225 enum SuspendReason suspend_state;
226
227 /**
191 * Our options. 228 * Our options.
192 */ 229 */
193 enum GNUNET_SERVICE_Options options; 230 enum GNUNET_SERVICE_Options options;
@@ -329,9 +366,9 @@ struct GNUNET_SERVICE_Client
329static int 366static int
330have_non_monitor_clients (struct GNUNET_SERVICE_Handle *sh) 367have_non_monitor_clients (struct GNUNET_SERVICE_Handle *sh)
331{ 368{
332 struct GNUNET_SERVICE_Client *client; 369 for (struct GNUNET_SERVICE_Client *client = sh->clients_head;
333 370 NULL != client;
334 for (client = sh->clients_head;NULL != client; client = client->next) 371 client = client->next)
335 { 372 {
336 if (client->is_monitor) 373 if (client->is_monitor)
337 continue; 374 continue;
@@ -375,6 +412,526 @@ service_shutdown (void *cls)
375 412
376 413
377/** 414/**
415 * Check if the given IP address is in the list of IP addresses.
416 *
417 * @param list a list of networks
418 * @param add the IP to check (in network byte order)
419 * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is
420 */
421static int
422check_ipv4_listed (const struct GNUNET_STRINGS_IPv4NetworkPolicy *list,
423 const struct in_addr *add)
424{
425 unsigned int i;
426
427 if (NULL == list)
428 return GNUNET_NO;
429 i = 0;
430 while ( (0 != list[i].network.s_addr) ||
431 (0 != list[i].netmask.s_addr) )
432 {
433 if ((add->s_addr & list[i].netmask.s_addr) ==
434 (list[i].network.s_addr & list[i].netmask.s_addr))
435 return GNUNET_YES;
436 i++;
437 }
438 return GNUNET_NO;
439}
440
441
442/**
443 * Check if the given IP address is in the list of IP addresses.
444 *
445 * @param list a list of networks
446 * @param ip the IP to check (in network byte order)
447 * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is
448 */
449static int
450check_ipv6_listed (const struct GNUNET_STRINGS_IPv6NetworkPolicy *list,
451 const struct in6_addr *ip)
452{
453 unsigned int i;
454 unsigned int j;
455 struct in6_addr zero;
456
457 if (NULL == list)
458 return GNUNET_NO;
459 memset (&zero,
460 0,
461 sizeof (struct in6_addr));
462 i = 0;
463NEXT:
464 while (0 != memcmp (&zero,
465 &list[i].network,
466 sizeof (struct in6_addr)))
467 {
468 for (j = 0; j < sizeof (struct in6_addr) / sizeof (int); j++)
469 if (((((int *) ip)[j] & ((int *) &list[i].netmask)[j])) !=
470 (((int *) &list[i].network)[j] & ((int *) &list[i].netmask)[j]))
471 {
472 i++;
473 goto NEXT;
474 }
475 return GNUNET_YES;
476 }
477 return GNUNET_NO;
478}
479
480
481/**
482 * Suspend accepting connections from the listen socket temporarily.
483 * Resume activity using #do_resume.
484 *
485 * @param sh service to stop accepting connections.
486 * @param sr reason for suspending accepting connections
487 */
488static void
489do_suspend (struct GNUNET_SERVICE_Handle *sh,
490 enum SuspendReason sr)
491{
492 struct ServiceListenContext *slc;
493
494 GNUNET_assert (0 == (sh->suspend_state & sr));
495 sh->suspend_state |= sr;
496 for (slc = sh->slc_head; NULL != slc; slc = slc->next)
497 {
498 if (NULL != slc->listen_task)
499 {
500 GNUNET_SCHEDULER_cancel (slc->listen_task);
501 slc->listen_task = NULL;
502 }
503 }
504}
505
506
507/**
508 * Task run when we are ready to transmit data to the
509 * client.
510 *
511 * @param cls the `struct GNUNET_SERVICE_Client *` to send to
512 */
513static void
514do_send (void *cls)
515{
516 struct GNUNET_SERVICE_Client *client = cls;
517 ssize_t ret;
518 size_t left;
519 const char *buf;
520
521 LOG (GNUNET_ERROR_TYPE_DEBUG,
522 "service: sending message with type %u",
523 ntohs(client->msg->type));
524
525
526 client->send_task = NULL;
527 buf = (const char *) client->msg;
528 left = ntohs (client->msg->size) - client->msg_pos;
529 ret = GNUNET_NETWORK_socket_send (client->sock,
530 &buf[client->msg_pos],
531 left);
532 GNUNET_assert (ret <= (ssize_t) left);
533 if (0 == ret)
534 {
535 LOG (GNUNET_ERROR_TYPE_DEBUG,
536 "no data send");
537 GNUNET_MQ_inject_error (client->mq,
538 GNUNET_MQ_ERROR_WRITE);
539 return;
540 }
541 if (-1 == ret)
542 {
543 if ( (EAGAIN == errno) ||
544 (EINTR == errno) )
545 {
546 /* ignore */
547 ret = 0;
548 }
549 else
550 {
551 if (EPIPE != errno)
552 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
553 "send");
554 LOG (GNUNET_ERROR_TYPE_DEBUG,
555 "socket send returned with error code %i",
556 errno);
557 GNUNET_MQ_inject_error (client->mq,
558 GNUNET_MQ_ERROR_WRITE);
559 return;
560 }
561 }
562 if (0 == client->msg_pos)
563 {
564 GNUNET_MQ_impl_send_in_flight (client->mq);
565 }
566 client->msg_pos += ret;
567 if (left > (size_t) ret)
568 {
569 GNUNET_assert (NULL == client->drop_task);
570 client->send_task
571 = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
572 client->sock,
573 &do_send,
574 client);
575 return;
576 }
577 GNUNET_MQ_impl_send_continue (client->mq);
578}
579
580
581/**
582 * Signature of functions implementing the sending functionality of a
583 * message queue.
584 *
585 * @param mq the message queue
586 * @param msg the message to send
587 * @param impl_state our `struct GNUNET_SERVICE_Client *`
588 */
589static void
590service_mq_send (struct GNUNET_MQ_Handle *mq,
591 const struct GNUNET_MessageHeader *msg,
592 void *impl_state)
593{
594 struct GNUNET_SERVICE_Client *client = impl_state;
595
596 (void) mq;
597 if (NULL != client->drop_task)
598 return; /* we're going down right now, do not try to send */
599 GNUNET_assert (NULL == client->send_task);
600 LOG (GNUNET_ERROR_TYPE_DEBUG,
601 "Sending message of type %u and size %u to client\n",
602 ntohs (msg->type),
603 ntohs (msg->size));
604 client->msg = msg;
605 client->msg_pos = 0;
606 client->send_task
607 = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
608 client->sock,
609 &do_send,
610 client);
611}
612
613
614/**
615 * Implementation function that cancels the currently sent message.
616 *
617 * @param mq message queue
618 * @param impl_state state specific to the implementation
619 */
620static void
621service_mq_cancel (struct GNUNET_MQ_Handle *mq,
622 void *impl_state)
623{
624 struct GNUNET_SERVICE_Client *client = impl_state;
625
626 (void) mq;
627 GNUNET_assert (0 == client->msg_pos);
628 client->msg = NULL;
629 GNUNET_SCHEDULER_cancel (client->send_task);
630 client->send_task = NULL;
631}
632
633
634/**
635 * Generic error handler, called with the appropriate
636 * error code and the same closure specified at the creation of
637 * the message queue.
638 * Not every message queue implementation supports an error handler.
639 *
640 * @param cls closure with our `struct GNUNET_SERVICE_Client`
641 * @param error error code
642 */
643static void
644service_mq_error_handler (void *cls,
645 enum GNUNET_MQ_Error error)
646{
647 struct GNUNET_SERVICE_Client *client = cls;
648 struct GNUNET_SERVICE_Handle *sh = client->sh;
649
650 if ( (GNUNET_MQ_ERROR_NO_MATCH == error) &&
651 (GNUNET_NO == sh->require_found) )
652 {
653 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
654 "No handler for message of type %u found\n",
655 (unsigned int) client->warn_type);
656 GNUNET_SERVICE_client_continue (client);
657 return; /* ignore error */
658 }
659 GNUNET_SERVICE_client_drop (client);
660}
661
662
663/**
664 * Task run to warn about missing calls to #GNUNET_SERVICE_client_continue().
665 *
666 * @param cls our `struct GNUNET_SERVICE_Client *` to process more requests from
667 */
668static void
669warn_no_client_continue (void *cls)
670{
671 struct GNUNET_SERVICE_Client *client = cls;
672
673 GNUNET_break (0 != client->warn_type); /* type should never be 0 here, as we don't use 0 */
674 client->warn_task
675 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
676 &warn_no_client_continue,
677 client);
678 LOG (GNUNET_ERROR_TYPE_WARNING,
679 _("Processing code for message of type %u did not call `GNUNET_SERVICE_client_continue' after %s\n"),
680 (unsigned int) client->warn_type,
681 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (client->warn_start),
682 GNUNET_YES));
683}
684
685
686/**
687 * Functions with this signature are called whenever a
688 * complete message is received by the tokenizer for a client.
689 *
690 * Do not call #GNUNET_MST_destroy() from within
691 * the scope of this callback.
692 *
693 * @param cls closure with the `struct GNUNET_SERVICE_Client *`
694 * @param message the actual message
695 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the client was dropped
696 */
697static int
698service_client_mst_cb (void *cls,
699 const struct GNUNET_MessageHeader *message)
700{
701 struct GNUNET_SERVICE_Client *client = cls;
702
703 LOG (GNUNET_ERROR_TYPE_DEBUG,
704 "Received message of type %u and size %u from client\n",
705 ntohs (message->type),
706 ntohs (message->size));
707 GNUNET_assert (GNUNET_NO == client->needs_continue);
708 client->needs_continue = GNUNET_YES;
709 client->warn_type = ntohs (message->type);
710 client->warn_start = GNUNET_TIME_absolute_get ();
711 GNUNET_assert (NULL == client->warn_task);
712 client->warn_task
713 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
714 &warn_no_client_continue,
715 client);
716 GNUNET_MQ_inject_message (client->mq,
717 message);
718 if (NULL != client->drop_task)
719 return GNUNET_SYSERR;
720 return GNUNET_OK;
721}
722
723
724/**
725 * A client sent us data. Receive and process it. If we are done,
726 * reschedule this task.
727 *
728 * @param cls the `struct GNUNET_SERVICE_Client` that sent us data.
729 */
730static void
731service_client_recv (void *cls)
732{
733 struct GNUNET_SERVICE_Client *client = cls;
734 int ret;
735
736 client->recv_task = NULL;
737 ret = GNUNET_MST_read (client->mst,
738 client->sock,
739 GNUNET_NO,
740 GNUNET_YES);
741 if (GNUNET_SYSERR == ret)
742 {
743 /* client closed connection (or IO error) */
744 if (NULL == client->drop_task)
745 {
746 GNUNET_assert (GNUNET_NO == client->needs_continue);
747 GNUNET_SERVICE_client_drop (client);
748 }
749 return;
750 }
751 if (GNUNET_NO == ret)
752 return; /* more messages in buffer, wait for application
753 to be done processing */
754 GNUNET_assert (GNUNET_OK == ret);
755 if (GNUNET_YES == client->needs_continue)
756 return;
757 if (NULL != client->recv_task)
758 return;
759 /* MST needs more data, re-schedule read job */
760 client->recv_task
761 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
762 client->sock,
763 &service_client_recv,
764 client);
765}
766
767
768/**
769 * We have successfully accepted a connection from a client. Now
770 * setup the client (with the scheduler) and tell the application.
771 *
772 * @param sh service that accepted the client
773 * @param sock socket associated with the client
774 */
775static void
776start_client (struct GNUNET_SERVICE_Handle *sh,
777 struct GNUNET_NETWORK_Handle *csock)
778{
779 struct GNUNET_SERVICE_Client *client;
780
781 client = GNUNET_new (struct GNUNET_SERVICE_Client);
782 GNUNET_CONTAINER_DLL_insert (sh->clients_head,
783 sh->clients_tail,
784 client);
785 client->sh = sh;
786 client->sock = csock;
787 client->mq = GNUNET_MQ_queue_for_callbacks (&service_mq_send,
788 NULL,
789 &service_mq_cancel,
790 client,
791 sh->handlers,
792 &service_mq_error_handler,
793 client);
794 client->mst = GNUNET_MST_create (&service_client_mst_cb,
795 client);
796 if (NULL != sh->connect_cb)
797 client->user_context = sh->connect_cb (sh->cb_cls,
798 client,
799 client->mq);
800 GNUNET_MQ_set_handlers_closure (client->mq,
801 client->user_context);
802 client->recv_task
803 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
804 client->sock,
805 &service_client_recv,
806 client);
807}
808
809
810/**
811 * We have a client. Accept the incoming socket(s) (and reschedule
812 * the listen task).
813 *
814 * @param cls the `struct ServiceListenContext` of the ready listen socket
815 */
816static void
817accept_client (void *cls)
818{
819 struct ServiceListenContext *slc = cls;
820 struct GNUNET_SERVICE_Handle *sh = slc->sh;
821
822 slc->listen_task = NULL;
823 while (1)
824 {
825 struct GNUNET_NETWORK_Handle *sock;
826 const struct sockaddr_in *v4;
827 const struct sockaddr_in6 *v6;
828 struct sockaddr_storage sa;
829 socklen_t addrlen;
830 int ok;
831
832 addrlen = sizeof (sa);
833 sock = GNUNET_NETWORK_socket_accept (slc->listen_socket,
834 (struct sockaddr *) &sa,
835 &addrlen);
836 if (NULL == sock)
837 {
838 if (EMFILE == errno)
839 do_suspend (sh,
840 SUSPEND_STATE_EMFILE);
841 else
842 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
843 "accept");
844 break;
845 }
846 switch (sa.ss_family)
847 {
848 case AF_INET:
849 GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
850 v4 = (const struct sockaddr_in *) &sa;
851 ok = ( ( (NULL == sh->v4_allowed) ||
852 (check_ipv4_listed (sh->v4_allowed,
853 &v4->sin_addr))) &&
854 ( (NULL == sh->v4_denied) ||
855 (! check_ipv4_listed (sh->v4_denied,
856 &v4->sin_addr)) ) );
857 break;
858 case AF_INET6:
859 GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
860 v6 = (const struct sockaddr_in6 *) &sa;
861 ok = ( ( (NULL == sh->v6_allowed) ||
862 (check_ipv6_listed (sh->v6_allowed,
863 &v6->sin6_addr))) &&
864 ( (NULL == sh->v6_denied) ||
865 (! check_ipv6_listed (sh->v6_denied,
866 &v6->sin6_addr)) ) );
867 break;
868#ifndef WINDOWS
869 case AF_UNIX:
870 ok = GNUNET_OK; /* controlled using file-system ACL now */
871 break;
872#endif
873 default:
874 LOG (GNUNET_ERROR_TYPE_WARNING,
875 _("Unknown address family %d\n"),
876 sa.ss_family);
877 return;
878 }
879 if (! ok)
880 {
881 LOG (GNUNET_ERROR_TYPE_DEBUG,
882 "Service rejected incoming connection from %s due to policy.\n",
883 GNUNET_a2s ((const struct sockaddr *) &sa,
884 addrlen));
885 GNUNET_break (GNUNET_OK ==
886 GNUNET_NETWORK_socket_close (sock));
887 continue;
888 }
889 LOG (GNUNET_ERROR_TYPE_DEBUG,
890 "Service accepted incoming connection from %s.\n",
891 GNUNET_a2s ((const struct sockaddr *) &sa,
892 addrlen));
893 start_client (slc->sh,
894 sock);
895 }
896 slc->listen_task
897 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
898 slc->listen_socket,
899 &accept_client,
900 slc);
901}
902
903
904/**
905 * Resume accepting connections from the listen socket.
906 *
907 * @param sh service to resume accepting connections.
908 * @param sr reason that is no longer causing the suspension,
909 * or #SUSPEND_STATE_NONE on first startup
910 */
911static void
912do_resume (struct GNUNET_SERVICE_Handle *sh,
913 enum SuspendReason sr)
914{
915 struct ServiceListenContext *slc;
916
917 GNUNET_assert ( (SUSPEND_STATE_NONE == sr) ||
918 (0 != (sh->suspend_state & sr)) );
919 sh->suspend_state -= sr;
920 if (SUSPEND_STATE_NONE != sh->suspend_state)
921 return;
922 for (slc = sh->slc_head; NULL != slc; slc = slc->next)
923 {
924 GNUNET_assert (NULL == slc->listen_task);
925 slc->listen_task
926 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
927 slc->listen_socket,
928 &accept_client,
929 slc);
930 }
931}
932
933
934/**
378 * First task run by any service. Initializes our shutdown task, 935 * First task run by any service. Initializes our shutdown task,
379 * starts the listening operation on our listen sockets and launches 936 * starts the listening operation on our listen sockets and launches
380 * the custom logic of the application service. 937 * the custom logic of the application service.
@@ -389,7 +946,8 @@ service_main (void *cls)
389 if (GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN != sh->options) 946 if (GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN != sh->options)
390 GNUNET_SCHEDULER_add_shutdown (&service_shutdown, 947 GNUNET_SCHEDULER_add_shutdown (&service_shutdown,
391 sh); 948 sh);
392 GNUNET_SERVICE_resume (sh); 949 do_resume (sh,
950 SUSPEND_STATE_NONE);
393 951
394 if (-1 != sh->ready_confirm_fd) 952 if (-1 != sh->ready_confirm_fd)
395 { 953 {
@@ -1665,7 +2223,8 @@ GNUNET_SERVICE_start (const char *service_name,
1665 GNUNET_free (sh); 2223 GNUNET_free (sh);
1666 return NULL; 2224 return NULL;
1667 } 2225 }
1668 GNUNET_SERVICE_resume (sh); 2226 do_resume (sh,
2227 SUSPEND_STATE_NONE);
1669 return sh; 2228 return sh;
1670} 2229}
1671 2230
@@ -1820,7 +2379,7 @@ GNUNET_SERVICE_run_ (int argc,
1820 GNUNET_DISK_file_test (opt_cfg_filename)) || 2379 GNUNET_DISK_file_test (opt_cfg_filename)) ||
1821 (GNUNET_SYSERR == 2380 (GNUNET_SYSERR ==
1822 GNUNET_CONFIGURATION_load (cfg, 2381 GNUNET_CONFIGURATION_load (cfg,
1823 opt_cfg_filename)) ) 2382 opt_cfg_filename)) )
1824 { 2383 {
1825 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2384 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1826 _("Malformed configuration file `%s', exit ...\n"), 2385 _("Malformed configuration file `%s', exit ...\n"),
@@ -1836,7 +2395,7 @@ GNUNET_SERVICE_run_ (int argc,
1836 if (GNUNET_SYSERR == 2395 if (GNUNET_SYSERR ==
1837 GNUNET_CONFIGURATION_load (cfg, 2396 GNUNET_CONFIGURATION_load (cfg,
1838 cfg_filename)) 2397 cfg_filename))
1839 { 2398 {
1840 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2399 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1841 _("Malformed configuration file `%s', exit ...\n"), 2400 _("Malformed configuration file `%s', exit ...\n"),
1842 cfg_filename); 2401 cfg_filename);
@@ -1953,472 +2512,8 @@ shutdown:
1953void 2512void
1954GNUNET_SERVICE_suspend (struct GNUNET_SERVICE_Handle *sh) 2513GNUNET_SERVICE_suspend (struct GNUNET_SERVICE_Handle *sh)
1955{ 2514{
1956 struct ServiceListenContext *slc; 2515 do_suspend (sh,
1957 2516 SUSPEND_STATE_APP);
1958 for (slc = sh->slc_head; NULL != slc; slc = slc->next)
1959 {
1960 if (NULL != slc->listen_task)
1961 {
1962 GNUNET_SCHEDULER_cancel (slc->listen_task);
1963 slc->listen_task = NULL;
1964 }
1965 }
1966}
1967
1968
1969/**
1970 * Task run when we are ready to transmit data to the
1971 * client.
1972 *
1973 * @param cls the `struct GNUNET_SERVICE_Client *` to send to
1974 */
1975static void
1976do_send (void *cls)
1977{
1978 struct GNUNET_SERVICE_Client *client = cls;
1979 ssize_t ret;
1980 size_t left;
1981 const char *buf;
1982
1983 LOG (GNUNET_ERROR_TYPE_DEBUG,
1984 "service: sending message with type %u",
1985 ntohs(client->msg->type));
1986
1987
1988 client->send_task = NULL;
1989 buf = (const char *) client->msg;
1990 left = ntohs (client->msg->size) - client->msg_pos;
1991 ret = GNUNET_NETWORK_socket_send (client->sock,
1992 &buf[client->msg_pos],
1993 left);
1994 GNUNET_assert (ret <= (ssize_t) left);
1995 if (0 == ret)
1996 {
1997 LOG (GNUNET_ERROR_TYPE_DEBUG,
1998 "no data send");
1999 GNUNET_MQ_inject_error (client->mq,
2000 GNUNET_MQ_ERROR_WRITE);
2001 return;
2002 }
2003 if (-1 == ret)
2004 {
2005 if ( (EAGAIN == errno) ||
2006 (EINTR == errno) )
2007 {
2008 /* ignore */
2009 ret = 0;
2010 }
2011 else
2012 {
2013 if (EPIPE != errno)
2014 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
2015 "send");
2016 LOG (GNUNET_ERROR_TYPE_DEBUG,
2017 "socket send returned with error code %i",
2018 errno);
2019 GNUNET_MQ_inject_error (client->mq,
2020 GNUNET_MQ_ERROR_WRITE);
2021 return;
2022 }
2023 }
2024 if (0 == client->msg_pos)
2025 {
2026 GNUNET_MQ_impl_send_in_flight (client->mq);
2027 }
2028 client->msg_pos += ret;
2029 if (left > (size_t) ret)
2030 {
2031 GNUNET_assert (NULL == client->drop_task);
2032 client->send_task
2033 = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2034 client->sock,
2035 &do_send,
2036 client);
2037 return;
2038 }
2039 GNUNET_MQ_impl_send_continue (client->mq);
2040}
2041
2042
2043/**
2044 * Signature of functions implementing the sending functionality of a
2045 * message queue.
2046 *
2047 * @param mq the message queue
2048 * @param msg the message to send
2049 * @param impl_state our `struct GNUNET_SERVICE_Client *`
2050 */
2051static void
2052service_mq_send (struct GNUNET_MQ_Handle *mq,
2053 const struct GNUNET_MessageHeader *msg,
2054 void *impl_state)
2055{
2056 struct GNUNET_SERVICE_Client *client = impl_state;
2057
2058 (void) mq;
2059 if (NULL != client->drop_task)
2060 return; /* we're going down right now, do not try to send */
2061 GNUNET_assert (NULL == client->send_task);
2062 LOG (GNUNET_ERROR_TYPE_DEBUG,
2063 "Sending message of type %u and size %u to client\n",
2064 ntohs (msg->type),
2065 ntohs (msg->size));
2066 client->msg = msg;
2067 client->msg_pos = 0;
2068 client->send_task
2069 = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2070 client->sock,
2071 &do_send,
2072 client);
2073}
2074
2075
2076/**
2077 * Implementation function that cancels the currently sent message.
2078 *
2079 * @param mq message queue
2080 * @param impl_state state specific to the implementation
2081 */
2082static void
2083service_mq_cancel (struct GNUNET_MQ_Handle *mq,
2084 void *impl_state)
2085{
2086 struct GNUNET_SERVICE_Client *client = impl_state;
2087
2088 (void) mq;
2089 GNUNET_assert (0 == client->msg_pos);
2090 client->msg = NULL;
2091 GNUNET_SCHEDULER_cancel (client->send_task);
2092 client->send_task = NULL;
2093}
2094
2095
2096/**
2097 * Generic error handler, called with the appropriate
2098 * error code and the same closure specified at the creation of
2099 * the message queue.
2100 * Not every message queue implementation supports an error handler.
2101 *
2102 * @param cls closure with our `struct GNUNET_SERVICE_Client`
2103 * @param error error code
2104 */
2105static void
2106service_mq_error_handler (void *cls,
2107 enum GNUNET_MQ_Error error)
2108{
2109 struct GNUNET_SERVICE_Client *client = cls;
2110 struct GNUNET_SERVICE_Handle *sh = client->sh;
2111
2112 if ( (GNUNET_MQ_ERROR_NO_MATCH == error) &&
2113 (GNUNET_NO == sh->require_found) )
2114 {
2115 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2116 "No handler for message of type %u found\n",
2117 (unsigned int) client->warn_type);
2118 GNUNET_SERVICE_client_continue (client);
2119 return; /* ignore error */
2120 }
2121 GNUNET_SERVICE_client_drop (client);
2122}
2123
2124
2125/**
2126 * Task run to warn about missing calls to #GNUNET_SERVICE_client_continue().
2127 *
2128 * @param cls our `struct GNUNET_SERVICE_Client *` to process more requests from
2129 */
2130static void
2131warn_no_client_continue (void *cls)
2132{
2133 struct GNUNET_SERVICE_Client *client = cls;
2134
2135 GNUNET_break (0 != client->warn_type); /* type should never be 0 here, as we don't use 0 */
2136 client->warn_task
2137 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
2138 &warn_no_client_continue,
2139 client);
2140 LOG (GNUNET_ERROR_TYPE_WARNING,
2141 _("Processing code for message of type %u did not call `GNUNET_SERVICE_client_continue' after %s\n"),
2142 (unsigned int) client->warn_type,
2143 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (client->warn_start),
2144 GNUNET_YES));
2145}
2146
2147
2148/**
2149 * Functions with this signature are called whenever a
2150 * complete message is received by the tokenizer for a client.
2151 *
2152 * Do not call #GNUNET_MST_destroy() from within
2153 * the scope of this callback.
2154 *
2155 * @param cls closure with the `struct GNUNET_SERVICE_Client *`
2156 * @param message the actual message
2157 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the client was dropped
2158 */
2159static int
2160service_client_mst_cb (void *cls,
2161 const struct GNUNET_MessageHeader *message)
2162{
2163 struct GNUNET_SERVICE_Client *client = cls;
2164
2165 LOG (GNUNET_ERROR_TYPE_DEBUG,
2166 "Received message of type %u and size %u from client\n",
2167 ntohs (message->type),
2168 ntohs (message->size));
2169 GNUNET_assert (GNUNET_NO == client->needs_continue);
2170 client->needs_continue = GNUNET_YES;
2171 client->warn_type = ntohs (message->type);
2172 client->warn_start = GNUNET_TIME_absolute_get ();
2173 GNUNET_assert (NULL == client->warn_task);
2174 client->warn_task
2175 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
2176 &warn_no_client_continue,
2177 client);
2178 GNUNET_MQ_inject_message (client->mq,
2179 message);
2180 if (NULL != client->drop_task)
2181 return GNUNET_SYSERR;
2182 return GNUNET_OK;
2183}
2184
2185
2186/**
2187 * A client sent us data. Receive and process it. If we are done,
2188 * reschedule this task.
2189 *
2190 * @param cls the `struct GNUNET_SERVICE_Client` that sent us data.
2191 */
2192static void
2193service_client_recv (void *cls)
2194{
2195 struct GNUNET_SERVICE_Client *client = cls;
2196 int ret;
2197
2198 client->recv_task = NULL;
2199 ret = GNUNET_MST_read (client->mst,
2200 client->sock,
2201 GNUNET_NO,
2202 GNUNET_YES);
2203 if (GNUNET_SYSERR == ret)
2204 {
2205 /* client closed connection (or IO error) */
2206 if (NULL == client->drop_task)
2207 {
2208 GNUNET_assert (GNUNET_NO == client->needs_continue);
2209 GNUNET_SERVICE_client_drop (client);
2210 }
2211 return;
2212 }
2213 if (GNUNET_NO == ret)
2214 return; /* more messages in buffer, wait for application
2215 to be done processing */
2216 GNUNET_assert (GNUNET_OK == ret);
2217 if (GNUNET_YES == client->needs_continue)
2218 return;
2219 if (NULL != client->recv_task)
2220 return;
2221 /* MST needs more data, re-schedule read job */
2222 client->recv_task
2223 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2224 client->sock,
2225 &service_client_recv,
2226 client);
2227}
2228
2229
2230/**
2231 * We have successfully accepted a connection from a client. Now
2232 * setup the client (with the scheduler) and tell the application.
2233 *
2234 * @param sh service that accepted the client
2235 * @param sock socket associated with the client
2236 */
2237static void
2238start_client (struct GNUNET_SERVICE_Handle *sh,
2239 struct GNUNET_NETWORK_Handle *csock)
2240{
2241 struct GNUNET_SERVICE_Client *client;
2242
2243 client = GNUNET_new (struct GNUNET_SERVICE_Client);
2244 GNUNET_CONTAINER_DLL_insert (sh->clients_head,
2245 sh->clients_tail,
2246 client);
2247 client->sh = sh;
2248 client->sock = csock;
2249 client->mq = GNUNET_MQ_queue_for_callbacks (&service_mq_send,
2250 NULL,
2251 &service_mq_cancel,
2252 client,
2253 sh->handlers,
2254 &service_mq_error_handler,
2255 client);
2256 client->mst = GNUNET_MST_create (&service_client_mst_cb,
2257 client);
2258 if (NULL != sh->connect_cb)
2259 client->user_context = sh->connect_cb (sh->cb_cls,
2260 client,
2261 client->mq);
2262 GNUNET_MQ_set_handlers_closure (client->mq,
2263 client->user_context);
2264 client->recv_task
2265 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2266 client->sock,
2267 &service_client_recv,
2268 client);
2269}
2270
2271
2272/**
2273 * Check if the given IP address is in the list of IP addresses.
2274 *
2275 * @param list a list of networks
2276 * @param add the IP to check (in network byte order)
2277 * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is
2278 */
2279static int
2280check_ipv4_listed (const struct GNUNET_STRINGS_IPv4NetworkPolicy *list,
2281 const struct in_addr *add)
2282{
2283 unsigned int i;
2284
2285 if (NULL == list)
2286 return GNUNET_NO;
2287 i = 0;
2288 while ( (0 != list[i].network.s_addr) ||
2289 (0 != list[i].netmask.s_addr) )
2290 {
2291 if ((add->s_addr & list[i].netmask.s_addr) ==
2292 (list[i].network.s_addr & list[i].netmask.s_addr))
2293 return GNUNET_YES;
2294 i++;
2295 }
2296 return GNUNET_NO;
2297}
2298
2299
2300/**
2301 * Check if the given IP address is in the list of IP addresses.
2302 *
2303 * @param list a list of networks
2304 * @param ip the IP to check (in network byte order)
2305 * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is
2306 */
2307static int
2308check_ipv6_listed (const struct GNUNET_STRINGS_IPv6NetworkPolicy *list,
2309 const struct in6_addr *ip)
2310{
2311 unsigned int i;
2312 unsigned int j;
2313 struct in6_addr zero;
2314
2315 if (NULL == list)
2316 return GNUNET_NO;
2317 memset (&zero,
2318 0,
2319 sizeof (struct in6_addr));
2320 i = 0;
2321NEXT:
2322 while (0 != memcmp (&zero,
2323 &list[i].network,
2324 sizeof (struct in6_addr)))
2325 {
2326 for (j = 0; j < sizeof (struct in6_addr) / sizeof (int); j++)
2327 if (((((int *) ip)[j] & ((int *) &list[i].netmask)[j])) !=
2328 (((int *) &list[i].network)[j] & ((int *) &list[i].netmask)[j]))
2329 {
2330 i++;
2331 goto NEXT;
2332 }
2333 return GNUNET_YES;
2334 }
2335 return GNUNET_NO;
2336}
2337
2338
2339/**
2340 * We have a client. Accept the incoming socket(s) (and reschedule
2341 * the listen task).
2342 *
2343 * @param cls the `struct ServiceListenContext` of the ready listen socket
2344 */
2345static void
2346accept_client (void *cls)
2347{
2348 struct ServiceListenContext *slc = cls;
2349 struct GNUNET_SERVICE_Handle *sh = slc->sh;
2350
2351 slc->listen_task = NULL;
2352 while (1)
2353 {
2354 struct GNUNET_NETWORK_Handle *sock;
2355 const struct sockaddr_in *v4;
2356 const struct sockaddr_in6 *v6;
2357 struct sockaddr_storage sa;
2358 socklen_t addrlen;
2359 int ok;
2360
2361 addrlen = sizeof (sa);
2362 sock = GNUNET_NETWORK_socket_accept (slc->listen_socket,
2363 (struct sockaddr *) &sa,
2364 &addrlen);
2365 if (NULL == sock)
2366 break;
2367 switch (sa.ss_family)
2368 {
2369 case AF_INET:
2370 GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
2371 v4 = (const struct sockaddr_in *) &sa;
2372 ok = ( ( (NULL == sh->v4_allowed) ||
2373 (check_ipv4_listed (sh->v4_allowed,
2374 &v4->sin_addr))) &&
2375 ( (NULL == sh->v4_denied) ||
2376 (! check_ipv4_listed (sh->v4_denied,
2377 &v4->sin_addr)) ) );
2378 break;
2379 case AF_INET6:
2380 GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
2381 v6 = (const struct sockaddr_in6 *) &sa;
2382 ok = ( ( (NULL == sh->v6_allowed) ||
2383 (check_ipv6_listed (sh->v6_allowed,
2384 &v6->sin6_addr))) &&
2385 ( (NULL == sh->v6_denied) ||
2386 (! check_ipv6_listed (sh->v6_denied,
2387 &v6->sin6_addr)) ) );
2388 break;
2389#ifndef WINDOWS
2390 case AF_UNIX:
2391 ok = GNUNET_OK; /* controlled using file-system ACL now */
2392 break;
2393#endif
2394 default:
2395 LOG (GNUNET_ERROR_TYPE_WARNING,
2396 _("Unknown address family %d\n"),
2397 sa.ss_family);
2398 return;
2399 }
2400 if (! ok)
2401 {
2402 LOG (GNUNET_ERROR_TYPE_DEBUG,
2403 "Service rejected incoming connection from %s due to policy.\n",
2404 GNUNET_a2s ((const struct sockaddr *) &sa,
2405 addrlen));
2406 GNUNET_break (GNUNET_OK ==
2407 GNUNET_NETWORK_socket_close (sock));
2408 continue;
2409 }
2410 LOG (GNUNET_ERROR_TYPE_DEBUG,
2411 "Service accepted incoming connection from %s.\n",
2412 GNUNET_a2s ((const struct sockaddr *) &sa,
2413 addrlen));
2414 start_client (slc->sh,
2415 sock);
2416 }
2417 slc->listen_task
2418 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2419 slc->listen_socket,
2420 &accept_client,
2421 slc);
2422} 2517}
2423 2518
2424 2519
@@ -2430,17 +2525,8 @@ accept_client (void *cls)
2430void 2525void
2431GNUNET_SERVICE_resume (struct GNUNET_SERVICE_Handle *sh) 2526GNUNET_SERVICE_resume (struct GNUNET_SERVICE_Handle *sh)
2432{ 2527{
2433 struct ServiceListenContext *slc; 2528 do_resume (sh,
2434 2529 SUSPEND_STATE_APP);
2435 for (slc = sh->slc_head; NULL != slc; slc = slc->next)
2436 {
2437 GNUNET_assert (NULL == slc->listen_task);
2438 slc->listen_task
2439 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2440 slc->listen_socket,
2441 &accept_client,
2442 slc);
2443 }
2444} 2530}
2445 2531
2446 2532
@@ -2547,6 +2633,9 @@ finish_client_drop (void *cls)
2547 { 2633 {
2548 GNUNET_break (GNUNET_OK == 2634 GNUNET_break (GNUNET_OK ==
2549 GNUNET_NETWORK_socket_close (c->sock)); 2635 GNUNET_NETWORK_socket_close (c->sock));
2636 if (0 != (SUSPEND_STATE_EMFILE & sh->suspend_state))
2637 do_resume (sh,
2638 SUSPEND_STATE_EMFILE);
2550 } 2639 }
2551 else 2640 else
2552 { 2641 {
@@ -2578,20 +2667,20 @@ GNUNET_SERVICE_client_drop (struct GNUNET_SERVICE_Client *c)
2578 "Client dropped: %p (MQ: %p)\n", 2667 "Client dropped: %p (MQ: %p)\n",
2579 c, 2668 c,
2580 c->mq); 2669 c->mq);
2581
2582#if EXECINFO 2670#if EXECINFO
2583 void *backtrace_array[MAX_TRACE_DEPTH]; 2671 {
2584 int num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH); 2672 void *backtrace_array[MAX_TRACE_DEPTH];
2673 int num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
2585 char **backtrace_strings = 2674 char **backtrace_strings =
2586 backtrace_symbols (backtrace_array, 2675 backtrace_symbols (backtrace_array,
2587 t->num_backtrace_strings); 2676 t->num_backtrace_strings);
2588 for (unsigned int i = 0; i < num_backtrace_strings; i++) 2677 for (unsigned int i = 0; i < num_backtrace_strings; i++)
2589 LOG (GNUNET_ERROR_TYPE_DEBUG, 2678 LOG (GNUNET_ERROR_TYPE_DEBUG,
2590 "client drop trace %u: %s\n", 2679 "client drop trace %u: %s\n",
2591 i, 2680 i,
2592 backtrace_strings[i]); 2681 backtrace_strings[i]);
2682 }
2593#endif 2683#endif
2594
2595 if (NULL != c->drop_task) 2684 if (NULL != c->drop_task)
2596 { 2685 {
2597 /* asked to drop twice! */ 2686 /* asked to drop twice! */
@@ -2635,7 +2724,8 @@ GNUNET_SERVICE_shutdown (struct GNUNET_SERVICE_Handle *sh)
2635{ 2724{
2636 struct GNUNET_SERVICE_Client *client; 2725 struct GNUNET_SERVICE_Client *client;
2637 2726
2638 GNUNET_SERVICE_suspend (sh); 2727 do_suspend (sh,
2728 SUSPEND_STATE_SHUTDOWN);
2639 sh->got_shutdown = GNUNET_NO; 2729 sh->got_shutdown = GNUNET_NO;
2640 while (NULL != (client = sh->clients_head)) 2730 while (NULL != (client = sh->clients_head))
2641 GNUNET_SERVICE_client_drop (client); 2731 GNUNET_SERVICE_client_drop (client);