libpurple/proxy.c

branch
purple-proxy-to-gio
changeset 37536
7f1d2f3ba6ae
parent 37535
447036b8779a
child 37537
9e1b392c606b
equal deleted inserted replaced
37535:447036b8779a 37536:7f1d2f3ba6ae
97 97
98 static PurpleProxyInfo *global_proxy_info = NULL; 98 static PurpleProxyInfo *global_proxy_info = NULL;
99 99
100 static GSList *handles = NULL; 100 static GSList *handles = NULL;
101 101
102 static void try_connect(PurpleProxyConnectData *connect_data);
103
104 /* 102 /*
105 * TODO: Eventually (GObjectification) this bad boy will be removed, because it is 103 * TODO: Eventually (GObjectification) this bad boy will be removed, because it is
106 * a gross fix for a crashy problem. 104 * a gross fix for a crashy problem.
107 */ 105 */
108 #define PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data) g_slist_find(handles, connect_data) 106 #define PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data) g_slist_find(handles, connect_data)
637 635
638 if (error_message != NULL) 636 if (error_message != NULL)
639 { 637 {
640 purple_debug_error("proxy", "Connection attempt failed: %s\n", 638 purple_debug_error("proxy", "Connection attempt failed: %s\n",
641 error_message); 639 error_message);
642 if (connect_data->hosts != NULL) 640
643 try_connect(connect_data); 641 /* Everything failed! Tell the originator of the request. */
644 else 642 connect_data->connect_cb(connect_data->data, -1, error_message);
645 { 643 purple_proxy_connect_data_destroy(connect_data);
646 /* Everything failed! Tell the originator of the request. */ 644 }
647 connect_data->connect_cb(connect_data->data, -1, error_message);
648 purple_proxy_connect_data_destroy(connect_data);
649 }
650 }
651 }
652
653 /*
654 * This calls purple_proxy_connect_data_disconnect(), but it lets you
655 * specify the error_message using a printf()-like syntax.
656 */
657 static void
658 purple_proxy_connect_data_disconnect_formatted(PurpleProxyConnectData *connect_data, const char *format, ...)
659 {
660 va_list args;
661 gchar *tmp;
662
663 va_start(args, format);
664 tmp = g_strdup_vprintf(format, args);
665 va_end(args);
666
667 purple_proxy_connect_data_disconnect(connect_data, tmp);
668 g_free(tmp);
669 } 645 }
670 646
671 static void 647 static void
672 purple_proxy_connect_data_connected(PurpleProxyConnectData *connect_data) 648 purple_proxy_connect_data_connected(PurpleProxyConnectData *connect_data)
673 { 649 {
683 */ 659 */
684 connect_data->fd = -1; 660 connect_data->fd = -1;
685 661
686 purple_proxy_connect_data_disconnect(connect_data, NULL); 662 purple_proxy_connect_data_disconnect(connect_data, NULL);
687 purple_proxy_connect_data_destroy(connect_data); 663 purple_proxy_connect_data_destroy(connect_data);
688 }
689
690 static void
691 socket_ready_cb(gpointer data, gint source, PurpleInputCondition cond)
692 {
693 PurpleProxyConnectData *connect_data = data;
694 int error = 0;
695 int ret;
696
697 /* If the socket-connected message had already been triggered when connect_data
698 * was destroyed via purple_proxy_connect_cancel(), we may get here with a freed connect_data.
699 */
700 if (!PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data))
701 return;
702
703 purple_debug_info("proxy", "Connecting to %s:%d.\n",
704 connect_data->host, connect_data->port);
705
706 /*
707 * purple_input_get_error after a non-blocking connect returns -1 if something is
708 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
709 * error holds what connect would have returned if it blocked until now.
710 * Thus, error == 0 is success, error == EINPROGRESS means "try again",
711 * and anything else is a real error.
712 *
713 * (error == EINPROGRESS can happen after a select because the kernel can
714 * be overly optimistic sometimes. select is just a hint that you might be
715 * able to do something.)
716 */
717 ret = purple_input_get_error(connect_data->fd, &error);
718
719 if (ret == 0 && error == EINPROGRESS) {
720 /* No worries - we'll be called again later */
721 /* TODO: Does this ever happen? */
722 purple_debug_info("proxy", "(ret == 0 && error == EINPROGRESS)\n");
723 return;
724 }
725
726 if (ret != 0 || error != 0) {
727 if (ret != 0)
728 error = errno;
729 purple_debug_error("proxy", "Error connecting to %s:%d (%s).\n",
730 connect_data->host, connect_data->port, g_strerror(error));
731
732 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
733 return;
734 }
735
736 purple_proxy_connect_data_connected(connect_data);
737 }
738
739 static gboolean
740 clean_connect(gpointer data)
741 {
742 purple_proxy_connect_data_connected(data);
743
744 return FALSE;
745 }
746
747 static void
748 proxy_connect_udp_none(PurpleProxyConnectData *connect_data, common_sockaddr_t *addr, socklen_t addrlen)
749 {
750 purple_debug_info("proxy", "UDP Connecting to %s:%d with no proxy\n",
751 connect_data->host, connect_data->port);
752
753 connect_data->fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0);
754 if (connect_data->fd < 0)
755 {
756 purple_proxy_connect_data_disconnect_formatted(connect_data,
757 _("Unable to create socket: %s"), g_strerror(errno));
758 return;
759 }
760 _purple_network_set_common_socket_flags(connect_data->fd);
761
762 if (connect(connect_data->fd, &addr->sa, addrlen) != 0)
763 {
764 if ((errno == EINPROGRESS) || (errno == EINTR))
765 {
766 purple_debug_info("proxy", "UDP connection in progress\n");
767 connect_data->inpa = purple_input_add(connect_data->fd,
768 PURPLE_INPUT_WRITE, socket_ready_cb, connect_data);
769 }
770 else
771 {
772 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
773 }
774 }
775 else
776 {
777 /*
778 * The connection happened IMMEDIATELY... strange, but whatever.
779 */
780 int error = ETIMEDOUT;
781 int ret;
782
783 purple_debug_info("proxy", "UDP Connected immediately.\n");
784
785 ret = purple_input_get_error(connect_data->fd, &error);
786 if ((ret != 0) || (error != 0))
787 {
788 if (ret != 0)
789 error = errno;
790 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
791 return;
792 }
793
794 /*
795 * We want to call the "connected" callback eventually, but we
796 * don't want to call it before we return, just in case.
797 */
798 purple_timeout_add(10, clean_connect, connect_data);
799 }
800 }
801
802 static void
803 proxy_connect_none(PurpleProxyConnectData *connect_data, common_sockaddr_t *addr, socklen_t addrlen)
804 {
805 purple_debug_info("proxy", "Connecting to %s:%d with no proxy\n",
806 connect_data->host, connect_data->port);
807
808 connect_data->fd = socket(addr->sa.sa_family, SOCK_STREAM, 0);
809 if (connect_data->fd < 0)
810 {
811 purple_proxy_connect_data_disconnect_formatted(connect_data,
812 _("Unable to create socket: %s"), g_strerror(errno));
813 return;
814 }
815 _purple_network_set_common_socket_flags(connect_data->fd);
816
817 if (connect(connect_data->fd, &addr->sa, addrlen) != 0)
818 {
819 if ((errno == EINPROGRESS) || (errno == EINTR))
820 {
821 purple_debug_info("proxy", "Connection in progress\n");
822 connect_data->inpa = purple_input_add(connect_data->fd,
823 PURPLE_INPUT_WRITE, socket_ready_cb, connect_data);
824 }
825 else
826 {
827 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
828 }
829 }
830 else
831 {
832 /*
833 * The connection happened IMMEDIATELY... strange, but whatever.
834 */
835 int error = ETIMEDOUT;
836 int ret;
837
838 purple_debug_info("proxy", "Connected immediately.\n");
839
840 ret = purple_input_get_error(connect_data->fd, &error);
841 if ((ret != 0) || (error != 0))
842 {
843 if (ret != 0)
844 error = errno;
845 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
846 return;
847 }
848
849 /*
850 * We want to call the "connected" callback eventually, but we
851 * don't want to call it before we return, just in case.
852 */
853 purple_timeout_add(10, clean_connect, connect_data);
854 }
855 }
856
857 /*
858 * This is a utility function used by the HTTP, SOCKS4 and SOCKS5
859 * connect functions. It writes data from a buffer to a socket.
860 * When all the data is written it sets up a watcher to read a
861 * response and call a specified function.
862 */
863 static void
864 proxy_do_write(gpointer data, gint source, PurpleInputCondition cond)
865 {
866 PurpleProxyConnectData *connect_data;
867 const guchar *request;
868 gsize request_len;
869 int ret;
870
871 connect_data = data;
872 request = connect_data->write_buffer + connect_data->written_len;
873 request_len = connect_data->write_buf_len - connect_data->written_len;
874
875 ret = write(connect_data->fd, request, request_len);
876 if (ret <= 0)
877 {
878 if (errno == EAGAIN)
879 /* No worries */
880 return;
881
882 /* Error! */
883 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
884 return;
885 }
886 if ((gsize)ret < request_len) {
887 connect_data->written_len += ret;
888 return;
889 }
890
891 /* We're done writing data! Wait for a response. */
892 g_free(connect_data->write_buffer);
893 connect_data->write_buffer = NULL;
894 purple_input_remove(connect_data->inpa);
895 connect_data->inpa = purple_input_add(connect_data->fd,
896 PURPLE_INPUT_READ, connect_data->read_cb, connect_data);
897 }
898
899 /*
900 * We're using an HTTP proxy for a non-port 80 tunnel. Read the
901 * response to the CONNECT request.
902 */
903 static void
904 http_canread(gpointer data, gint source, PurpleInputCondition cond)
905 {
906 int len, headers_len, status = 0;
907 gboolean error;
908 PurpleProxyConnectData *connect_data = data;
909 char *p;
910 gsize max_read;
911
912 if (connect_data->read_buffer == NULL) {
913 connect_data->read_buf_len = 8192;
914 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
915 connect_data->read_len = 0;
916 }
917
918 p = (char *)connect_data->read_buffer + connect_data->read_len;
919 max_read = connect_data->read_buf_len - connect_data->read_len - 1;
920
921 len = read(connect_data->fd, p, max_read);
922
923 if (len == 0) {
924 purple_proxy_connect_data_disconnect(connect_data,
925 _("Server closed the connection"));
926 return;
927 }
928
929 if (len < 0) {
930 if (errno == EAGAIN)
931 /* No worries */
932 return;
933
934 /* Error! */
935 purple_proxy_connect_data_disconnect_formatted(connect_data,
936 _("Lost connection with server: %s"), g_strerror(errno));
937 return;
938 }
939
940 connect_data->read_len += len;
941 p[len] = '\0';
942
943 p = g_strstr_len((const gchar *)connect_data->read_buffer,
944 connect_data->read_len, "\r\n\r\n");
945 if (p != NULL) {
946 *p = '\0';
947 headers_len = (p - (char *)connect_data->read_buffer) + 4;
948 } else if((gsize)len == max_read)
949 headers_len = len;
950 else
951 return;
952
953 error = strncmp((const char *)connect_data->read_buffer, "HTTP/", 5) != 0;
954 if (!error) {
955 int major;
956 p = (char *)connect_data->read_buffer + 5;
957 major = strtol(p, &p, 10);
958 error = (major == 0) || (*p != '.');
959 if(!error) {
960 int minor;
961 p++;
962 minor = strtol(p, &p, 10);
963 error = (*p != ' ');
964 if(!error) {
965 p++;
966 status = strtol(p, &p, 10);
967 error = (*p != ' ');
968 (void)minor; /* we don't need its value */
969 }
970 }
971 }
972
973 /* Read the contents */
974 p = g_strrstr((const gchar *)connect_data->read_buffer, "Content-Length: ");
975 if (p != NULL) {
976 gchar *tmp;
977 gsize content_len;
978 char tmpc;
979
980 p += strlen("Content-Length: ");
981 tmp = strchr(p, '\r');
982 if(tmp)
983 *tmp = '\0';
984 if (sscanf(p, "%" G_GSIZE_FORMAT, &content_len) != 1) {
985 /* Couldn't read content length */
986 purple_debug_error("proxy", "Couldn't read content length value "
987 "from %s\n", p);
988 if(tmp)
989 *tmp = '\r';
990 purple_proxy_connect_data_disconnect_formatted(connect_data,
991 _("Unable to parse response from HTTP proxy: %s"),
992 connect_data->read_buffer);
993 return;
994 }
995 if(tmp)
996 *tmp = '\r';
997
998 /* Compensate for what has already been read */
999 content_len -= connect_data->read_len - headers_len;
1000 /* I'm assuming that we're doing this to prevent the server from
1001 complaining / breaking since we don't read the whole page */
1002 while (content_len--) {
1003 /* TODO: deal with EAGAIN (and other errors) better */
1004 /* TODO: Reading 1 byte at a time is horrible and stupid. */
1005 if (read(connect_data->fd, &tmpc, 1) < 0 && errno != EAGAIN)
1006 break;
1007 }
1008 }
1009
1010 if (error) {
1011 purple_proxy_connect_data_disconnect_formatted(connect_data,
1012 _("Unable to parse response from HTTP proxy: %s"),
1013 connect_data->read_buffer);
1014 return;
1015 }
1016 else if (status != 200) {
1017 purple_debug_error("proxy",
1018 "Proxy server replied with:\n%s\n",
1019 connect_data->read_buffer);
1020
1021 if (status == 407 /* Proxy Auth */) {
1022 const char *header;
1023 gchar *request;
1024
1025 header = g_strrstr((const gchar *)connect_data->read_buffer,
1026 "Proxy-Authenticate: NTLM");
1027 if (header != NULL) {
1028 const char *header_end = header + strlen("Proxy-Authenticate: NTLM");
1029 const char *domain = purple_proxy_info_get_username(connect_data->gpi);
1030 char *username = NULL, hostname[256];
1031 gchar *response;
1032 int ret;
1033
1034 ret = gethostname(hostname, sizeof(hostname));
1035 hostname[sizeof(hostname) - 1] = '\0';
1036 if (ret < 0 || hostname[0] == '\0') {
1037 purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?");
1038 g_strlcpy(hostname, "localhost", sizeof(hostname));
1039 }
1040
1041 if (domain != NULL)
1042 username = (char*) strchr(domain, '\\');
1043 if (username == NULL) {
1044 purple_proxy_connect_data_disconnect_formatted(connect_data,
1045 _("HTTP proxy connection error %d"), status);
1046 return;
1047 }
1048 *username = '\0';
1049
1050 /* Is there a message? */
1051 if (*header_end == ' ') {
1052 /* Check for Type-2 */
1053 char *tmp = (char*) header;
1054 guint8 *nonce;
1055
1056 header_end++;
1057 username++;
1058 while(*tmp != '\r' && *tmp != '\0') tmp++;
1059 *tmp = '\0';
1060 nonce = purple_ntlm_parse_type2(header_end, NULL);
1061 response = purple_ntlm_gen_type3(username,
1062 (gchar*) purple_proxy_info_get_password(connect_data->gpi),
1063 hostname,
1064 domain, nonce, NULL);
1065 username--;
1066 } else /* Empty message */
1067 response = purple_ntlm_gen_type1(hostname, domain);
1068
1069 *username = '\\';
1070
1071 request = g_strdup_printf(
1072 "CONNECT %s:%d HTTP/1.1\r\n"
1073 "Host: %s:%d\r\n"
1074 "Proxy-Authorization: NTLM %s\r\n"
1075 "Proxy-Connection: Keep-Alive\r\n\r\n",
1076 connect_data->host, connect_data->port,
1077 connect_data->host, connect_data->port,
1078 response);
1079
1080 g_free(response);
1081
1082 } else if (g_strrstr((const char *)connect_data->read_buffer, "Proxy-Authenticate: Basic") != NULL) {
1083 gchar *t1, *t2;
1084 const char *username, *password;
1085
1086 username = purple_proxy_info_get_username(connect_data->gpi);
1087 password = purple_proxy_info_get_password(connect_data->gpi);
1088
1089 t1 = g_strdup_printf("%s:%s",
1090 username ? username : "",
1091 password ? password : "");
1092 t2 = purple_base64_encode((guchar *)t1, strlen(t1));
1093 g_free(t1);
1094
1095 request = g_strdup_printf(
1096 "CONNECT %s:%d HTTP/1.1\r\n"
1097 "Host: %s:%d\r\n"
1098 "Proxy-Authorization: Basic %s\r\n",
1099 connect_data->host, connect_data->port,
1100 connect_data->host, connect_data->port,
1101 t2);
1102
1103 g_free(t2);
1104
1105 } else {
1106 purple_proxy_connect_data_disconnect_formatted(connect_data,
1107 _("HTTP proxy connection error %d"), status);
1108 return;
1109 }
1110
1111 purple_input_remove(connect_data->inpa);
1112 g_free(connect_data->read_buffer);
1113 connect_data->read_buffer = NULL;
1114
1115 connect_data->write_buffer = (guchar *)request;
1116 connect_data->write_buf_len = strlen(request);
1117 connect_data->written_len = 0;
1118
1119 connect_data->read_cb = http_canread;
1120
1121 connect_data->inpa = purple_input_add(connect_data->fd,
1122 PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1123
1124 proxy_do_write(connect_data, connect_data->fd, cond);
1125
1126 return;
1127 }
1128
1129 if (status == 403) {
1130 /* Forbidden */
1131 purple_proxy_connect_data_disconnect_formatted(connect_data,
1132 _("Access denied: HTTP proxy server forbids port %d tunneling"),
1133 connect_data->port);
1134 } else {
1135 purple_proxy_connect_data_disconnect_formatted(connect_data,
1136 _("HTTP proxy connection error %d"), status);
1137 }
1138 } else {
1139 purple_input_remove(connect_data->inpa);
1140 connect_data->inpa = 0;
1141 g_free(connect_data->read_buffer);
1142 connect_data->read_buffer = NULL;
1143 purple_debug_info("proxy", "HTTP proxy connection established\n");
1144 purple_proxy_connect_data_connected(connect_data);
1145 return;
1146 }
1147 }
1148
1149 static void
1150 http_start_connect_tunneling(PurpleProxyConnectData *connect_data) {
1151 GString *request;
1152 int ret;
1153
1154 purple_debug_info("proxy", "Using CONNECT tunneling for %s:%d\n",
1155 connect_data->host, connect_data->port);
1156
1157 request = g_string_sized_new(4096);
1158 g_string_append_printf(request,
1159 "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n",
1160 connect_data->host, connect_data->port,
1161 connect_data->host, connect_data->port);
1162
1163 if (purple_proxy_info_get_username(connect_data->gpi) != NULL)
1164 {
1165 char *t1, *t2, *ntlm_type1;
1166 char hostname[256];
1167
1168 ret = gethostname(hostname, sizeof(hostname));
1169 hostname[sizeof(hostname) - 1] = '\0';
1170 if (ret < 0 || hostname[0] == '\0') {
1171 purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?");
1172 g_strlcpy(hostname, "localhost", sizeof(hostname));
1173 }
1174
1175 t1 = g_strdup_printf("%s:%s",
1176 purple_proxy_info_get_username(connect_data->gpi),
1177 purple_proxy_info_get_password(connect_data->gpi) ?
1178 purple_proxy_info_get_password(connect_data->gpi) : "");
1179 t2 = purple_base64_encode((const guchar *)t1, strlen(t1));
1180 g_free(t1);
1181
1182 ntlm_type1 = purple_ntlm_gen_type1(hostname, "");
1183
1184 g_string_append_printf(request,
1185 "Proxy-Authorization: Basic %s\r\n"
1186 "Proxy-Authorization: NTLM %s\r\n"
1187 "Proxy-Connection: Keep-Alive\r\n",
1188 t2, ntlm_type1);
1189 g_free(ntlm_type1);
1190 g_free(t2);
1191 }
1192
1193 g_string_append(request, "\r\n");
1194
1195 connect_data->write_buf_len = request->len;
1196 connect_data->write_buffer = (guchar *)g_string_free(request, FALSE);
1197 connect_data->written_len = 0;
1198 connect_data->read_cb = http_canread;
1199
1200 connect_data->inpa = purple_input_add(connect_data->fd,
1201 PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1202 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1203 }
1204
1205 static void
1206 http_canwrite(gpointer data, gint source, PurpleInputCondition cond) {
1207 PurpleProxyConnectData *connect_data = data;
1208 int ret, error = ETIMEDOUT;
1209
1210 purple_debug_info("proxy", "Connected to %s:%d.\n",
1211 connect_data->host, connect_data->port);
1212
1213 if (connect_data->inpa > 0) {
1214 purple_input_remove(connect_data->inpa);
1215 connect_data->inpa = 0;
1216 }
1217
1218 ret = purple_input_get_error(connect_data->fd, &error);
1219 if (ret != 0 || error != 0) {
1220 if (ret != 0)
1221 error = errno;
1222 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
1223 return;
1224 }
1225
1226 if (connect_data->port == 80) {
1227 /*
1228 * If we're trying to connect to something running on
1229 * port 80 then we assume the traffic using this
1230 * connection is going to be HTTP traffic. If it's
1231 * not then this will fail (uglily). But it's good
1232 * to avoid using the CONNECT method because it's
1233 * not always allowed.
1234 */
1235 purple_debug_info("proxy", "HTTP proxy connection established\n");
1236 purple_proxy_connect_data_connected(connect_data);
1237 } else {
1238 http_start_connect_tunneling(connect_data);
1239 }
1240
1241 }
1242
1243 static void
1244 proxy_connect_http(PurpleProxyConnectData *connect_data, common_sockaddr_t *addr, socklen_t addrlen)
1245 {
1246 purple_debug_info("proxy",
1247 "Connecting to %s:%d via %s:%d using HTTP\n",
1248 connect_data->host, connect_data->port,
1249 (purple_proxy_info_get_host(connect_data->gpi) ? purple_proxy_info_get_host(connect_data->gpi) : "(null)"),
1250 purple_proxy_info_get_port(connect_data->gpi));
1251
1252 connect_data->fd = socket(addr->sa.sa_family, SOCK_STREAM, 0);
1253 if (connect_data->fd < 0)
1254 {
1255 purple_proxy_connect_data_disconnect_formatted(connect_data,
1256 _("Unable to create socket: %s"), g_strerror(errno));
1257 return;
1258 }
1259 _purple_network_set_common_socket_flags(connect_data->fd);
1260
1261 if (connect(connect_data->fd, &addr->sa, addrlen) != 0) {
1262 if (errno == EINPROGRESS || errno == EINTR) {
1263 purple_debug_info("proxy", "HTTP connection in progress\n");
1264
1265 connect_data->inpa = purple_input_add(connect_data->fd,
1266 PURPLE_INPUT_WRITE, http_canwrite, connect_data);
1267 } else
1268 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
1269 } else {
1270 purple_debug_info("proxy", "Connected immediately.\n");
1271
1272 http_canwrite(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1273 }
1274 }
1275
1276 static void
1277 s4_canread(gpointer data, gint source, PurpleInputCondition cond)
1278 {
1279 PurpleProxyConnectData *connect_data = data;
1280 guchar *buf;
1281 int len, max_read;
1282
1283 /* This is really not going to block under normal circumstances, but to
1284 * be correct, we deal with the unlikely scenario */
1285
1286 if (connect_data->read_buffer == NULL) {
1287 connect_data->read_buf_len = 12;
1288 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
1289 connect_data->read_len = 0;
1290 }
1291
1292 buf = connect_data->read_buffer + connect_data->read_len;
1293 max_read = connect_data->read_buf_len - connect_data->read_len;
1294
1295 len = read(connect_data->fd, buf, max_read);
1296
1297 if ((len < 0 && errno == EAGAIN) || (len > 0 && len + connect_data->read_len < 4))
1298 return;
1299 else if (len + connect_data->read_len >= 4) {
1300 if (connect_data->read_buffer[1] == 90) {
1301 purple_proxy_connect_data_connected(connect_data);
1302 return;
1303 }
1304 }
1305
1306 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
1307 }
1308
1309 static void
1310 s4_host_resolved(GObject *source_object, GAsyncResult *res, gpointer data)
1311 {
1312 GInetAddress *address = NULL;
1313 GError *error = NULL;
1314 GList *hosts = NULL, *l = NULL;
1315 PurpleProxyConnectData *connect_data = data;
1316 unsigned char packet[9];
1317
1318 if(G_IS_CANCELLABLE(connect_data->cancellable)) {
1319 g_object_unref(G_OBJECT(connect_data->cancellable));
1320
1321 connect_data->cancellable = NULL;
1322 }
1323
1324 hosts = g_resolver_lookup_by_name_finish(G_RESOLVER(source_object),
1325 res, &error);
1326
1327 if (error->message != NULL) {
1328 purple_proxy_connect_data_disconnect(connect_data, error->message);
1329
1330 g_error_free(error);
1331
1332 return;
1333 }
1334
1335 if (hosts == NULL) {
1336 purple_proxy_connect_data_disconnect_formatted(connect_data,
1337 _("Error resolving %s"), connect_data->host);
1338 return;
1339 }
1340
1341 for(l = hosts; l; l = l->next) {
1342 address = G_INET_ADDRESS(l->data);
1343
1344 if(!g_inet_address_get_is_loopback(address) && !g_inet_address_get_is_link_local(address))
1345 break;
1346
1347 address = NULL;
1348 }
1349
1350 if(address != NULL) {
1351 packet[0] = 0x04;
1352 packet[1] = 0x01;
1353 packet[2] = connect_data->port >> 8;
1354 packet[3] = connect_data->port & 0xff;
1355 memcpy(packet + 4, g_inet_address_to_bytes(address), 4);
1356 packet[8] = 0x00;
1357
1358 connect_data->write_buffer = g_memdup(packet, sizeof(packet));
1359 connect_data->write_buf_len = sizeof(packet);
1360 connect_data->written_len = 0;
1361 connect_data->read_cb = s4_canread;
1362
1363 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1364
1365 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1366 } else {
1367 purple_proxy_connect_data_disconnect_formatted(connect_data,
1368 _("Error resolving %s"), connect_data->host);
1369 }
1370
1371 g_resolver_free_addresses(hosts);
1372 }
1373
1374 static void
1375 s4_canwrite(gpointer data, gint source, PurpleInputCondition cond)
1376 {
1377 PurpleProxyConnectData *connect_data = data;
1378 int error = ETIMEDOUT;
1379 int ret;
1380
1381 purple_debug_info("socks4 proxy", "Connected.\n");
1382
1383 if (connect_data->inpa > 0) {
1384 purple_input_remove(connect_data->inpa);
1385 connect_data->inpa = 0;
1386 }
1387
1388 ret = purple_input_get_error(connect_data->fd, &error);
1389 if ((ret != 0) || (error != 0)) {
1390 if (ret != 0)
1391 error = errno;
1392 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
1393 return;
1394 }
1395
1396 /*
1397 * The socks4 spec doesn't include support for doing host name lookups by
1398 * the proxy. Many socks4 servers do this via the "socks4a" extension to
1399 * the protocol. There doesn't appear to be a way to detect if a server
1400 * supports this, so we require that the user set a global option.
1401 */
1402 if (purple_prefs_get_bool("/purple/proxy/socks4_remotedns")) {
1403 unsigned char packet[9];
1404 int len;
1405
1406 purple_debug_info("socks4 proxy", "Attempting to use remote DNS.\n");
1407
1408 packet[0] = 0x04;
1409 packet[1] = 0x01;
1410 packet[2] = connect_data->port >> 8;
1411 packet[3] = connect_data->port & 0xff;
1412 packet[4] = 0x00;
1413 packet[5] = 0x00;
1414 packet[6] = 0x00;
1415 packet[7] = 0x01;
1416 packet[8] = 0x00;
1417
1418 len = sizeof(packet) + strlen(connect_data->host) + 1;
1419
1420 connect_data->write_buffer = g_malloc0(len);
1421 memcpy(connect_data->write_buffer, packet, sizeof(packet));
1422 memcpy(connect_data->write_buffer + sizeof(packet), connect_data->host, strlen(connect_data->host));
1423 connect_data->write_buf_len = len;
1424 connect_data->written_len = 0;
1425 connect_data->read_cb = s4_canread;
1426
1427 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1428
1429 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1430 } else {
1431 GResolver *resolver = NULL;
1432
1433 connect_data->cancellable = g_cancellable_new();
1434
1435 resolver = g_resolver_get_default();
1436
1437 g_resolver_lookup_by_name_async(resolver,
1438 connect_data->host,
1439 connect_data->cancellable,
1440 s4_host_resolved,
1441 connect_data);
1442
1443 g_object_unref(G_OBJECT(resolver));
1444
1445 if (connect_data->cancellable == NULL) {
1446 purple_debug_error("proxy", "dns query failed unexpectedly.\n");
1447 purple_proxy_connect_data_destroy(connect_data);
1448 }
1449 }
1450 }
1451
1452 static void
1453 proxy_connect_socks4(PurpleProxyConnectData *connect_data, common_sockaddr_t *addr, socklen_t addrlen)
1454 {
1455 purple_debug_info("proxy",
1456 "Connecting to %s:%d via %s:%d using SOCKS4\n",
1457 connect_data->host, connect_data->port,
1458 purple_proxy_info_get_host(connect_data->gpi),
1459 purple_proxy_info_get_port(connect_data->gpi));
1460
1461 connect_data->fd = socket(addr->sa.sa_family, SOCK_STREAM, 0);
1462 if (connect_data->fd < 0)
1463 {
1464 purple_proxy_connect_data_disconnect_formatted(connect_data,
1465 _("Unable to create socket: %s"), g_strerror(errno));
1466 return;
1467 }
1468 _purple_network_set_common_socket_flags(connect_data->fd);
1469
1470 if (connect(connect_data->fd, &addr->sa, addrlen) != 0)
1471 {
1472 if ((errno == EINPROGRESS) || (errno == EINTR))
1473 {
1474 purple_debug_info("proxy", "SOCKS4 connection in progress\n");
1475 connect_data->inpa = purple_input_add(connect_data->fd,
1476 PURPLE_INPUT_WRITE, s4_canwrite, connect_data);
1477 }
1478 else
1479 {
1480 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
1481 }
1482 }
1483 else
1484 {
1485 purple_debug_info("proxy", "Connected immediately.\n");
1486
1487 s4_canwrite(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1488 }
1489 }
1490
1491 static gboolean
1492 s5_ensure_buffer_length(PurpleProxyConnectData *connect_data, guint len)
1493 {
1494 if(connect_data->read_len < len) {
1495 if(connect_data->read_buf_len < len) {
1496 /* it's not just that we haven't read enough, it's that we haven't tried to read enough yet */
1497 purple_debug_info("s5", "reallocing from %" G_GSIZE_FORMAT
1498 " to %d\n", connect_data->read_buf_len, len);
1499 connect_data->read_buf_len = len;
1500 connect_data->read_buffer = g_realloc(connect_data->read_buffer, connect_data->read_buf_len);
1501 }
1502 return FALSE;
1503 }
1504
1505 return TRUE;
1506 }
1507
1508 static void
1509 s5_canread_again(gpointer data, gint source, PurpleInputCondition cond)
1510 {
1511 guchar *dest, *buf;
1512 PurpleProxyConnectData *connect_data = data;
1513 int len;
1514
1515 if (connect_data->read_buffer == NULL) {
1516 connect_data->read_buf_len = 5;
1517 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
1518 connect_data->read_len = 0;
1519 }
1520
1521 dest = connect_data->read_buffer + connect_data->read_len;
1522 buf = connect_data->read_buffer;
1523
1524 len = read(connect_data->fd, dest, (connect_data->read_buf_len - connect_data->read_len));
1525
1526 if (len == 0)
1527 {
1528 purple_proxy_connect_data_disconnect(connect_data,
1529 _("Server closed the connection"));
1530 return;
1531 }
1532
1533 if (len < 0)
1534 {
1535 if (errno == EAGAIN)
1536 /* No worries */
1537 return;
1538
1539 /* Error! */
1540 purple_proxy_connect_data_disconnect_formatted(connect_data,
1541 _("Lost connection with server: %s"), g_strerror(errno));
1542 return;
1543 }
1544
1545 connect_data->read_len += len;
1546
1547 if(connect_data->read_len < 4)
1548 return;
1549
1550 if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
1551 if ((buf[0] == 0x05) && (buf[1] < 0x09)) {
1552 purple_debug_error("socks5 proxy", "%s", socks5errors[buf[1]]);
1553 purple_proxy_connect_data_disconnect(connect_data,
1554 socks5errors[buf[1]]);
1555 } else {
1556 purple_debug_error("socks5 proxy", "Bad data.\n");
1557 purple_proxy_connect_data_disconnect(connect_data,
1558 _("Received invalid data on connection with server"));
1559 }
1560 return;
1561 }
1562
1563 /* Skip past BND.ADDR */
1564 switch(buf[3]) {
1565 case 0x01: /* the address is a version-4 IP address, with a length of 4 octets */
1566 if(!s5_ensure_buffer_length(connect_data, 4 + 4))
1567 return;
1568 buf += 4 + 4;
1569 break;
1570 case 0x03: /* the address field contains a fully-qualified domain name. The first
1571 octet of the address field contains the number of octets of name that
1572 follow, there is no terminating NUL octet. */
1573 if(!s5_ensure_buffer_length(connect_data, 4 + 1))
1574 return;
1575 buf += 4;
1576 if(!s5_ensure_buffer_length(connect_data, 4 + 1 + buf[0]))
1577 return;
1578 buf += buf[0] + 1;
1579 break;
1580 case 0x04: /* the address is a version-6 IP address, with a length of 16 octets */
1581 if(!s5_ensure_buffer_length(connect_data, 4 + 16))
1582 return;
1583 buf += 4 + 16;
1584 break;
1585 default:
1586 purple_debug_error("socks5 proxy", "Invalid ATYP received (0x%X)\n", buf[3]);
1587 purple_proxy_connect_data_disconnect(connect_data,
1588 _("Received invalid data on connection with server"));
1589 return;
1590 }
1591
1592 /* Skip past BND.PORT */
1593 if(!s5_ensure_buffer_length(connect_data, (buf - connect_data->read_buffer) + 2))
1594 return;
1595
1596 purple_proxy_connect_data_connected(connect_data);
1597 }
1598
1599 static void
1600 s5_sendconnect(gpointer data, int source)
1601 {
1602 PurpleProxyConnectData *connect_data = data;
1603 size_t hlen = strlen(connect_data->host);
1604 connect_data->write_buf_len = 5 + hlen + 2;
1605 connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
1606 connect_data->written_len = 0;
1607
1608 connect_data->write_buffer[0] = 0x05;
1609 connect_data->write_buffer[1] = 0x01; /* CONNECT */
1610 connect_data->write_buffer[2] = 0x00; /* reserved */
1611 connect_data->write_buffer[3] = 0x03; /* address type -- host name */
1612 connect_data->write_buffer[4] = hlen;
1613 memcpy(connect_data->write_buffer + 5, connect_data->host, hlen);
1614 connect_data->write_buffer[5 + hlen] = connect_data->port >> 8;
1615 connect_data->write_buffer[5 + hlen + 1] = connect_data->port & 0xff;
1616
1617 connect_data->read_cb = s5_canread_again;
1618
1619 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1620 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1621 }
1622
1623 static void
1624 s5_readauth(gpointer data, gint source, PurpleInputCondition cond)
1625 {
1626 PurpleProxyConnectData *connect_data = data;
1627 int len;
1628
1629 if (connect_data->read_buffer == NULL) {
1630 connect_data->read_buf_len = 2;
1631 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
1632 connect_data->read_len = 0;
1633 }
1634
1635 purple_debug_info("socks5 proxy", "Got auth response.\n");
1636
1637 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len,
1638 connect_data->read_buf_len - connect_data->read_len);
1639
1640 if (len == 0)
1641 {
1642 purple_proxy_connect_data_disconnect(connect_data,
1643 _("Server closed the connection"));
1644 return;
1645 }
1646
1647 if (len < 0)
1648 {
1649 if (errno == EAGAIN)
1650 /* No worries */
1651 return;
1652
1653 /* Error! */
1654 purple_proxy_connect_data_disconnect_formatted(connect_data,
1655 _("Lost connection with server: %s"), g_strerror(errno));
1656 return;
1657 }
1658
1659 connect_data->read_len += len;
1660 if (connect_data->read_len < 2)
1661 return;
1662
1663 purple_input_remove(connect_data->inpa);
1664 connect_data->inpa = 0;
1665
1666 if ((connect_data->read_buffer[0] != 0x01) || (connect_data->read_buffer[1] != 0x00)) {
1667 purple_proxy_connect_data_disconnect(connect_data,
1668 _("Received invalid data on connection with server"));
1669 return;
1670 }
1671
1672 g_free(connect_data->read_buffer);
1673 connect_data->read_buffer = NULL;
1674
1675 s5_sendconnect(connect_data, connect_data->fd);
1676 }
1677
1678 static void
1679 hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd, unsigned char * response)
1680 {
1681 PurpleHash *hash;
1682 int i;
1683 unsigned char Kxoripad[65];
1684 unsigned char Kxoropad[65];
1685 size_t pwlen;
1686
1687 hash = purple_md5_hash_new();
1688
1689 memset(Kxoripad,0,sizeof(Kxoripad));
1690 memset(Kxoropad,0,sizeof(Kxoropad));
1691
1692 pwlen=strlen(passwd);
1693 if (pwlen>64) {
1694 purple_hash_append(hash, (const guchar *)passwd, strlen(passwd));
1695 purple_hash_digest(hash, Kxoripad, sizeof(Kxoripad));
1696 pwlen=16;
1697 } else {
1698 memcpy(Kxoripad, passwd, pwlen);
1699 }
1700 memcpy(Kxoropad,Kxoripad,pwlen);
1701
1702 for (i=0;i<64;i++) {
1703 Kxoripad[i]^=0x36;
1704 Kxoropad[i]^=0x5c;
1705 }
1706
1707 purple_hash_reset(hash);
1708 purple_hash_append(hash, Kxoripad, 64);
1709 purple_hash_append(hash, challenge, challen);
1710 purple_hash_digest(hash, Kxoripad, sizeof(Kxoripad));
1711
1712 purple_hash_reset(hash);
1713 purple_hash_append(hash, Kxoropad, 64);
1714 purple_hash_append(hash, Kxoripad, 16);
1715 purple_hash_digest(hash, response, 16);
1716
1717 g_object_unref(hash);
1718 }
1719
1720 static void
1721 s5_readchap(gpointer data, gint source, PurpleInputCondition cond);
1722
1723 /*
1724 * Return how many bytes we processed
1725 * -1 means we've shouldn't keep reading from the buffer
1726 */
1727 static gssize
1728 s5_parse_chap_msg(PurpleProxyConnectData *connect_data)
1729 {
1730 guchar *buf, *cmdbuf = connect_data->read_buffer;
1731 int len, navas, currentav;
1732
1733 purple_debug_misc("socks5 proxy", "Reading CHAP message: %x\n", *cmdbuf);
1734
1735 if (*cmdbuf != 0x01) {
1736 purple_proxy_connect_data_disconnect(connect_data,
1737 _("Received invalid data on connection with server"));
1738 return -1;
1739 }
1740 cmdbuf++;
1741
1742 navas = *cmdbuf;
1743
1744 purple_debug_misc("socks5 proxy", "Expecting %d attribute(s).\n", navas);
1745 if (G_UNLIKELY(navas < 0 || navas > 10000)) { /* XXX: what's the threshold? */
1746 purple_proxy_connect_data_disconnect(connect_data,
1747 _("Received invalid data on connection with server"));
1748 return -1;
1749 }
1750
1751 cmdbuf++;
1752
1753 for (currentav = 0; currentav < navas; currentav++) {
1754
1755 len = connect_data->read_len - (cmdbuf - connect_data->read_buffer);
1756 /* We don't have enough data to even know how long the next attribute is,
1757 * or we don't have the full length of the next attribute. */
1758 if (len < 2 || len < (cmdbuf[1] + 2)) {
1759 /* Clear out the attributes that have been read - decrease the attribute count */
1760 connect_data->read_buffer[1] = navas - currentav;
1761 /* Move the unprocessed data into the first attribute position */
1762 memmove((connect_data->read_buffer + 2), cmdbuf, len);
1763 /* Decrease the read count accordingly */
1764 connect_data->read_len = len + 2;
1765
1766 purple_debug_info("socks5 proxy", "Need more data to retrieve attribute %d.\n", currentav);
1767
1768 return -1;
1769 }
1770
1771 buf = cmdbuf + 2;
1772
1773 if (cmdbuf[1] == 0) {
1774 purple_debug_error("socks5 proxy", "Attribute %x Value length of 0; ignoring.\n", cmdbuf[0]);
1775 cmdbuf = buf;
1776 continue;
1777 }
1778
1779 switch (cmdbuf[0]) {
1780 case 0x00:
1781 purple_debug_info("socks5 proxy", "Received STATUS of %x\n", buf[0]);
1782 /* Did auth work? */
1783 if (buf[0] == 0x00) {
1784 purple_input_remove(connect_data->inpa);
1785 connect_data->inpa = 0;
1786 g_free(connect_data->read_buffer);
1787 connect_data->read_buffer = NULL;
1788 /* Success */
1789 s5_sendconnect(connect_data, connect_data->fd);
1790 } else {
1791 /* Failure */
1792 purple_debug_warning("proxy",
1793 "socks5 CHAP authentication "
1794 "failed. Disconnecting...");
1795 purple_proxy_connect_data_disconnect(connect_data,
1796 _("Authentication failed"));
1797 }
1798 return -1;
1799 case 0x01:
1800 /* We've already validated that cmdbuf[1] is sane. */
1801 purple_debug_info("socks5 proxy", "Received TEXT-MESSAGE of '%.*s'\n", (int) cmdbuf[1], buf);
1802 break;
1803 case 0x03:
1804 purple_debug_info("socks5 proxy", "Received CHALLENGE\n");
1805 /* Server wants our credentials */
1806
1807 connect_data->write_buf_len = 16 + 4;
1808 connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
1809 connect_data->written_len = 0;
1810
1811 hmacmd5_chap(buf, cmdbuf[1],
1812 purple_proxy_info_get_password(connect_data->gpi),
1813 connect_data->write_buffer + 4);
1814 /* TODO: What about USER-IDENTITY? */
1815 connect_data->write_buffer[0] = 0x01;
1816 connect_data->write_buffer[1] = 0x01;
1817 connect_data->write_buffer[2] = 0x04;
1818 connect_data->write_buffer[3] = 0x10;
1819
1820 purple_input_remove(connect_data->inpa);
1821 g_free(connect_data->read_buffer);
1822 connect_data->read_buffer = NULL;
1823
1824 connect_data->read_cb = s5_readchap;
1825
1826 connect_data->inpa = purple_input_add(connect_data->fd,
1827 PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
1828
1829 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
1830 return -1;
1831 case 0x11:
1832 purple_debug_info("socks5 proxy", "Received ALGORIGTHMS of %x\n", buf[0]);
1833 /* Server wants to select an algorithm */
1834 if (buf[0] != 0x85) {
1835 /* Only currently support HMAC-MD5 */
1836 purple_debug_warning("proxy",
1837 "Server tried to select an "
1838 "algorithm that we did not advertise "
1839 "as supporting. This is a violation "
1840 "of the socks5 CHAP specification. "
1841 "Disconnecting...");
1842 purple_proxy_connect_data_disconnect(connect_data,
1843 _("Received invalid data on connection with server"));
1844 return -1;
1845 }
1846 break;
1847 default:
1848 purple_debug_info("socks5 proxy", "Received unused command %x, length=%d\n", cmdbuf[0], cmdbuf[1]);
1849 }
1850 cmdbuf = buf + cmdbuf[1];
1851 }
1852
1853 return (cmdbuf - connect_data->read_buffer);
1854 }
1855
1856 static void
1857 s5_readchap(gpointer data, gint source, PurpleInputCondition cond)
1858 {
1859 gssize msg_ret;
1860 PurpleProxyConnectData *connect_data = data;
1861 int len;
1862
1863 purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got CHAP response.\n");
1864
1865 if (connect_data->read_buffer == NULL) {
1866 /* A big enough butfer to read the message header (2 bytes) and at least one complete attribute and value (1 + 1 + 255). */
1867 connect_data->read_buf_len = 259;
1868 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
1869 connect_data->read_len = 0;
1870 }
1871
1872 if (connect_data->read_buf_len - connect_data->read_len == 0) {
1873 /*If the stuff below is right, this shouldn't be possible. */
1874 purple_debug_error("socks5 proxy", "This is about to suck because the read buffer is full (shouldn't happen).\n");
1875 }
1876
1877 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len,
1878 connect_data->read_buf_len - connect_data->read_len);
1879
1880 if (len == 0) {
1881 purple_proxy_connect_data_disconnect(connect_data,
1882 _("Server closed the connection"));
1883 return;
1884 }
1885
1886 if (len < 0) {
1887 if (errno == EAGAIN)
1888 /* No worries */
1889 return;
1890
1891 /* Error! */
1892 purple_proxy_connect_data_disconnect_formatted(connect_data,
1893 _("Lost connection with server: %s"), g_strerror(errno));
1894 return;
1895 }
1896
1897 connect_data->read_len += len;
1898
1899 /* We may have read more than one message into the buffer, we need to make sure to process them all */
1900 while (1) {
1901
1902 /* We need more to be able to read this message */
1903 if (connect_data->read_len < 2)
1904 return;
1905
1906 msg_ret = s5_parse_chap_msg(connect_data);
1907
1908 if (msg_ret < 0)
1909 return;
1910
1911 /* See if we have another message already in the buffer */
1912 if ((len = connect_data->read_len - msg_ret) > 0) {
1913
1914 /* Move on to the next message */
1915 memmove(connect_data->read_buffer, connect_data->read_buffer + msg_ret, len);
1916 /* Decrease the read count accordingly */
1917 connect_data->read_len = len;
1918
1919 /* Try to read the message that connect_data->read_buffer now points to */
1920 continue;
1921 }
1922
1923 break;
1924 }
1925
1926 /* Fell through. We ran out of CHAP events to process, but haven't
1927 * succeeded or failed authentication - there may be more to come.
1928 * If this is the case, come straight back here. */
1929
1930 purple_debug_info("socks5 proxy", "Waiting for another message from which to read CHAP info.\n");
1931
1932 /* We've processed all the available attributes, so get ready for a whole new message */
1933 g_free(connect_data->read_buffer);
1934 connect_data->read_buffer = NULL;
1935 }
1936
1937 static void
1938 s5_canread(gpointer data, gint source, PurpleInputCondition cond)
1939 {
1940 PurpleProxyConnectData *connect_data = data;
1941 int len;
1942
1943 if (connect_data->read_buffer == NULL) {
1944 connect_data->read_buf_len = 2;
1945 connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
1946 connect_data->read_len = 0;
1947 }
1948
1949 purple_debug_info("socks5 proxy", "Able to read.\n");
1950
1951 len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len,
1952 connect_data->read_buf_len - connect_data->read_len);
1953
1954 if (len == 0)
1955 {
1956 purple_proxy_connect_data_disconnect(connect_data,
1957 _("Server closed the connection"));
1958 return;
1959 }
1960
1961 if (len < 0)
1962 {
1963 if (errno == EAGAIN)
1964 /* No worries */
1965 return;
1966
1967 /* Error! */
1968 purple_proxy_connect_data_disconnect_formatted(connect_data,
1969 _("Lost connection with server: %s"), g_strerror(errno));
1970 return;
1971 }
1972
1973 connect_data->read_len += len;
1974 if (connect_data->read_len < 2)
1975 return;
1976
1977 purple_input_remove(connect_data->inpa);
1978 connect_data->inpa = 0;
1979
1980 if ((connect_data->read_buffer[0] != 0x05) || (connect_data->read_buffer[1] == 0xff)) {
1981 purple_proxy_connect_data_disconnect(connect_data,
1982 _("Received invalid data on connection with server"));
1983 return;
1984 }
1985
1986 if (connect_data->read_buffer[1] == 0x02) {
1987 size_t i, j;
1988 const char *u, *p;
1989
1990 u = purple_proxy_info_get_username(connect_data->gpi);
1991 p = purple_proxy_info_get_password(connect_data->gpi);
1992
1993 i = (u == NULL) ? 0 : strlen(u);
1994 j = (p == NULL) ? 0 : strlen(p);
1995
1996 connect_data->write_buf_len = 1 + 1 + i + 1 + j;
1997 connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
1998 connect_data->written_len = 0;
1999
2000 connect_data->write_buffer[0] = 0x01; /* version 1 */
2001 connect_data->write_buffer[1] = i;
2002 if (u != NULL)
2003 memcpy(connect_data->write_buffer + 2, u, i);
2004 connect_data->write_buffer[2 + i] = j;
2005 if (p != NULL)
2006 memcpy(connect_data->write_buffer + 2 + i + 1, p, j);
2007
2008 g_free(connect_data->read_buffer);
2009 connect_data->read_buffer = NULL;
2010
2011 connect_data->read_cb = s5_readauth;
2012
2013 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE,
2014 proxy_do_write, connect_data);
2015
2016 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
2017
2018 return;
2019 } else if (connect_data->read_buffer[1] == 0x03) {
2020 size_t userlen;
2021 userlen = strlen(purple_proxy_info_get_username(connect_data->gpi));
2022
2023 connect_data->write_buf_len = 7 + userlen;
2024 connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
2025 connect_data->written_len = 0;
2026
2027 connect_data->write_buffer[0] = 0x01;
2028 connect_data->write_buffer[1] = 0x02;
2029 connect_data->write_buffer[2] = 0x11;
2030 connect_data->write_buffer[3] = 0x01;
2031 connect_data->write_buffer[4] = 0x85;
2032 connect_data->write_buffer[5] = 0x02;
2033 connect_data->write_buffer[6] = userlen;
2034 memcpy(connect_data->write_buffer + 7,
2035 purple_proxy_info_get_username(connect_data->gpi), userlen);
2036
2037 g_free(connect_data->read_buffer);
2038 connect_data->read_buffer = NULL;
2039
2040 connect_data->read_cb = s5_readchap;
2041
2042 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE,
2043 proxy_do_write, connect_data);
2044
2045 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
2046
2047 return;
2048 } else {
2049 g_free(connect_data->read_buffer);
2050 connect_data->read_buffer = NULL;
2051
2052 s5_sendconnect(connect_data, connect_data->fd);
2053 }
2054 }
2055
2056 static void
2057 s5_canwrite(gpointer data, gint source, PurpleInputCondition cond)
2058 {
2059 unsigned char buf[5];
2060 int i;
2061 PurpleProxyConnectData *connect_data = data;
2062 int error = ETIMEDOUT;
2063 int ret;
2064
2065 purple_debug_info("socks5 proxy", "Connected.\n");
2066
2067 if (connect_data->inpa > 0)
2068 {
2069 purple_input_remove(connect_data->inpa);
2070 connect_data->inpa = 0;
2071 }
2072
2073 ret = purple_input_get_error(connect_data->fd, &error);
2074 if ((ret != 0) || (error != 0))
2075 {
2076 if (ret != 0)
2077 error = errno;
2078 purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
2079 return;
2080 }
2081
2082 buf[0] = 0x05; /* SOCKS version 5 */
2083
2084 if (purple_proxy_info_get_username(connect_data->gpi) != NULL) {
2085 buf[1] = 0x03; /* three methods */
2086 buf[2] = 0x00; /* no authentication */
2087 buf[3] = 0x03; /* CHAP authentication */
2088 buf[4] = 0x02; /* username/password authentication */
2089 i = 5;
2090 }
2091 else {
2092 buf[1] = 0x01;
2093 buf[2] = 0x00;
2094 i = 3;
2095 }
2096
2097 connect_data->write_buf_len = i;
2098 connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
2099 memcpy(connect_data->write_buffer, buf, i);
2100
2101 connect_data->read_cb = s5_canread;
2102
2103 connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
2104 proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
2105 }
2106
2107 static void
2108 proxy_connect_socks5(PurpleProxyConnectData *connect_data, common_sockaddr_t *addr, socklen_t addrlen)
2109 {
2110 purple_debug_info("proxy",
2111 "Connecting to %s:%d via %s:%d using SOCKS5\n",
2112 connect_data->host, connect_data->port,
2113 purple_proxy_info_get_host(connect_data->gpi),
2114 purple_proxy_info_get_port(connect_data->gpi));
2115
2116 connect_data->fd = socket(addr->sa.sa_family, SOCK_STREAM, 0);
2117 if (connect_data->fd < 0)
2118 {
2119 purple_proxy_connect_data_disconnect_formatted(connect_data,
2120 _("Unable to create socket: %s"), g_strerror(errno));
2121 return;
2122 }
2123 _purple_network_set_common_socket_flags(connect_data->fd);
2124
2125 if (connect(connect_data->fd, &addr->sa, addrlen) != 0)
2126 {
2127 if ((errno == EINPROGRESS) || (errno == EINTR))
2128 {
2129 purple_debug_info("proxy", "SOCKS5 connection in progress\n");
2130 connect_data->inpa = purple_input_add(connect_data->fd,
2131 PURPLE_INPUT_WRITE, s5_canwrite, connect_data);
2132 }
2133 else
2134 {
2135 purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
2136 }
2137 }
2138 else
2139 {
2140 purple_debug_info("proxy", "Connected immediately.\n");
2141
2142 s5_canwrite(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
2143 }
2144 }
2145
2146 /*
2147 * This function attempts to connect to the next IP address in the list
2148 * of IP addresses returned to us by purple_dnsquery_a() and attempts
2149 * to connect to each one. This is called after the hostname is
2150 * resolved, and each time a connection attempt fails (assuming there
2151 * is another IP address to try).
2152 */
2153 #ifndef INET6_ADDRSTRLEN
2154 #define INET6_ADDRSTRLEN 46
2155 #endif
2156
2157 static void try_connect(PurpleProxyConnectData *connect_data)
2158 {
2159 GInetAddress *address = NULL;
2160 GSocketAddress *socket_address = NULL;
2161 GError *error = NULL;
2162 char *ipaddr;
2163
2164 common_sockaddr_t addr;
2165 socklen_t addrlen = 0;
2166
2167 address = G_INET_ADDRESS(connect_data->hosts->data);
2168 ipaddr = g_inet_address_to_string(address);
2169
2170 purple_debug_info("proxy", "Attempting connection to %s\n", ipaddr);
2171 g_free(ipaddr);
2172
2173 socket_address = g_inet_socket_address_new(address, connect_data->port);
2174 addrlen = g_socket_address_get_native_size(socket_address);
2175
2176 g_socket_address_to_native(socket_address, &addr, addrlen, &error);
2177 if(error != NULL) {
2178 purple_debug_info("proxy", "failed connnection : %s\n", error->message);
2179
2180 g_error_free(error);
2181
2182 return;
2183 }
2184
2185 if (connect_data->socket_type == SOCK_DGRAM) {
2186 proxy_connect_udp_none(connect_data, &addr, addrlen);
2187
2188 return;
2189 }
2190
2191 switch (purple_proxy_info_get_proxy_type(connect_data->gpi)) {
2192 case PURPLE_PROXY_NONE:
2193 proxy_connect_none(connect_data, &addr, addrlen);
2194 break;
2195
2196 case PURPLE_PROXY_HTTP:
2197 proxy_connect_http(connect_data, &addr, addrlen);
2198 break;
2199
2200 case PURPLE_PROXY_SOCKS4:
2201 proxy_connect_socks4(connect_data, &addr, addrlen);
2202 break;
2203
2204 case PURPLE_PROXY_SOCKS5:
2205 case PURPLE_PROXY_TOR:
2206 proxy_connect_socks5(connect_data, &addr, addrlen);
2207 break;
2208
2209 case PURPLE_PROXY_USE_ENVVAR:
2210 proxy_connect_http(connect_data, &addr, addrlen);
2211 break;
2212
2213 default:
2214 break;
2215 }
2216
2217 g_object_unref(G_OBJECT(socket_address));
2218 }
2219
2220 static void
2221 connection_host_resolved(GObject *source, GAsyncResult *res, gpointer data) {
2222 PurpleProxyConnectData *connect_data = (PurpleProxyConnectData *)data;
2223 GError *error = NULL;
2224 GList *addresses = NULL;
2225
2226 addresses = g_resolver_lookup_by_name_finish(G_RESOLVER(source),
2227 res, &error);
2228
2229 if(G_IS_CANCELLABLE(connect_data->cancellable)) {
2230 g_object_unref(G_OBJECT(connect_data->cancellable));
2231
2232 connect_data->cancellable = NULL;
2233 }
2234
2235 if (error != NULL) {
2236 purple_proxy_connect_data_disconnect(connect_data, error->message);
2237
2238 g_error_free(error);
2239
2240 g_resolver_free_addresses(addresses);
2241
2242 return;
2243 }
2244
2245 if (addresses == NULL) {
2246 purple_proxy_connect_data_disconnect(connect_data, _("Unable to resolve hostname"));
2247
2248 return;
2249 }
2250
2251 connect_data->hosts = addresses;
2252
2253 try_connect(connect_data);
2254 } 664 }
2255 665
2256 PurpleProxyInfo * 666 PurpleProxyInfo *
2257 purple_proxy_get_setup(PurpleAccount *account) 667 purple_proxy_get_setup(PurpleAccount *account)
2258 { 668 {
2442 handles = g_slist_prepend(handles, connect_data); 852 handles = g_slist_prepend(handles, connect_data);
2443 853
2444 return connect_data; 854 return connect_data;
2445 } 855 }
2446 856
2447 PurpleProxyConnectData *
2448 purple_proxy_connect_udp(void *handle, PurpleAccount *account,
2449 const char *host, int port,
2450 PurpleProxyConnectFunction connect_cb, gpointer data)
2451 {
2452 const char *connecthost = host;
2453 int connectport = port;
2454 PurpleProxyConnectData *connect_data;
2455 GResolver *resolver;
2456
2457 g_return_val_if_fail(host != NULL, NULL);
2458 g_return_val_if_fail(port > 0, NULL);
2459 g_return_val_if_fail(connect_cb != NULL, NULL);
2460
2461 connect_data = g_new0(PurpleProxyConnectData, 1);
2462 connect_data->fd = -1;
2463 connect_data->socket_type = SOCK_DGRAM;
2464 connect_data->handle = handle;
2465 connect_data->connect_cb = connect_cb;
2466 connect_data->data = data;
2467 connect_data->host = g_strdup(host);
2468 connect_data->port = port;
2469 connect_data->gpi = purple_proxy_get_setup(account);
2470 connect_data->account = account;
2471
2472 if ((purple_proxy_info_get_proxy_type(connect_data->gpi) != PURPLE_PROXY_NONE) &&
2473 (purple_proxy_info_get_host(connect_data->gpi) == NULL ||
2474 purple_proxy_info_get_port(connect_data->gpi) <= 0)) {
2475
2476 purple_notify_error(NULL, NULL, _("Invalid proxy settings"),
2477 _("Either the host name or port number specified for "
2478 "your given proxy type is invalid."),
2479 purple_request_cpar_from_account(account));
2480 purple_proxy_connect_data_destroy(connect_data);
2481 return NULL;
2482 }
2483
2484 switch (purple_proxy_info_get_proxy_type(connect_data->gpi))
2485 {
2486 case PURPLE_PROXY_NONE:
2487 break;
2488
2489 case PURPLE_PROXY_HTTP:
2490 case PURPLE_PROXY_SOCKS4:
2491 case PURPLE_PROXY_SOCKS5:
2492 case PURPLE_PROXY_TOR:
2493 case PURPLE_PROXY_USE_ENVVAR:
2494 purple_debug_info("proxy", "Ignoring Proxy type (%d) for UDP.\n",
2495 purple_proxy_info_get_proxy_type(connect_data->gpi));
2496 break;
2497
2498 default:
2499 purple_debug_error("proxy", "Invalid Proxy type (%d) specified.\n",
2500 purple_proxy_info_get_proxy_type(connect_data->gpi));
2501 purple_proxy_connect_data_destroy(connect_data);
2502 return NULL;
2503 }
2504
2505 connect_data->cancellable = g_cancellable_new();
2506
2507 resolver = g_resolver_get_default();
2508 g_resolver_lookup_by_name_async(resolver,
2509 connecthost,
2510 connect_data->cancellable,
2511 connection_host_resolved,
2512 connect_data);
2513 g_object_unref(resolver);
2514
2515 if (connect_data->cancellable == NULL) {
2516 purple_proxy_connect_data_destroy(connect_data);
2517 return NULL;
2518 }
2519
2520 handles = g_slist_prepend(handles, connect_data);
2521
2522 return connect_data;
2523 }
2524
2525 static void 857 static void
2526 socks5_proxy_connect_cb(GObject *source, GAsyncResult *res, gpointer user_data) 858 socks5_proxy_connect_cb(GObject *source, GAsyncResult *res, gpointer user_data)
2527 { 859 {
2528 PurpleProxyConnectData *connect_data = user_data; 860 PurpleProxyConnectData *connect_data = user_data;
2529 GIOStream *stream; 861 GIOStream *stream;

mercurial