diff options
author | Christian Grothoff <christian@grothoff.org> | 2010-11-10 08:08:37 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2010-11-10 08:08:37 +0000 |
commit | c096eaed42b83fcd4afc87d3d24fe31975a4b62a (patch) | |
tree | 03d6f5ebdbb97180ceba093f63a17c8b776ec527 /src/arm | |
parent | d1d2efce4c68210f4cc6873e2c91b8ba21e876ad (diff) | |
download | gnunet-c096eaed42b83fcd4afc87d3d24fe31975a4b62a.tar.gz gnunet-c096eaed42b83fcd4afc87d3d24fe31975a4b62a.zip |
sockets patch from #1616
Diffstat (limited to 'src/arm')
-rw-r--r-- | src/arm/gnunet-service-arm_interceptor.c | 287 |
1 files changed, 223 insertions, 64 deletions
diff --git a/src/arm/gnunet-service-arm_interceptor.c b/src/arm/gnunet-service-arm_interceptor.c index 56268b2d0..2dd4f3703 100644 --- a/src/arm/gnunet-service-arm_interceptor.c +++ b/src/arm/gnunet-service-arm_interceptor.c | |||
@@ -187,9 +187,15 @@ struct ForwardedConnection | |||
187 | * Have we ever successfully written data to the service? | 187 | * Have we ever successfully written data to the service? |
188 | */ | 188 | */ |
189 | int first_write_done; | 189 | int first_write_done; |
190 | }; | ||
191 | 190 | ||
192 | 191 | ||
192 | /** | ||
193 | * Service connection attempts | ||
194 | */ | ||
195 | struct ServiceListeningInfo *service_connect_ipv4; | ||
196 | struct ServiceListeningInfo *service_connect_ipv6; | ||
197 | }; | ||
198 | |||
193 | /** | 199 | /** |
194 | * Array with the names of the services started by default. | 200 | * Array with the names of the services started by default. |
195 | */ | 201 | */ |
@@ -694,6 +700,10 @@ receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
694 | &forwardToService, fc); | 700 | &forwardToService, fc); |
695 | } | 701 | } |
696 | 702 | ||
703 | static void fc_acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc, int is_ipv4, int is_ipv6); | ||
704 | static void fc_acceptConnection_ipv4 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
705 | static void fc_acceptConnection_ipv6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
706 | static int service_try_to_connect (struct sockaddr *addr, socklen_t addrlen, struct ForwardedConnection *fc); | ||
697 | 707 | ||
698 | /** | 708 | /** |
699 | * | 709 | * |
@@ -704,99 +714,248 @@ start_forwarding (void *cls, | |||
704 | { | 714 | { |
705 | struct ForwardedConnection *fc = cls; | 715 | struct ForwardedConnection *fc = cls; |
706 | struct GNUNET_TIME_Relative rem; | 716 | struct GNUNET_TIME_Relative rem; |
717 | int fail = 0; | ||
718 | int failures = 0; | ||
719 | int is_zero = 0, is_ipv6 = 0, is_ipv4 = 0; | ||
720 | struct sockaddr_in *target_ipv4; | ||
721 | struct sockaddr_in6 *target_ipv6; | ||
722 | |||
723 | int free_ipv4 = 1, free_ipv6 = 1; | ||
724 | char listen_address[128]; | ||
725 | uint16_t listening_port; | ||
707 | 726 | ||
708 | fc->start_task = GNUNET_SCHEDULER_NO_TASK; | 727 | fc->start_task = GNUNET_SCHEDULER_NO_TASK; |
709 | if ( (NULL != tc) && | 728 | if ( (NULL != tc) && |
710 | (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) ) | 729 | (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) ) |
711 | { | 730 | { |
712 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 731 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, |
713 | _("Unable to forward to service `%s': shutdown\n"), | 732 | _("Unable to forward to service `%s': shutdown\n"), |
714 | fc->listen_info->serviceName); | 733 | fc->listen_info->serviceName); |
715 | closeClientAndServiceSockets (fc, REASON_ERROR); | 734 | closeClientAndServiceSockets (fc, REASON_ERROR); |
716 | return; | 735 | return; |
717 | } | 736 | } |
718 | rem = GNUNET_TIME_absolute_get_remaining (fc->timeout); | 737 | rem = GNUNET_TIME_absolute_get_remaining (fc->timeout); |
719 | if (rem.rel_value == 0) | 738 | if (rem.rel_value == 0) |
739 | { | ||
740 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
741 | _("Unable to forward to service `%s': timeout before connect\n"), | ||
742 | fc->listen_info->serviceName); | ||
743 | closeClientAndServiceSockets (fc, REASON_ERROR); | ||
744 | return; | ||
745 | } | ||
746 | target_ipv4 = GNUNET_malloc (sizeof (struct sockaddr_in)); | ||
747 | target_ipv6 = GNUNET_malloc (sizeof (struct sockaddr_in6)); | ||
748 | |||
749 | switch (fc->listen_info->service_addr->sa_family) | ||
750 | { | ||
751 | case AF_INET: | ||
752 | inet_ntop (fc->listen_info->service_addr->sa_family, (const void *) &((struct sockaddr_in *) fc->listen_info->service_addr)->sin_addr, listen_address, INET_ADDRSTRLEN); | ||
753 | if (strncmp (listen_address, "0.0.0.0:", 8) == 0 || strncmp (listen_address, "0.0.0.0", 7) == 0) | ||
754 | is_zero = 1; | ||
755 | is_ipv4 = 1; | ||
756 | listening_port = ((struct sockaddr_in *)fc->listen_info->service_addr)->sin_port; | ||
757 | break; | ||
758 | case AF_INET6: | ||
759 | inet_ntop (fc->listen_info->service_addr->sa_family, (const void *) &((struct sockaddr_in6 *) fc->listen_info->service_addr)->sin6_addr, listen_address, INET6_ADDRSTRLEN); | ||
760 | if (strncmp (listen_address, "[::]:", 5) == 0 || strncmp (listen_address, "::", 2) == 0) | ||
761 | is_zero = 1; | ||
762 | is_ipv6 = 1; | ||
763 | listening_port = ((struct sockaddr_in6 *)fc->listen_info->service_addr)->sin6_port; | ||
764 | break; | ||
765 | default: | ||
766 | break; | ||
767 | } | ||
768 | |||
769 | fc->service_connect_ipv4 = NULL; | ||
770 | fc->service_connect_ipv6 = NULL; | ||
771 | if (is_zero) | ||
772 | { | ||
773 | /* connect to [::1] and 127.0.0.1 instead of [::] and 0.0.0.0 */ | ||
774 | inet_pton (AF_INET, "127.0.0.1", &target_ipv4->sin_addr); | ||
775 | target_ipv4->sin_family = AF_INET; | ||
776 | target_ipv4->sin_port = listening_port; | ||
777 | is_ipv4 = 1; | ||
778 | free_ipv4 = 0; | ||
779 | |||
780 | inet_pton (AF_INET6, "0:0:0:0:0:0:0:1", &target_ipv6->sin6_addr); | ||
781 | target_ipv6->sin6_family = AF_INET6; | ||
782 | target_ipv6->sin6_port = listening_port; | ||
783 | is_ipv6 = 1; | ||
784 | free_ipv6 = 0; | ||
785 | } | ||
786 | else | ||
787 | { | ||
788 | if (is_ipv4) | ||
789 | memcpy (target_ipv4, fc->listen_info->service_addr, sizeof (struct sockaddr_in)); | ||
790 | else if (is_ipv6) | ||
791 | memcpy (target_ipv6, fc->listen_info->service_addr, sizeof (struct sockaddr_in6)); | ||
792 | } | ||
793 | |||
794 | if (is_ipv4) | ||
795 | failures += free_ipv4 = service_try_to_connect ((struct sockaddr *) target_ipv4, sizeof (struct sockaddr_in), fc); | ||
796 | if (is_ipv6) | ||
797 | failures += free_ipv6 = service_try_to_connect ((struct sockaddr *) target_ipv6, sizeof (struct sockaddr_in6), fc); | ||
798 | |||
799 | if (is_ipv4 + is_ipv6 <= failures) | ||
800 | fail = 1; | ||
801 | |||
802 | if (free_ipv4) | ||
803 | GNUNET_free (target_ipv4); | ||
804 | if (free_ipv6) | ||
805 | GNUNET_free (target_ipv6); | ||
806 | |||
807 | if (fail) | ||
808 | { | ||
809 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
810 | _ ("Unable to start service `%s': %s\n"), | ||
811 | fc->listen_info->serviceName, | ||
812 | STRERROR (errno)); | ||
813 | closeClientAndServiceSockets (fc, REASON_ERROR); | ||
814 | return; | ||
815 | } | ||
816 | } | ||
817 | |||
818 | static void | ||
819 | fc_acceptConnection_ipv4 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
820 | { | ||
821 | fc_acceptConnection (cls, tc, 1, 0); | ||
822 | } | ||
823 | |||
824 | static void | ||
825 | fc_acceptConnection_ipv6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
826 | { | ||
827 | fc_acceptConnection (cls, tc, 0, 1); | ||
828 | } | ||
829 | |||
830 | static void | ||
831 | fc_acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc, int is_ipv4, int is_ipv6) | ||
832 | { | ||
833 | struct ForwardedConnection *fc = cls; | ||
834 | struct ServiceListeningInfo *sli; | ||
835 | |||
836 | if (is_ipv4) | ||
837 | sli = fc->service_connect_ipv4; | ||
838 | else if (is_ipv6) | ||
839 | sli = fc->service_connect_ipv6; | ||
840 | else | ||
841 | GNUNET_break (0); | ||
842 | |||
843 | if ((tc->reason & (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT | GNUNET_SCHEDULER_REASON_PREREQ_DONE)) || ((tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY) && fc->armServiceSocket)) | ||
844 | { | ||
845 | GNUNET_NETWORK_socket_close (sli->listeningSocket); | ||
846 | if (is_ipv4) | ||
847 | fc->service_connect_ipv4 = NULL; | ||
848 | else if (is_ipv6) | ||
849 | fc->service_connect_ipv6 = NULL; | ||
850 | } | ||
851 | else if (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY) | ||
852 | { | ||
853 | #if DEBUG_SERVICE_MANAGER | ||
854 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
855 | "Connected to service, now starting forwarding\n"); | ||
856 | #endif | ||
857 | fc->armServiceSocket = sli->listeningSocket; | ||
858 | if ((is_ipv4 && fc->service_connect_ipv6 != NULL)) | ||
720 | { | 859 | { |
721 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 860 | GNUNET_SCHEDULER_cancel (fc->service_connect_ipv6->acceptTask); |
722 | _("Unable to forward to service `%s': timeout before connect\n"), | 861 | fc->service_connect_ipv6->acceptTask = GNUNET_SCHEDULER_add_now (fc_acceptConnection_ipv6, fc); |
723 | fc->listen_info->serviceName); | ||
724 | closeClientAndServiceSockets (fc, REASON_ERROR); | ||
725 | return; | ||
726 | } | ||
727 | fc->armServiceSocket = | ||
728 | GNUNET_NETWORK_socket_create (fc->listen_info->service_addr->sa_family, | ||
729 | SOCK_STREAM, 0); | ||
730 | if (NULL == fc->armServiceSocket) | ||
731 | { | ||
732 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
733 | _ ("Unable to start service `%s': %s\n"), | ||
734 | fc->listen_info->serviceName, | ||
735 | STRERROR (errno)); | ||
736 | closeClientAndServiceSockets (fc, REASON_ERROR); | ||
737 | return; | ||
738 | } | 862 | } |
739 | if ( (GNUNET_SYSERR == | 863 | else if (is_ipv6 && fc->service_connect_ipv4 != NULL) |
740 | GNUNET_NETWORK_socket_connect (fc->armServiceSocket, | ||
741 | fc->listen_info->service_addr, | ||
742 | fc->listen_info->service_addr_len)) && | ||
743 | (errno != EINPROGRESS) ) | ||
744 | { | 864 | { |
745 | GNUNET_break (GNUNET_OK == | 865 | GNUNET_SCHEDULER_cancel (fc->service_connect_ipv4->acceptTask); |
746 | GNUNET_NETWORK_socket_close (fc->armServiceSocket)); | 866 | fc->service_connect_ipv4->acceptTask = GNUNET_SCHEDULER_add_now (fc_acceptConnection_ipv4, fc); |
747 | fc->armServiceSocket = NULL; | ||
748 | fc->back_off = GNUNET_TIME_relative_multiply (fc->back_off, 2); | ||
749 | #if DEBUG_SERVICE_MANAGER | ||
750 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
751 | "Failed to connected to service `%s' at `%s', will try again in %llu ms\n", | ||
752 | fc->listen_info->serviceName, | ||
753 | GNUNET_a2s (fc->listen_info->service_addr, | ||
754 | fc->listen_info->service_addr_len), | ||
755 | (unsigned long long) GNUNET_TIME_relative_min (fc->back_off, | ||
756 | rem).rel_value); | ||
757 | #endif | ||
758 | GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == fc->start_task); | ||
759 | fc->start_task | ||
760 | = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_min (fc->back_off, | ||
761 | rem), | ||
762 | &start_forwarding, | ||
763 | fc); | ||
764 | return; | ||
765 | } | 867 | } |
766 | #if DEBUG_SERVICE_MANAGER | 868 | GNUNET_free (fc->listen_info->service_addr); |
767 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 869 | fc->listen_info->service_addr = sli->service_addr; |
768 | "Connected to service, now starting forwarding\n"); | 870 | fc->listen_info->service_addr_len = sli->service_addr_len; |
769 | #endif | 871 | /*fc->listen_info->listeningSocket is it closed already?*/ |
770 | if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK) | 872 | if (fc->client_to_service_task == GNUNET_SCHEDULER_NO_TASK) |
771 | { | 873 | { |
772 | if (fc->client_to_service_bufferDataLength == 0) | 874 | if (fc->client_to_service_bufferDataLength == 0) |
773 | fc->client_to_service_task = | 875 | fc->client_to_service_task = |
774 | GNUNET_SCHEDULER_add_read_net ( | 876 | GNUNET_SCHEDULER_add_read_net ( |
775 | GNUNET_TIME_UNIT_FOREVER_REL, | 877 | GNUNET_TIME_UNIT_FOREVER_REL, |
776 | fc->armClientSocket, | 878 | fc->armClientSocket, |
777 | &receiveFromClient, fc); | 879 | &receiveFromClient, fc); |
778 | else | 880 | else |
779 | fc->client_to_service_task = | 881 | fc->client_to_service_task = |
780 | GNUNET_SCHEDULER_add_write_net ( | 882 | GNUNET_SCHEDULER_add_write_net ( |
781 | GNUNET_TIME_UNIT_FOREVER_REL, | 883 | GNUNET_TIME_UNIT_FOREVER_REL, |
782 | fc->armServiceSocket, | 884 | fc->armServiceSocket, |
783 | &forwardToService, fc); | 885 | &forwardToService, fc); |
784 | } | 886 | } |
785 | if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK) | 887 | if (fc->service_to_client_task == GNUNET_SCHEDULER_NO_TASK) |
786 | { | 888 | { |
787 | if (fc->service_to_client_bufferDataLength == 0) | 889 | if (fc->service_to_client_bufferDataLength == 0) |
788 | fc->service_to_client_task = | 890 | fc->service_to_client_task = |
789 | GNUNET_SCHEDULER_add_read_net ( | 891 | GNUNET_SCHEDULER_add_read_net ( |
790 | GNUNET_TIME_UNIT_FOREVER_REL, | 892 | GNUNET_TIME_UNIT_FOREVER_REL, |
791 | fc->armServiceSocket, | 893 | fc->armServiceSocket, |
792 | &receiveFromService, fc); | 894 | &receiveFromService, fc); |
793 | else | 895 | else |
794 | fc->service_to_client_task = | 896 | fc->service_to_client_task = |
795 | GNUNET_SCHEDULER_add_write_net ( | 897 | GNUNET_SCHEDULER_add_write_net ( |
796 | GNUNET_TIME_UNIT_FOREVER_REL, | 898 | GNUNET_TIME_UNIT_FOREVER_REL, |
797 | fc->armClientSocket, | 899 | fc->armClientSocket, |
798 | &forwardToClient, fc); | 900 | &forwardToClient, fc); |
799 | } | 901 | } |
902 | } | ||
903 | else | ||
904 | { | ||
905 | GNUNET_break (0); | ||
906 | } | ||
907 | GNUNET_free (sli); | ||
908 | } | ||
909 | |||
910 | static int | ||
911 | service_try_to_connect (struct sockaddr *addr, socklen_t addrlen, struct ForwardedConnection *fc) | ||
912 | { | ||
913 | struct GNUNET_NETWORK_Handle *sock; | ||
914 | struct ServiceListeningInfo *serviceListeningInfo; | ||
915 | |||
916 | sock = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); | ||
917 | if (sock == NULL) | ||
918 | { | ||
919 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to create a socket\n"); | ||
920 | return 1; | ||
921 | } | ||
922 | |||
923 | if ( (GNUNET_SYSERR == GNUNET_NETWORK_socket_connect (sock, addr, addrlen)) && | ||
924 | (errno != EINPROGRESS) ) | ||
925 | { | ||
926 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect\n"); | ||
927 | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); | ||
928 | return 1; | ||
929 | } | ||
930 | |||
931 | serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo)); | ||
932 | serviceListeningInfo->serviceName = NULL; | ||
933 | serviceListeningInfo->service_addr = addr; | ||
934 | serviceListeningInfo->service_addr_len = addrlen; | ||
935 | serviceListeningInfo->listeningSocket = sock; | ||
936 | |||
937 | switch (addrlen) | ||
938 | { | ||
939 | case sizeof (struct sockaddr_in): | ||
940 | fc->service_connect_ipv4 = serviceListeningInfo; | ||
941 | serviceListeningInfo->acceptTask = | ||
942 | GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
943 | serviceListeningInfo->listeningSocket, | ||
944 | &fc_acceptConnection_ipv4, fc); | ||
945 | break; | ||
946 | case sizeof (struct sockaddr_in6): | ||
947 | fc->service_connect_ipv6 = serviceListeningInfo; | ||
948 | serviceListeningInfo->acceptTask = | ||
949 | GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
950 | serviceListeningInfo->listeningSocket, | ||
951 | &fc_acceptConnection_ipv6, fc); | ||
952 | break; | ||
953 | default: | ||
954 | GNUNET_break (0); | ||
955 | return 1; | ||
956 | break; | ||
957 | } | ||
958 | return 0; | ||
800 | } | 959 | } |
801 | 960 | ||
802 | 961 | ||