| 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 { |