| 14164:d85f44e1fec1 | 14165:0f09ac2220f4 |
|---|---|
| 42 GaimProxyConnectFunction connect_cb; | 42 GaimProxyConnectFunction connect_cb; |
| 43 GaimProxyErrorFunction error_cb; | 43 GaimProxyErrorFunction error_cb; |
| 44 gpointer data; | 44 gpointer data; |
| 45 char *host; | 45 char *host; |
| 46 int port; | 46 int port; |
| 47 int fd; | |
| 47 guint inpa; | 48 guint inpa; |
| 48 GaimProxyInfo *gpi; | 49 GaimProxyInfo *gpi; |
| 49 | 50 |
| 50 /** | 51 /** |
| 51 * This contains alternating length/char* values. The char* | 52 * This contains alternating length/char* values. The char* |
| 52 * values need to be freed when removed from the linked list. | 53 * values need to be freed when removed from the linked list. |
| 53 */ | 54 */ |
| 54 GSList *hosts; | 55 GSList *hosts; |
| 55 | 56 |
| 57 /* | |
| 58 * All of the following variables are used when establishing a | |
| 59 * connection through a proxy. | |
| 60 */ | |
| 56 guchar *write_buffer; | 61 guchar *write_buffer; |
| 57 gsize write_buf_len; | 62 gsize write_buf_len; |
| 58 gsize written_len; | 63 gsize written_len; |
| 59 GaimInputFunction read_cb; | 64 GaimInputFunction read_cb; |
| 60 guchar *read_buffer; | 65 guchar *read_buffer; |
| 73 "Command not supported\n", | 78 "Command not supported\n", |
| 74 "Address type not supported\n" | 79 "Address type not supported\n" |
| 75 }; | 80 }; |
| 76 | 81 |
| 77 static GaimProxyInfo *global_proxy_info = NULL; | 82 static GaimProxyInfo *global_proxy_info = NULL; |
| 83 | |
| 84 /* | |
| 85 * TODO: Once all callers of gaim_proxy_connect() are keeping track | |
| 86 * of the return value from that function this linked list | |
| 87 * will no longer be needed. | |
| 88 */ | |
| 78 static GSList *connect_infos = NULL; | 89 static GSList *connect_infos = NULL; |
| 79 | 90 |
| 80 static void try_connect(GaimProxyConnectInfo *); | 91 static void try_connect(GaimProxyConnectInfo *connect_info); |
| 81 | 92 |
| 82 /************************************************************************** | 93 /************************************************************************** |
| 83 * Proxy structure API | 94 * Proxy structure API |
| 84 **************************************************************************/ | 95 **************************************************************************/ |
| 85 GaimProxyInfo * | 96 GaimProxyInfo * |
| 260 } | 271 } |
| 261 /************************************************************************** | 272 /************************************************************************** |
| 262 * Proxy API | 273 * Proxy API |
| 263 **************************************************************************/ | 274 **************************************************************************/ |
| 264 | 275 |
| 276 /* | |
| 277 * This is used when the connection attempt to one particular IP | |
| 278 * address fails. We close the socket, remove the watcher and get | |
| 279 * rid of input and output buffers. Normally try_connect() will | |
| 280 * be called immediately after this. | |
| 281 */ | |
| 282 static void | |
| 283 gaim_proxy_connect_info_disconnect(GaimProxyConnectInfo *connect_info) | |
| 284 { | |
| 285 if (connect_info->inpa > 0) | |
| 286 { | |
| 287 gaim_input_remove(connect_info->inpa); | |
| 288 connect_info->inpa = 0; | |
| 289 } | |
| 290 | |
| 291 if (connect_info->fd >= 0) | |
| 292 { | |
| 293 close(connect_info->fd); | |
| 294 connect_info->fd = -1; | |
| 295 } | |
| 296 | |
| 297 g_free(connect_info->write_buffer); | |
| 298 connect_info->write_buffer = NULL; | |
| 299 | |
| 300 g_free(connect_info->read_buffer); | |
| 301 connect_info->read_buffer = NULL; | |
| 302 } | |
| 303 | |
| 265 static void | 304 static void |
| 266 gaim_proxy_connect_info_destroy(GaimProxyConnectInfo *connect_info) | 305 gaim_proxy_connect_info_destroy(GaimProxyConnectInfo *connect_info) |
| 267 { | 306 { |
| 307 gaim_proxy_connect_info_disconnect(connect_info); | |
| 308 | |
| 268 connect_infos = g_slist_remove(connect_infos, connect_info); | 309 connect_infos = g_slist_remove(connect_infos, connect_info); |
| 269 | |
| 270 if (connect_info->inpa > 0) | |
| 271 gaim_input_remove(connect_info->inpa); | |
| 272 | 310 |
| 273 while (connect_info->hosts != NULL) | 311 while (connect_info->hosts != NULL) |
| 274 { | 312 { |
| 275 /* Discard the length... */ | 313 /* Discard the length... */ |
| 276 connect_info->hosts = g_slist_remove(connect_info->hosts, connect_info->hosts->data); | 314 connect_info->hosts = g_slist_remove(connect_info->hosts, connect_info->hosts->data); |
| 278 g_free(connect_info->hosts->data); | 316 g_free(connect_info->hosts->data); |
| 279 connect_info->hosts = g_slist_remove(connect_info->hosts, connect_info->hosts->data); | 317 connect_info->hosts = g_slist_remove(connect_info->hosts, connect_info->hosts->data); |
| 280 } | 318 } |
| 281 | 319 |
| 282 g_free(connect_info->host); | 320 g_free(connect_info->host); |
| 283 g_free(connect_info->write_buffer); | |
| 284 g_free(connect_info->read_buffer); | |
| 285 g_free(connect_info); | 321 g_free(connect_info); |
| 286 } | 322 } |
| 287 | 323 |
| 288 static void | 324 static void |
| 289 gaim_proxy_connect_info_connected(GaimProxyConnectInfo *connect_info, int fd) | 325 gaim_proxy_connect_info_connected(GaimProxyConnectInfo *connect_info) |
| 290 { | 326 { |
| 291 connect_info->connect_cb(connect_info->data, fd); | 327 connect_info->connect_cb(connect_info->data, connect_info->fd); |
| 328 | |
| 329 /* | |
| 330 * We've passed the file descriptor to the protocol, so it's no longer | |
| 331 * our responsibility, and we should be careful not to free it when | |
| 332 * we destroy the connect_info. | |
| 333 */ | |
| 334 connect_info->fd = -1; | |
| 335 | |
| 292 gaim_proxy_connect_info_destroy(connect_info); | 336 gaim_proxy_connect_info_destroy(connect_info); |
| 293 } | 337 } |
| 294 | 338 |
| 295 /** | 339 /** |
| 296 * @param error An error message explaining why the connection | 340 * @param error An error message explaining why the connection |
| 1017 * | 1061 * |
| 1018 * (error == EINPROGRESS can happen after a select because the kernel can | 1062 * (error == EINPROGRESS can happen after a select because the kernel can |
| 1019 * be overly optimistic sometimes. select is just a hint that you might be | 1063 * be overly optimistic sometimes. select is just a hint that you might be |
| 1020 * able to do something.) | 1064 * able to do something.) |
| 1021 */ | 1065 */ |
| 1022 ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); | 1066 ret = getsockopt(connect_info->fd, SOL_SOCKET, SO_ERROR, &error, &len); |
| 1023 if (ret == 0 && error == EINPROGRESS) | 1067 if (ret == 0 && error == EINPROGRESS) |
| 1024 return; /* we'll be called again later */ | 1068 return; /* we'll be called again later */ |
| 1025 if (ret < 0 || error != 0) { | 1069 if (ret < 0 || error != 0) { |
| 1026 if (ret!=0) | 1070 if (ret!=0) |
| 1027 error = errno; | 1071 error = errno; |
| 1028 close(source); | |
| 1029 gaim_input_remove(connect_info->inpa); | |
| 1030 connect_info->inpa = 0; | |
| 1031 | 1072 |
| 1032 gaim_debug_error("proxy", | 1073 gaim_debug_error("proxy", |
| 1033 "getsockopt SO_ERROR check: %s\n", strerror(error)); | 1074 "getsockopt SO_ERROR check: %s\n", strerror(error)); |
| 1034 | 1075 |
| 1076 gaim_proxy_connect_info_disconnect(connect_info); | |
| 1035 try_connect(connect_info); | 1077 try_connect(connect_info); |
| 1036 return; | 1078 return; |
| 1037 } | 1079 } |
| 1038 | 1080 |
| 1039 gaim_input_remove(connect_info->inpa); | 1081 gaim_input_remove(connect_info->inpa); |
| 1040 connect_info->inpa = 0; | 1082 connect_info->inpa = 0; |
| 1041 | 1083 |
| 1042 gaim_proxy_connect_info_connected(connect_info, source); | 1084 gaim_proxy_connect_info_connected(connect_info); |
| 1043 } | 1085 } |
| 1044 | 1086 |
| 1045 static gboolean clean_connect(gpointer data) | 1087 static gboolean |
| 1046 { | 1088 clean_connect(gpointer data) |
| 1047 GaimProxyConnectInfo *connect_info = data; | 1089 { |
| 1048 | 1090 GaimProxyConnectInfo *connect_info; |
| 1049 gaim_proxy_connect_info_connected(connect_info, connect_info->port); | 1091 |
| 1092 connect_info = data; | |
| 1093 gaim_proxy_connect_info_connected(connect_info); | |
| 1050 | 1094 |
| 1051 return FALSE; | 1095 return FALSE; |
| 1052 } | 1096 } |
| 1053 | |
| 1054 | 1097 |
| 1055 static int | 1098 static int |
| 1056 proxy_connect_none(GaimProxyConnectInfo *connect_info, struct sockaddr *addr, socklen_t addrlen) | 1099 proxy_connect_none(GaimProxyConnectInfo *connect_info, struct sockaddr *addr, socklen_t addrlen) |
| 1057 { | 1100 { |
| 1058 int fd = -1; | 1101 gaim_debug_info("proxy", "Connecting to %s:%d with no proxy\n", |
| 1059 | 1102 connect_info->host, connect_info->port); |
| 1060 gaim_debug_info("proxy", | 1103 |
| 1061 "Connecting to %s:%d with no proxy\n", connect_info->host, connect_info->port); | 1104 connect_info->fd = socket(addr->sa_family, SOCK_STREAM, 0); |
| 1062 | 1105 if (connect_info->fd < 0) |
| 1063 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) { | 1106 { |
| 1064 gaim_debug_error("proxy", | 1107 gaim_debug_error("proxy", |
| 1065 "Unable to create socket: %s\n", strerror(errno)); | 1108 "Unable to create socket: %s\n", strerror(errno)); |
| 1066 return -1; | 1109 return -1; |
| 1067 } | 1110 } |
| 1068 fcntl(fd, F_SETFL, O_NONBLOCK); | 1111 fcntl(connect_info->fd, F_SETFL, O_NONBLOCK); |
| 1069 #ifndef _WIN32 | 1112 #ifndef _WIN32 |
| 1070 fcntl(fd, F_SETFD, FD_CLOEXEC); | 1113 fcntl(connect_info->fd, F_SETFD, FD_CLOEXEC); |
| 1071 #endif | 1114 #endif |
| 1072 | 1115 |
| 1073 if (connect(fd, (struct sockaddr *)addr, addrlen) != 0) | 1116 if (connect(connect_info->fd, (struct sockaddr *)addr, addrlen) != 0) |
| 1074 { | 1117 { |
| 1075 if ((errno == EINPROGRESS) || (errno == EINTR)) { | 1118 if ((errno == EINPROGRESS) || (errno == EINTR)) { |
| 1076 /* This just confuses people. */ | 1119 gaim_debug_info("proxy", "Connection in progress\n"); |
| 1077 /* gaim_debug_warning("proxy", | 1120 connect_info->inpa = gaim_input_add(connect_info->fd, GAIM_INPUT_WRITE, no_one_calls, connect_info); |
| 1078 "Connect would have blocked.\n"); */ | |
| 1079 connect_info->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, connect_info); | |
| 1080 } | 1121 } |
| 1081 else { | 1122 else { |
| 1082 gaim_debug_error("proxy", | 1123 gaim_debug_error("proxy", |
| 1083 "Connect failed: %s\n", strerror(errno)); | 1124 "Connect failed: %s\n", strerror(errno)); |
| 1084 close(fd); | 1125 close(connect_info->fd); |
| 1126 connect_info->fd = -1; | |
| 1085 return -1; | 1127 return -1; |
| 1086 } | 1128 } |
| 1087 } | 1129 } |
| 1088 else { | 1130 else |
| 1131 { | |
| 1132 /* | |
| 1133 * The connection happened IMMEDIATELY... strange, but whatever. | |
| 1134 */ | |
| 1089 socklen_t len; | 1135 socklen_t len; |
| 1090 int error = ETIMEDOUT; | 1136 int error = ETIMEDOUT; |
| 1091 gaim_debug_misc("proxy", "Connect didn't block.\n"); | 1137 gaim_debug_info("proxy", "Connected immediately.\n"); |
| 1092 len = sizeof(error); | 1138 len = sizeof(error); |
| 1093 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) | 1139 if (getsockopt(connect_info->fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) |
| 1094 { | 1140 { |
| 1095 gaim_debug_error("proxy", "getsockopt failed.\n"); | 1141 gaim_debug_error("proxy", "getsockopt failed.\n"); |
| 1096 close(fd); | 1142 close(connect_info->fd); |
| 1143 connect_info->fd = -1; | |
| 1097 return -1; | 1144 return -1; |
| 1098 } | 1145 } |
| 1099 /* TODO: Why is the following line so strange? */ | 1146 |
| 1100 connect_info->port = fd; /* bleh */ | 1147 /* |
| 1101 gaim_timeout_add(10, clean_connect, connect_info); /* we do this because we never | 1148 * We want to call the "connected" callback eventually, but we |
| 1102 want to call our callback | 1149 * don't want to call it before we return, just in case. |
| 1103 before we return. */ | 1150 */ |
| 1104 } | 1151 gaim_timeout_add(10, clean_connect, connect_info); |
| 1105 | 1152 } |
| 1106 return fd; | 1153 |
| 1154 return connect_info->fd; | |
| 1107 } | 1155 } |
| 1108 | 1156 |
| 1109 static void | 1157 static void |
| 1110 proxy_do_write(gpointer data, gint source, GaimInputCondition cond) | 1158 proxy_do_write(gpointer data, gint source, GaimInputCondition cond) |
| 1111 { | 1159 { |
| 1112 GaimProxyConnectInfo *connect_info = data; | 1160 GaimProxyConnectInfo *connect_info = data; |
| 1113 const guchar *request = connect_info->write_buffer + connect_info->written_len; | 1161 const guchar *request = connect_info->write_buffer + connect_info->written_len; |
| 1114 gsize request_len = connect_info->write_buf_len - connect_info->written_len; | 1162 gsize request_len = connect_info->write_buf_len - connect_info->written_len; |
| 1115 | 1163 |
| 1116 int ret = write(source, request, request_len); | 1164 int ret = write(connect_info->fd, request, request_len); |
| 1117 | 1165 |
| 1118 if(ret < 0 && errno == EAGAIN) | 1166 if(ret < 0 && errno == EAGAIN) |
| 1119 return; | 1167 return; |
| 1120 else if(ret < 0) { | 1168 else if(ret < 0) { |
| 1121 gaim_input_remove(connect_info->inpa); | 1169 gaim_proxy_connect_info_disconnect(connect_info); |
| 1122 connect_info->inpa = 0; | |
| 1123 close(source); | |
| 1124 g_free(connect_info->write_buffer); | |
| 1125 connect_info->write_buffer = NULL; | |
| 1126 try_connect(connect_info); | 1170 try_connect(connect_info); |
| 1127 return; | 1171 return; |
| 1128 } else if (ret < request_len) { | 1172 } else if (ret < request_len) { |
| 1129 connect_info->written_len += ret; | 1173 connect_info->written_len += ret; |
| 1130 return; | 1174 return; |
| 1133 gaim_input_remove(connect_info->inpa); | 1177 gaim_input_remove(connect_info->inpa); |
| 1134 g_free(connect_info->write_buffer); | 1178 g_free(connect_info->write_buffer); |
| 1135 connect_info->write_buffer = NULL; | 1179 connect_info->write_buffer = NULL; |
| 1136 | 1180 |
| 1137 /* register the response handler for the response */ | 1181 /* register the response handler for the response */ |
| 1138 connect_info->inpa = gaim_input_add(source, GAIM_INPUT_READ, connect_info->read_cb, connect_info); | 1182 connect_info->inpa = gaim_input_add(connect_info->fd, GAIM_INPUT_READ, connect_info->read_cb, connect_info); |
| 1139 } | 1183 } |
| 1140 | 1184 |
| 1141 #define HTTP_GOODSTRING "HTTP/1.0 200" | 1185 #define HTTP_GOODSTRING "HTTP/1.0 200" |
| 1142 #define HTTP_GOODSTRING2 "HTTP/1.1 200" | 1186 #define HTTP_GOODSTRING2 "HTTP/1.1 200" |
| 1143 | 1187 |
| 1159 } | 1203 } |
| 1160 | 1204 |
| 1161 p = connect_info->read_buffer + connect_info->read_len; | 1205 p = connect_info->read_buffer + connect_info->read_len; |
| 1162 max_read = connect_info->read_buf_len - connect_info->read_len - 1; | 1206 max_read = connect_info->read_buf_len - connect_info->read_len - 1; |
| 1163 | 1207 |
| 1164 len = read(source, p, max_read); | 1208 len = read(connect_info->fd, p, max_read); |
| 1165 if(len < 0 && errno == EAGAIN) | 1209 if(len < 0 && errno == EAGAIN) |
| 1166 return; | 1210 return; |
| 1167 else if(len <= 0) { | 1211 else if(len <= 0) { |
| 1168 close(source); | |
| 1169 gaim_proxy_connect_info_error(connect_info, _("Lost connection with server for an unknown reason.")); | 1212 gaim_proxy_connect_info_error(connect_info, _("Lost connection with server for an unknown reason.")); |
| 1170 return; | 1213 return; |
| 1171 } else { | 1214 } else { |
| 1172 connect_info->read_len += len; | 1215 connect_info->read_len += len; |
| 1173 } | 1216 } |
| 1220 len -= connect_info->read_len - headers_len; | 1263 len -= connect_info->read_len - headers_len; |
| 1221 /* I'm assuming that we're doing this to prevent the server from | 1264 /* I'm assuming that we're doing this to prevent the server from |
| 1222 complaining / breaking since we don't read the whole page */ | 1265 complaining / breaking since we don't read the whole page */ |
| 1223 while(len--) { | 1266 while(len--) { |
| 1224 /* TODO: deal with EAGAIN (and other errors) better */ | 1267 /* TODO: deal with EAGAIN (and other errors) better */ |
| 1225 if (read(source, &tmpc, 1) < 0 && errno != EAGAIN) | 1268 if (read(connect_info->fd, &tmpc, 1) < 0 && errno != EAGAIN) |
| 1226 break; | 1269 break; |
| 1227 } | 1270 } |
| 1228 } | 1271 } |
| 1229 | 1272 |
| 1230 if (error) | 1273 if (error) |
| 1231 { | 1274 { |
| 1232 close(source); | |
| 1233 msg = g_strdup_printf("Unable to parse response from HTTP proxy: %s\n", | 1275 msg = g_strdup_printf("Unable to parse response from HTTP proxy: %s\n", |
| 1234 connect_info->read_buffer); | 1276 connect_info->read_buffer); |
| 1235 gaim_proxy_connect_info_error(connect_info, msg); | 1277 gaim_proxy_connect_info_error(connect_info, msg); |
| 1236 g_free(msg); | 1278 g_free(msg); |
| 1237 return; | 1279 return; |
| 1253 gchar *request; | 1295 gchar *request; |
| 1254 gchar *response; | 1296 gchar *response; |
| 1255 username = strchr(domain, '\\'); | 1297 username = strchr(domain, '\\'); |
| 1256 if (username == NULL) | 1298 if (username == NULL) |
| 1257 { | 1299 { |
| 1258 close(source); | |
| 1259 msg = g_strdup_printf(_("HTTP proxy connection error %d"), status); | 1300 msg = g_strdup_printf(_("HTTP proxy connection error %d"), status); |
| 1260 gaim_proxy_connect_info_error(connect_info, msg); | 1301 gaim_proxy_connect_info_error(connect_info, msg); |
| 1261 g_free(msg); | 1302 g_free(msg); |
| 1262 return; | 1303 return; |
| 1263 } | 1304 } |
| 1290 connect_info->write_buf_len = strlen(request); | 1331 connect_info->write_buf_len = strlen(request); |
| 1291 connect_info->written_len = 0; | 1332 connect_info->written_len = 0; |
| 1292 | 1333 |
| 1293 connect_info->read_cb = http_canread; | 1334 connect_info->read_cb = http_canread; |
| 1294 | 1335 |
| 1295 connect_info->inpa = gaim_input_add(source, | 1336 connect_info->inpa = gaim_input_add(connect_info->fd, |
| 1296 GAIM_INPUT_WRITE, proxy_do_write, connect_info); | 1337 GAIM_INPUT_WRITE, proxy_do_write, connect_info); |
| 1297 | 1338 |
| 1298 proxy_do_write(connect_info, source, cond); | 1339 proxy_do_write(connect_info, connect_info->fd, cond); |
| 1299 return; | 1340 return; |
| 1300 } else if((ntlm = g_strrstr((const char *)connect_info->read_buffer, "Proxy-Authenticate: NTLM"))) { /* Empty message */ | 1341 } else if((ntlm = g_strrstr((const char *)connect_info->read_buffer, "Proxy-Authenticate: NTLM"))) { /* Empty message */ |
| 1301 gchar request[2048]; | 1342 gchar request[2048]; |
| 1302 gchar *domain = (gchar*) gaim_proxy_info_get_username(connect_info->gpi); | 1343 gchar *domain = (gchar*) gaim_proxy_info_get_username(connect_info->gpi); |
| 1303 gchar *username; | 1344 gchar *username; |
| 1304 int request_len; | 1345 int request_len; |
| 1305 username = strchr(domain, '\\'); | 1346 username = strchr(domain, '\\'); |
| 1306 if (username == NULL) | 1347 if (username == NULL) |
| 1307 { | 1348 { |
| 1308 close(source); | |
| 1309 msg = g_strdup_printf(_("HTTP proxy connection error %d"), status); | 1349 msg = g_strdup_printf(_("HTTP proxy connection error %d"), status); |
| 1310 gaim_proxy_connect_info_error(connect_info, msg); | 1350 gaim_proxy_connect_info_error(connect_info, msg); |
| 1311 g_free(msg); | 1351 g_free(msg); |
| 1312 return; | 1352 return; |
| 1313 } | 1353 } |
| 1337 connect_info->write_buf_len = request_len; | 1377 connect_info->write_buf_len = request_len; |
| 1338 connect_info->written_len = 0; | 1378 connect_info->written_len = 0; |
| 1339 | 1379 |
| 1340 connect_info->read_cb = http_canread; | 1380 connect_info->read_cb = http_canread; |
| 1341 | 1381 |
| 1342 connect_info->inpa = gaim_input_add(source, | 1382 connect_info->inpa = gaim_input_add(connect_info->fd, |
| 1343 GAIM_INPUT_WRITE, proxy_do_write, connect_info); | 1383 GAIM_INPUT_WRITE, proxy_do_write, connect_info); |
| 1344 | 1384 |
| 1345 proxy_do_write(connect_info, source, cond); | 1385 proxy_do_write(connect_info, connect_info->fd, cond); |
| 1346 return; | 1386 return; |
| 1347 } else { | 1387 } else { |
| 1348 close(source); | |
| 1349 msg = g_strdup_printf(_("HTTP proxy connection error %d"), status); | 1388 msg = g_strdup_printf(_("HTTP proxy connection error %d"), status); |
| 1350 gaim_proxy_connect_info_error(connect_info, msg); | 1389 gaim_proxy_connect_info_error(connect_info, msg); |
| 1351 g_free(msg); | 1390 g_free(msg); |
| 1352 return; | 1391 return; |
| 1353 } | 1392 } |
| 1365 gaim_input_remove(connect_info->inpa); | 1404 gaim_input_remove(connect_info->inpa); |
| 1366 connect_info->inpa = 0; | 1405 connect_info->inpa = 0; |
| 1367 g_free(connect_info->read_buffer); | 1406 g_free(connect_info->read_buffer); |
| 1368 connect_info->read_buffer = NULL; | 1407 connect_info->read_buffer = NULL; |
| 1369 gaim_debug_info("proxy", "HTTP proxy connection established\n"); | 1408 gaim_debug_info("proxy", "HTTP proxy connection established\n"); |
| 1370 gaim_proxy_connect_info_connected(connect_info, source); | 1409 gaim_proxy_connect_info_connected(connect_info); |
| 1371 return; | 1410 return; |
| 1372 } | 1411 } |
| 1373 } | 1412 } |
| 1374 | 1413 |
| 1375 | 1414 |
| 1391 connect_info->inpa = 0; | 1430 connect_info->inpa = 0; |
| 1392 } | 1431 } |
| 1393 | 1432 |
| 1394 len = sizeof(error); | 1433 len = sizeof(error); |
| 1395 | 1434 |
| 1396 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { | 1435 if (getsockopt(connect_info->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { |
| 1397 close(source); | 1436 gaim_proxy_connect_info_disconnect(connect_info); |
| 1398 | |
| 1399 try_connect(connect_info); | 1437 try_connect(connect_info); |
| 1400 return; | 1438 return; |
| 1401 } | 1439 } |
| 1402 | 1440 |
| 1403 gaim_debug_info("proxy", "using CONNECT tunnelling for %s:%d\n", | 1441 gaim_debug_info("proxy", "using CONNECT tunnelling for %s:%d\n", |
| 1433 connect_info->write_buf_len = request_len; | 1471 connect_info->write_buf_len = request_len; |
| 1434 connect_info->written_len = 0; | 1472 connect_info->written_len = 0; |
| 1435 | 1473 |
| 1436 connect_info->read_cb = http_canread; | 1474 connect_info->read_cb = http_canread; |
| 1437 | 1475 |
| 1438 connect_info->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, | 1476 connect_info->inpa = gaim_input_add(connect_info->fd, GAIM_INPUT_WRITE, proxy_do_write, |
| 1439 connect_info); | 1477 connect_info); |
| 1440 | 1478 |
| 1441 proxy_do_write(connect_info, source, cond); | 1479 proxy_do_write(connect_info, connect_info->fd, cond); |
| 1442 } | 1480 } |
| 1443 | 1481 |
| 1444 static int | 1482 static int |
| 1445 proxy_connect_http(GaimProxyConnectInfo *connect_info, struct sockaddr *addr, socklen_t addrlen) | 1483 proxy_connect_http(GaimProxyConnectInfo *connect_info, struct sockaddr *addr, socklen_t addrlen) |
| 1446 { | 1484 { |
| 1447 int fd = -1; | |
| 1448 | |
| 1449 gaim_debug_info("http proxy", | 1485 gaim_debug_info("http proxy", |
| 1450 "Connecting to %s:%d via %s:%d using HTTP\n", | 1486 "Connecting to %s:%d via %s:%d using HTTP\n", |
| 1451 (connect_info->host ? connect_info->host : "(null)"), connect_info->port, | 1487 (connect_info->host ? connect_info->host : "(null)"), connect_info->port, |
| 1452 (gaim_proxy_info_get_host(connect_info->gpi) ? gaim_proxy_info_get_host(connect_info->gpi) : "(null)"), | 1488 (gaim_proxy_info_get_host(connect_info->gpi) ? gaim_proxy_info_get_host(connect_info->gpi) : "(null)"), |
| 1453 gaim_proxy_info_get_port(connect_info->gpi)); | 1489 gaim_proxy_info_get_port(connect_info->gpi)); |
| 1454 | 1490 |
| 1455 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) { | 1491 connect_info->fd = socket(addr->sa_family, SOCK_STREAM, 0); |
| 1492 if (connect_info->fd < 0) | |
| 1456 return -1; | 1493 return -1; |
| 1457 } | 1494 |
| 1458 | 1495 fcntl(connect_info->fd, F_SETFL, O_NONBLOCK); |
| 1459 fcntl(fd, F_SETFL, O_NONBLOCK); | |
| 1460 #ifndef _WIN32 | 1496 #ifndef _WIN32 |
| 1461 fcntl(fd, F_SETFD, FD_CLOEXEC); | 1497 fcntl(connect_info->fd, F_SETFD, FD_CLOEXEC); |
| 1462 #endif | 1498 #endif |
| 1463 | 1499 |
| 1464 if (connect(fd, addr, addrlen) != 0) | 1500 if (connect(connect_info->fd, addr, addrlen) != 0) |
| 1465 { | 1501 { |
| 1466 if ((errno == EINPROGRESS) || (errno == EINTR)) { | 1502 if ((errno == EINPROGRESS) || (errno == EINTR)) { |
| 1467 gaim_debug_warning("http proxy", | 1503 gaim_debug_info("http proxy", "Connection in progress\n"); |
| 1468 "Connect would have blocked.\n"); | |
| 1469 | 1504 |
| 1470 if (connect_info->port != 80) { | 1505 if (connect_info->port != 80) { |
| 1471 /* we need to do CONNECT first */ | 1506 /* we need to do CONNECT first */ |
| 1472 connect_info->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, | 1507 connect_info->inpa = gaim_input_add(connect_info->fd, GAIM_INPUT_WRITE, |
| 1473 http_canwrite, connect_info); | 1508 http_canwrite, connect_info); |
| 1474 } else { | 1509 } else { |
| 1475 gaim_debug_info("proxy", "HTTP proxy connection established\n"); | 1510 gaim_debug_info("proxy", "HTTP proxy connection established\n"); |
| 1476 gaim_proxy_connect_info_connected(connect_info, fd); | 1511 gaim_proxy_connect_info_connected(connect_info); |
| 1477 } | 1512 } |
| 1478 } else { | 1513 } else { |
| 1479 close(fd); | 1514 close(connect_info->fd); |
| 1515 connect_info->fd = -1; | |
| 1480 return -1; | 1516 return -1; |
| 1481 } | 1517 } |
| 1482 } | 1518 } |
| 1483 else { | 1519 else { |
| 1484 socklen_t len; | 1520 socklen_t len; |
| 1485 int error = ETIMEDOUT; | 1521 int error = ETIMEDOUT; |
| 1486 | 1522 |
| 1487 gaim_debug_misc("http proxy", | 1523 gaim_debug_info("http proxy", "Connected immediately.\n"); |
| 1488 "Connect didn't block.\n"); | |
| 1489 | 1524 |
| 1490 len = sizeof(error); | 1525 len = sizeof(error); |
| 1491 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) | 1526 if (getsockopt(connect_info->fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) |
| 1492 { | 1527 { |
| 1493 close(fd); | 1528 close(connect_info->fd); |
| 1529 connect_info->fd = -1; | |
| 1494 return -1; | 1530 return -1; |
| 1495 } | 1531 } |
| 1496 http_canwrite(connect_info, fd, GAIM_INPUT_WRITE); | 1532 http_canwrite(connect_info, connect_info->fd, GAIM_INPUT_WRITE); |
| 1497 } | 1533 } |
| 1498 | 1534 |
| 1499 return fd; | 1535 return connect_info->fd; |
| 1500 } | 1536 } |
| 1501 | 1537 |
| 1502 | 1538 |
| 1503 static void | 1539 static void |
| 1504 s4_canread(gpointer data, gint source, GaimInputCondition cond) | 1540 s4_canread(gpointer data, gint source, GaimInputCondition cond) |
| 1517 } | 1553 } |
| 1518 | 1554 |
| 1519 buf = connect_info->read_buffer + connect_info->read_len; | 1555 buf = connect_info->read_buffer + connect_info->read_len; |
| 1520 max_read = connect_info->read_buf_len - connect_info->read_len; | 1556 max_read = connect_info->read_buf_len - connect_info->read_len; |
| 1521 | 1557 |
| 1522 len = read(source, buf, max_read); | 1558 len = read(connect_info->fd, buf, max_read); |
| 1523 | 1559 |
| 1524 if ((len < 0 && errno == EAGAIN) || (len > 0 && len + connect_info->read_len < 4)) | 1560 if ((len < 0 && errno == EAGAIN) || (len > 0 && len + connect_info->read_len < 4)) |
| 1525 return; | 1561 return; |
| 1526 else if (len + connect_info->read_len >= 4) { | 1562 else if (len + connect_info->read_len >= 4) { |
| 1527 if (connect_info->read_buffer[1] == 90) { | 1563 if (connect_info->read_buffer[1] == 90) { |
| 1528 gaim_proxy_connect_info_connected(connect_info, source); | 1564 gaim_proxy_connect_info_connected(connect_info); |
| 1529 return; | 1565 return; |
| 1530 } | 1566 } |
| 1531 } | 1567 } |
| 1532 | 1568 |
| 1533 gaim_input_remove(connect_info->inpa); | 1569 gaim_proxy_connect_info_disconnect(connect_info); |
| 1534 connect_info->inpa = 0; | |
| 1535 g_free(connect_info->read_buffer); | |
| 1536 connect_info->read_buffer = NULL; | |
| 1537 | |
| 1538 close(source); | |
| 1539 | |
| 1540 try_connect(connect_info); | 1570 try_connect(connect_info); |
| 1541 } | 1571 } |
| 1542 | 1572 |
| 1543 static void | 1573 static void |
| 1544 s4_canwrite(gpointer data, gint source, GaimInputCondition cond) | 1574 s4_canwrite(gpointer data, gint source, GaimInputCondition cond) |
| 1557 connect_info->inpa = 0; | 1587 connect_info->inpa = 0; |
| 1558 } | 1588 } |
| 1559 | 1589 |
| 1560 len = sizeof(error); | 1590 len = sizeof(error); |
| 1561 | 1591 |
| 1562 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { | 1592 if (getsockopt(connect_info->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { |
| 1563 close(source); | 1593 gaim_proxy_connect_info_disconnect(connect_info); |
| 1564 | |
| 1565 try_connect(connect_info); | 1594 try_connect(connect_info); |
| 1566 return; | 1595 return; |
| 1567 } | 1596 } |
| 1568 | 1597 |
| 1569 /* | 1598 /* |
| 1572 * extensions to the protocol. Since we don't know if a | 1601 * extensions to the protocol. Since we don't know if a |
| 1573 * server supports this, it would need to be implemented | 1602 * server supports this, it would need to be implemented |
| 1574 * with an option, or some detection mechanism - in the | 1603 * with an option, or some detection mechanism - in the |
| 1575 * meantime, stick with plain old SOCKS4. | 1604 * meantime, stick with plain old SOCKS4. |
| 1576 */ | 1605 */ |
| 1577 if (!(hp = gethostbyname(connect_info->host))) { | 1606 /* TODO: This needs to be non-blocking! */ |
| 1578 close(source); | 1607 hp = gethostbyname(connect_info->host); |
| 1579 | 1608 if (hp == NULL) { |
| 1609 gaim_proxy_connect_info_disconnect(connect_info); | |
| 1580 try_connect(connect_info); | 1610 try_connect(connect_info); |
| 1581 return; | 1611 return; |
| 1582 } | 1612 } |
| 1583 | 1613 |
| 1584 packet[0] = 4; | 1614 packet[0] = 4; |
| 1594 connect_info->write_buffer = g_memdup(packet, sizeof(packet)); | 1624 connect_info->write_buffer = g_memdup(packet, sizeof(packet)); |
| 1595 connect_info->write_buf_len = sizeof(packet); | 1625 connect_info->write_buf_len = sizeof(packet); |
| 1596 connect_info->written_len = 0; | 1626 connect_info->written_len = 0; |
| 1597 connect_info->read_cb = s4_canread; | 1627 connect_info->read_cb = s4_canread; |
| 1598 | 1628 |
| 1599 connect_info->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, connect_info); | 1629 connect_info->inpa = gaim_input_add(connect_info->fd, GAIM_INPUT_WRITE, proxy_do_write, connect_info); |
| 1600 | 1630 |
| 1601 proxy_do_write(connect_info, source, cond); | 1631 proxy_do_write(connect_info, connect_info->fd, cond); |
| 1602 } | 1632 } |
| 1603 | 1633 |
| 1604 static int | 1634 static int |
| 1605 proxy_connect_socks4(GaimProxyConnectInfo *connect_info, struct sockaddr *addr, socklen_t addrlen) | 1635 proxy_connect_socks4(GaimProxyConnectInfo *connect_info, struct sockaddr *addr, socklen_t addrlen) |
| 1606 { | 1636 { |
| 1607 int fd = -1; | |
| 1608 | |
| 1609 gaim_debug_info("socks4 proxy", | 1637 gaim_debug_info("socks4 proxy", |
| 1610 "Connecting to %s:%d via %s:%d using SOCKS4\n", | 1638 "Connecting to %s:%d via %s:%d using SOCKS4\n", |
| 1611 connect_info->host, connect_info->port, | 1639 connect_info->host, connect_info->port, |
| 1612 gaim_proxy_info_get_host(connect_info->gpi), | 1640 gaim_proxy_info_get_host(connect_info->gpi), |
| 1613 gaim_proxy_info_get_port(connect_info->gpi)); | 1641 gaim_proxy_info_get_port(connect_info->gpi)); |
| 1614 | 1642 |
| 1615 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) | 1643 connect_info->fd = socket(addr->sa_family, SOCK_STREAM, 0); |
| 1644 if (connect_info->fd < 0) | |
| 1616 return -1; | 1645 return -1; |
| 1617 | 1646 |
| 1618 fcntl(fd, F_SETFL, O_NONBLOCK); | 1647 fcntl(connect_info->fd, F_SETFL, O_NONBLOCK); |
| 1619 #ifndef _WIN32 | 1648 #ifndef _WIN32 |
| 1620 fcntl(fd, F_SETFD, FD_CLOEXEC); | 1649 fcntl(connect_info->fd, F_SETFD, FD_CLOEXEC); |
| 1621 #endif | 1650 #endif |
| 1622 | 1651 |
| 1623 if (connect(fd, addr, addrlen) != 0) | 1652 if (connect(connect_info->fd, addr, addrlen) != 0) |
| 1624 { | 1653 { |
| 1625 if ((errno == EINPROGRESS) || (errno == EINTR)) { | 1654 if ((errno == EINPROGRESS) || (errno == EINTR)) { |
| 1626 gaim_debug_warning("socks4 proxy", | 1655 gaim_debug_info("socks4 proxy", "Connection in progress.\n"); |
| 1627 "Connect would have blocked.\n"); | 1656 connect_info->inpa = gaim_input_add(connect_info->fd, GAIM_INPUT_WRITE, s4_canwrite, connect_info); |
| 1628 connect_info->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, s4_canwrite, connect_info); | |
| 1629 } | 1657 } |
| 1630 else { | 1658 else { |
| 1631 close(fd); | 1659 close(connect_info->fd); |
| 1660 connect_info->fd = -1; | |
| 1632 return -1; | 1661 return -1; |
| 1633 } | 1662 } |
| 1634 } else { | 1663 } else { |
| 1635 socklen_t len; | 1664 socklen_t len; |
| 1636 int error = ETIMEDOUT; | 1665 int error = ETIMEDOUT; |
| 1637 | 1666 |
| 1638 gaim_debug_misc("socks4 proxy", | 1667 gaim_debug_info("socks4 proxy", "Connected immediately.\n"); |
| 1639 "Connect didn't block.\n"); | |
| 1640 | 1668 |
| 1641 len = sizeof(error); | 1669 len = sizeof(error); |
| 1642 | 1670 |
| 1643 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) | 1671 if (getsockopt(connect_info->fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) |
| 1644 { | 1672 { |
| 1645 close(fd); | 1673 close(connect_info->fd); |
| 1674 connect_info->fd = -1; | |
| 1646 return -1; | 1675 return -1; |
| 1647 } | 1676 } |
| 1648 | 1677 |
| 1649 s4_canwrite(connect_info, fd, GAIM_INPUT_WRITE); | 1678 s4_canwrite(connect_info, connect_info->fd, GAIM_INPUT_WRITE); |
| 1650 } | 1679 } |
| 1651 | 1680 |
| 1652 return fd; | 1681 return connect_info->fd; |
| 1653 } | 1682 } |
| 1654 | 1683 |
| 1655 static void | 1684 static void |
| 1656 s5_canread_again(gpointer data, gint source, GaimInputCondition cond) | 1685 s5_canread_again(gpointer data, gint source, GaimInputCondition cond) |
| 1657 { | 1686 { |
| 1668 dest = connect_info->read_buffer + connect_info->read_len; | 1697 dest = connect_info->read_buffer + connect_info->read_len; |
| 1669 buf = connect_info->read_buffer; | 1698 buf = connect_info->read_buffer; |
| 1670 | 1699 |
| 1671 gaim_debug_info("socks5 proxy", "Able to read again.\n"); | 1700 gaim_debug_info("socks5 proxy", "Able to read again.\n"); |
| 1672 | 1701 |
| 1673 len = read(source, dest, (connect_info->read_buf_len - connect_info->read_len)); | 1702 len = read(connect_info->fd, dest, (connect_info->read_buf_len - connect_info->read_len)); |
| 1674 if(len < 0 && errno == EAGAIN) | 1703 if(len < 0 && errno == EAGAIN) |
| 1675 return; | 1704 return; |
| 1676 else if(len <= 0) { | 1705 else if(len <= 0) { |
| 1677 gaim_debug_warning("socks5 proxy", "or not...\n"); | 1706 gaim_debug_warning("socks5 proxy", "or not...\n"); |
| 1678 close(source); | 1707 gaim_proxy_connect_info_disconnect(connect_info); |
| 1679 gaim_input_remove(connect_info->inpa); | |
| 1680 connect_info->inpa = 0; | |
| 1681 g_free(connect_info->read_buffer); | |
| 1682 connect_info->read_buffer = NULL; | |
| 1683 try_connect(connect_info); | 1708 try_connect(connect_info); |
| 1684 return; | 1709 return; |
| 1685 } | 1710 } |
| 1686 connect_info->read_len += len; | 1711 connect_info->read_len += len; |
| 1687 | 1712 |
| 1691 if ((buf[0] != 0x05) || (buf[1] != 0x00)) { | 1716 if ((buf[0] != 0x05) || (buf[1] != 0x00)) { |
| 1692 if ((buf[0] == 0x05) && (buf[1] < 0x09)) | 1717 if ((buf[0] == 0x05) && (buf[1] < 0x09)) |
| 1693 gaim_debug_error("socks5 proxy", socks5errors[buf[1]]); | 1718 gaim_debug_error("socks5 proxy", socks5errors[buf[1]]); |
| 1694 else | 1719 else |
| 1695 gaim_debug_error("socks5 proxy", "Bad data.\n"); | 1720 gaim_debug_error("socks5 proxy", "Bad data.\n"); |
| 1696 close(source); | 1721 gaim_proxy_connect_info_disconnect(connect_info); |
| 1697 gaim_input_remove(connect_info->inpa); | |
| 1698 connect_info->inpa = 0; | |
| 1699 g_free(connect_info->read_buffer); | |
| 1700 connect_info->read_buffer = NULL; | |
| 1701 try_connect(connect_info); | 1722 try_connect(connect_info); |
| 1702 return; | 1723 return; |
| 1703 } | 1724 } |
| 1704 | 1725 |
| 1705 /* Skip past BND.ADDR */ | 1726 /* Skip past BND.ADDR */ |
| 1730 return; | 1751 return; |
| 1731 | 1752 |
| 1732 /* Skip past BND.PORT */ | 1753 /* Skip past BND.PORT */ |
| 1733 buf += 2; | 1754 buf += 2; |
| 1734 | 1755 |
| 1735 gaim_proxy_connect_info_connected(connect_info, source); | 1756 gaim_proxy_connect_info_connected(connect_info); |
| 1736 } | 1757 } |
| 1737 | 1758 |
| 1738 static void | 1759 static void |
| 1739 s5_sendconnect(gpointer data, int source) | 1760 s5_sendconnect(gpointer data, int source) |
| 1740 { | 1761 { |
| 1753 connect_info->write_buffer[5 + hlen] = connect_info->port >> 8; | 1774 connect_info->write_buffer[5 + hlen] = connect_info->port >> 8; |
| 1754 connect_info->write_buffer[5 + hlen + 1] = connect_info->port & 0xff; | 1775 connect_info->write_buffer[5 + hlen + 1] = connect_info->port & 0xff; |
| 1755 | 1776 |
| 1756 connect_info->read_cb = s5_canread_again; | 1777 connect_info->read_cb = s5_canread_again; |
| 1757 | 1778 |
| 1758 connect_info->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, connect_info); | 1779 connect_info->inpa = gaim_input_add(connect_info->fd, GAIM_INPUT_WRITE, proxy_do_write, connect_info); |
| 1759 proxy_do_write(connect_info, source, GAIM_INPUT_WRITE); | 1780 proxy_do_write(connect_info, connect_info->fd, GAIM_INPUT_WRITE); |
| 1760 } | 1781 } |
| 1761 | 1782 |
| 1762 static void | 1783 static void |
| 1763 s5_readauth(gpointer data, gint source, GaimInputCondition cond) | 1784 s5_readauth(gpointer data, gint source, GaimInputCondition cond) |
| 1764 { | 1785 { |
| 1771 connect_info->read_len = 0; | 1792 connect_info->read_len = 0; |
| 1772 } | 1793 } |
| 1773 | 1794 |
| 1774 gaim_debug_info("socks5 proxy", "Got auth response.\n"); | 1795 gaim_debug_info("socks5 proxy", "Got auth response.\n"); |
| 1775 | 1796 |
| 1776 len = read(source, connect_info->read_buffer + connect_info->read_len, | 1797 len = read(connect_info->fd, connect_info->read_buffer + connect_info->read_len, |
| 1777 connect_info->read_buf_len - connect_info->read_len); | 1798 connect_info->read_buf_len - connect_info->read_len); |
| 1778 if(len < 0 && errno == EAGAIN) | 1799 if(len < 0 && errno == EAGAIN) |
| 1779 return; | 1800 return; |
| 1780 else if(len <= 0) { | 1801 else if(len <= 0) { |
| 1781 close(source); | 1802 gaim_proxy_connect_info_disconnect(connect_info); |
| 1782 gaim_input_remove(connect_info->inpa); | |
| 1783 connect_info->inpa = 0; | |
| 1784 g_free(connect_info->read_buffer); | |
| 1785 connect_info->read_buffer = NULL; | |
| 1786 try_connect(connect_info); | 1803 try_connect(connect_info); |
| 1787 return; | 1804 return; |
| 1788 } | 1805 } |
| 1789 connect_info->read_len += len; | 1806 connect_info->read_len += len; |
| 1790 | 1807 |
| 1793 | 1810 |
| 1794 gaim_input_remove(connect_info->inpa); | 1811 gaim_input_remove(connect_info->inpa); |
| 1795 connect_info->inpa = 0; | 1812 connect_info->inpa = 0; |
| 1796 | 1813 |
| 1797 if ((connect_info->read_buffer[0] != 0x01) || (connect_info->read_buffer[1] != 0x00)) { | 1814 if ((connect_info->read_buffer[0] != 0x01) || (connect_info->read_buffer[1] != 0x00)) { |
| 1798 close(source); | 1815 gaim_proxy_connect_info_disconnect(connect_info); |
| 1799 g_free(connect_info->read_buffer); | |
| 1800 connect_info->read_buffer = NULL; | |
| 1801 try_connect(connect_info); | 1816 try_connect(connect_info); |
| 1802 return; | 1817 return; |
| 1803 } | 1818 } |
| 1804 | 1819 |
| 1805 g_free(connect_info->read_buffer); | 1820 g_free(connect_info->read_buffer); |
| 1806 connect_info->read_buffer = NULL; | 1821 connect_info->read_buffer = NULL; |
| 1807 | 1822 |
| 1808 s5_sendconnect(connect_info, source); | 1823 s5_sendconnect(connect_info, connect_info->fd); |
| 1809 } | 1824 } |
| 1810 | 1825 |
| 1811 static void | 1826 static void |
| 1812 hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd, unsigned char * response) | 1827 hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd, unsigned char * response) |
| 1813 { | 1828 { |
| 1865 connect_info->read_buf_len = 20; | 1880 connect_info->read_buf_len = 20; |
| 1866 connect_info->read_buffer = g_malloc(connect_info->read_buf_len); | 1881 connect_info->read_buffer = g_malloc(connect_info->read_buf_len); |
| 1867 connect_info->read_len = 0; | 1882 connect_info->read_len = 0; |
| 1868 } | 1883 } |
| 1869 | 1884 |
| 1870 len = read(source, connect_info->read_buffer + connect_info->read_len, | 1885 len = read(connect_info->fd, connect_info->read_buffer + connect_info->read_len, |
| 1871 connect_info->read_buf_len - connect_info->read_len); | 1886 connect_info->read_buf_len - connect_info->read_len); |
| 1872 | 1887 |
| 1873 if(len < 0 && errno == EAGAIN) | 1888 if(len < 0 && errno == EAGAIN) |
| 1874 return; | 1889 return; |
| 1875 else if(len <= 0) { | 1890 else if(len <= 0) { |
| 1876 close(source); | 1891 gaim_proxy_connect_info_disconnect(connect_info); |
| 1877 gaim_input_remove(connect_info->inpa); | |
| 1878 connect_info->inpa = 0; | |
| 1879 g_free(connect_info->read_buffer); | |
| 1880 connect_info->read_buffer = NULL; | |
| 1881 try_connect(connect_info); | 1892 try_connect(connect_info); |
| 1882 return; | 1893 return; |
| 1883 } | 1894 } |
| 1884 connect_info->read_len += len; | 1895 connect_info->read_len += len; |
| 1885 | 1896 |
| 1887 return; | 1898 return; |
| 1888 | 1899 |
| 1889 cmdbuf = connect_info->read_buffer; | 1900 cmdbuf = connect_info->read_buffer; |
| 1890 | 1901 |
| 1891 if (*cmdbuf != 0x01) { | 1902 if (*cmdbuf != 0x01) { |
| 1892 close(source); | 1903 gaim_proxy_connect_info_disconnect(connect_info); |
| 1893 gaim_input_remove(connect_info->inpa); | |
| 1894 connect_info->inpa = 0; | |
| 1895 g_free(connect_info->read_buffer); | |
| 1896 connect_info->read_buffer = NULL; | |
| 1897 try_connect(connect_info); | 1904 try_connect(connect_info); |
| 1898 return; | 1905 return; |
| 1899 } | 1906 } |
| 1900 cmdbuf++; | 1907 cmdbuf++; |
| 1901 | 1908 |
| 1915 gaim_input_remove(connect_info->inpa); | 1922 gaim_input_remove(connect_info->inpa); |
| 1916 connect_info->inpa = 0; | 1923 connect_info->inpa = 0; |
| 1917 g_free(connect_info->read_buffer); | 1924 g_free(connect_info->read_buffer); |
| 1918 connect_info->read_buffer = NULL; | 1925 connect_info->read_buffer = NULL; |
| 1919 /* Success */ | 1926 /* Success */ |
| 1920 s5_sendconnect(connect_info, source); | 1927 s5_sendconnect(connect_info, connect_info->fd); |
| 1921 return; | 1928 return; |
| 1922 } else { | 1929 } else { |
| 1923 /* Failure */ | 1930 /* Failure */ |
| 1924 gaim_debug_warning("proxy", | 1931 gaim_debug_warning("proxy", |
| 1925 "socks5 CHAP authentication " | 1932 "socks5 CHAP authentication " |
| 1926 "failed. Disconnecting..."); | 1933 "failed. Disconnecting..."); |
| 1927 close(source); | 1934 gaim_proxy_connect_info_disconnect(connect_info); |
| 1928 gaim_input_remove(connect_info->inpa); | |
| 1929 connect_info->inpa = 0; | |
| 1930 g_free(connect_info->read_buffer); | |
| 1931 connect_info->read_buffer = NULL; | |
| 1932 try_connect(connect_info); | 1935 try_connect(connect_info); |
| 1933 return; | 1936 return; |
| 1934 } | 1937 } |
| 1935 break; | 1938 break; |
| 1936 case 0x03: | 1939 case 0x03: |
| 1952 g_free(connect_info->read_buffer); | 1955 g_free(connect_info->read_buffer); |
| 1953 connect_info->read_buffer = NULL; | 1956 connect_info->read_buffer = NULL; |
| 1954 | 1957 |
| 1955 connect_info->read_cb = s5_readchap; | 1958 connect_info->read_cb = s5_readchap; |
| 1956 | 1959 |
| 1957 connect_info->inpa = gaim_input_add(source, | 1960 connect_info->inpa = gaim_input_add(connect_info->fd, |
| 1958 GAIM_INPUT_WRITE, proxy_do_write, connect_info); | 1961 GAIM_INPUT_WRITE, proxy_do_write, connect_info); |
| 1959 | 1962 |
| 1960 proxy_do_write(connect_info, source, GAIM_INPUT_WRITE); | 1963 proxy_do_write(connect_info, connect_info->fd, GAIM_INPUT_WRITE); |
| 1961 break; | 1964 break; |
| 1962 case 0x11: | 1965 case 0x11: |
| 1963 /* Server wants to select an algorithm */ | 1966 /* Server wants to select an algorithm */ |
| 1964 if (buf[0] != 0x85) { | 1967 if (buf[0] != 0x85) { |
| 1965 /* Only currently support HMAC-MD5 */ | 1968 /* Only currently support HMAC-MD5 */ |
| 1967 "Server tried to select an " | 1970 "Server tried to select an " |
| 1968 "algorithm that we did not advertise " | 1971 "algorithm that we did not advertise " |
| 1969 "as supporting. This is a violation " | 1972 "as supporting. This is a violation " |
| 1970 "of the socks5 CHAP specification. " | 1973 "of the socks5 CHAP specification. " |
| 1971 "Disconnecting..."); | 1974 "Disconnecting..."); |
| 1972 close(source); | 1975 gaim_proxy_connect_info_disconnect(connect_info); |
| 1973 gaim_input_remove(connect_info->inpa); | |
| 1974 connect_info->inpa = 0; | |
| 1975 g_free(connect_info->read_buffer); | |
| 1976 connect_info->read_buffer = NULL; | |
| 1977 try_connect(connect_info); | 1976 try_connect(connect_info); |
| 1978 return; | 1977 return; |
| 1979 } | 1978 } |
| 1980 break; | 1979 break; |
| 1981 } | 1980 } |
| 1998 connect_info->read_len = 0; | 1997 connect_info->read_len = 0; |
| 1999 } | 1998 } |
| 2000 | 1999 |
| 2001 gaim_debug_info("socks5 proxy", "Able to read.\n"); | 2000 gaim_debug_info("socks5 proxy", "Able to read.\n"); |
| 2002 | 2001 |
| 2003 len = read(source, connect_info->read_buffer + connect_info->read_len, | 2002 len = read(connect_info->fd, connect_info->read_buffer + connect_info->read_len, |
| 2004 connect_info->read_buf_len - connect_info->read_len); | 2003 connect_info->read_buf_len - connect_info->read_len); |
| 2005 if(len < 0 && errno == EAGAIN) | 2004 if(len < 0 && errno == EAGAIN) |
| 2006 return; | 2005 return; |
| 2007 else if(len <= 0) { | 2006 else if(len <= 0) { |
| 2008 close(source); | 2007 gaim_proxy_connect_info_disconnect(connect_info); |
| 2009 gaim_input_remove(connect_info->inpa); | |
| 2010 connect_info->inpa = 0; | |
| 2011 g_free(connect_info->read_buffer); | |
| 2012 connect_info->read_buffer = NULL; | |
| 2013 try_connect(connect_info); | 2008 try_connect(connect_info); |
| 2014 return; | 2009 return; |
| 2015 } | 2010 } |
| 2016 connect_info->read_len += len; | 2011 connect_info->read_len += len; |
| 2017 | 2012 |
| 2020 | 2015 |
| 2021 gaim_input_remove(connect_info->inpa); | 2016 gaim_input_remove(connect_info->inpa); |
| 2022 connect_info->inpa = 0; | 2017 connect_info->inpa = 0; |
| 2023 | 2018 |
| 2024 if ((connect_info->read_buffer[0] != 0x05) || (connect_info->read_buffer[1] == 0xff)) { | 2019 if ((connect_info->read_buffer[0] != 0x05) || (connect_info->read_buffer[1] == 0xff)) { |
| 2025 close(source); | 2020 gaim_proxy_connect_info_disconnect(connect_info); |
| 2026 g_free(connect_info->read_buffer); | |
| 2027 connect_info->read_buffer = NULL; | |
| 2028 try_connect(connect_info); | 2021 try_connect(connect_info); |
| 2029 return; | 2022 return; |
| 2030 } | 2023 } |
| 2031 | 2024 |
| 2032 if (connect_info->read_buffer[1] == 0x02) { | 2025 if (connect_info->read_buffer[1] == 0x02) { |
| 2054 g_free(connect_info->read_buffer); | 2047 g_free(connect_info->read_buffer); |
| 2055 connect_info->read_buffer = NULL; | 2048 connect_info->read_buffer = NULL; |
| 2056 | 2049 |
| 2057 connect_info->read_cb = s5_readauth; | 2050 connect_info->read_cb = s5_readauth; |
| 2058 | 2051 |
| 2059 connect_info->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, | 2052 connect_info->inpa = gaim_input_add(connect_info->fd, GAIM_INPUT_WRITE, |
| 2060 proxy_do_write, connect_info); | 2053 proxy_do_write, connect_info); |
| 2061 | 2054 |
| 2062 proxy_do_write(connect_info, source, GAIM_INPUT_WRITE); | 2055 proxy_do_write(connect_info, connect_info->fd, GAIM_INPUT_WRITE); |
| 2063 | 2056 |
| 2064 return; | 2057 return; |
| 2065 } else if (connect_info->read_buffer[1] == 0x03) { | 2058 } else if (connect_info->read_buffer[1] == 0x03) { |
| 2066 gsize userlen; | 2059 gsize userlen; |
| 2067 userlen = strlen(gaim_proxy_info_get_username(connect_info->gpi)); | 2060 userlen = strlen(gaim_proxy_info_get_username(connect_info->gpi)); |
| 2083 g_free(connect_info->read_buffer); | 2076 g_free(connect_info->read_buffer); |
| 2084 connect_info->read_buffer = NULL; | 2077 connect_info->read_buffer = NULL; |
| 2085 | 2078 |
| 2086 connect_info->read_cb = s5_readchap; | 2079 connect_info->read_cb = s5_readchap; |
| 2087 | 2080 |
| 2088 connect_info->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, | 2081 connect_info->inpa = gaim_input_add(connect_info->fd, GAIM_INPUT_WRITE, |
| 2089 proxy_do_write, connect_info); | 2082 proxy_do_write, connect_info); |
| 2090 | 2083 |
| 2091 proxy_do_write(connect_info, source, GAIM_INPUT_WRITE); | 2084 proxy_do_write(connect_info, connect_info->fd, GAIM_INPUT_WRITE); |
| 2092 | 2085 |
| 2093 return; | 2086 return; |
| 2094 } else { | 2087 } else { |
| 2095 g_free(connect_info->read_buffer); | 2088 g_free(connect_info->read_buffer); |
| 2096 connect_info->read_buffer = NULL; | 2089 connect_info->read_buffer = NULL; |
| 2097 | 2090 |
| 2098 s5_sendconnect(connect_info, source); | 2091 s5_sendconnect(connect_info, connect_info->fd); |
| 2099 } | 2092 } |
| 2100 } | 2093 } |
| 2101 | 2094 |
| 2102 static void | 2095 static void |
| 2103 s5_canwrite(gpointer data, gint source, GaimInputCondition cond) | 2096 s5_canwrite(gpointer data, gint source, GaimInputCondition cond) |
| 2115 gaim_input_remove(connect_info->inpa); | 2108 gaim_input_remove(connect_info->inpa); |
| 2116 connect_info->inpa = 0; | 2109 connect_info->inpa = 0; |
| 2117 } | 2110 } |
| 2118 | 2111 |
| 2119 len = sizeof(error); | 2112 len = sizeof(error); |
| 2120 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { | 2113 if (getsockopt(connect_info->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { |
| 2121 close(source); | 2114 gaim_proxy_connect_info_disconnect(connect_info); |
| 2122 | |
| 2123 try_connect(connect_info); | 2115 try_connect(connect_info); |
| 2124 return; | 2116 return; |
| 2125 } | 2117 } |
| 2126 | 2118 |
| 2127 i = 0; | 2119 i = 0; |
| 2144 connect_info->write_buffer = g_malloc(connect_info->write_buf_len); | 2136 connect_info->write_buffer = g_malloc(connect_info->write_buf_len); |
| 2145 memcpy(connect_info->write_buffer, buf, i); | 2137 memcpy(connect_info->write_buffer, buf, i); |
| 2146 | 2138 |
| 2147 connect_info->read_cb = s5_canread; | 2139 connect_info->read_cb = s5_canread; |
| 2148 | 2140 |
| 2149 connect_info->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, connect_info); | 2141 connect_info->inpa = gaim_input_add(connect_info->fd, GAIM_INPUT_WRITE, proxy_do_write, connect_info); |
| 2150 proxy_do_write(connect_info, source, GAIM_INPUT_WRITE); | 2142 proxy_do_write(connect_info, connect_info->fd, GAIM_INPUT_WRITE); |
| 2151 } | 2143 } |
| 2152 | 2144 |
| 2153 static int | 2145 static int |
| 2154 proxy_connect_socks5(GaimProxyConnectInfo *connect_info, struct sockaddr *addr, socklen_t addrlen) | 2146 proxy_connect_socks5(GaimProxyConnectInfo *connect_info, struct sockaddr *addr, socklen_t addrlen) |
| 2155 { | 2147 { |
| 2156 int fd = -1; | |
| 2157 | |
| 2158 gaim_debug_info("socks5 proxy", | 2148 gaim_debug_info("socks5 proxy", |
| 2159 "Connecting to %s:%d via %s:%d using SOCKS5\n", | 2149 "Connecting to %s:%d via %s:%d using SOCKS5\n", |
| 2160 connect_info->host, connect_info->port, | 2150 connect_info->host, connect_info->port, |
| 2161 gaim_proxy_info_get_host(connect_info->gpi), | 2151 gaim_proxy_info_get_host(connect_info->gpi), |
| 2162 gaim_proxy_info_get_port(connect_info->gpi)); | 2152 gaim_proxy_info_get_port(connect_info->gpi)); |
| 2163 | 2153 |
| 2164 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) | 2154 connect_info->fd = socket(addr->sa_family, SOCK_STREAM, 0); |
| 2155 if (connect_info->fd < 0) | |
| 2165 return -1; | 2156 return -1; |
| 2166 | 2157 |
| 2167 fcntl(fd, F_SETFL, O_NONBLOCK); | 2158 fcntl(connect_info->fd, F_SETFL, O_NONBLOCK); |
| 2168 #ifndef _WIN32 | 2159 #ifndef _WIN32 |
| 2169 fcntl(fd, F_SETFD, FD_CLOEXEC); | 2160 fcntl(connect_info->fd, F_SETFD, FD_CLOEXEC); |
| 2170 #endif | 2161 #endif |
| 2171 | 2162 |
| 2172 if (connect(fd, addr, addrlen) != 0) | 2163 if (connect(connect_info->fd, addr, addrlen) != 0) |
| 2173 { | 2164 { |
| 2174 if ((errno == EINPROGRESS) || (errno == EINTR)) { | 2165 if ((errno == EINPROGRESS) || (errno == EINTR)) { |
| 2175 gaim_debug_warning("socks5 proxy", | 2166 gaim_debug_info("socks5 proxy", "Connection in progress\n"); |
| 2176 "Connect would have blocked.\n"); | 2167 connect_info->inpa = gaim_input_add(connect_info->fd, GAIM_INPUT_WRITE, s5_canwrite, connect_info); |
| 2177 | |
| 2178 connect_info->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, s5_canwrite, connect_info); | |
| 2179 } | 2168 } |
| 2180 else { | 2169 else { |
| 2181 close(fd); | 2170 close(connect_info->fd); |
| 2171 connect_info->fd = -1; | |
| 2182 return -1; | 2172 return -1; |
| 2183 } | 2173 } |
| 2184 } | 2174 } |
| 2185 else { | 2175 else { |
| 2186 socklen_t len; | 2176 socklen_t len; |
| 2187 int error = ETIMEDOUT; | 2177 int error = ETIMEDOUT; |
| 2188 | 2178 |
| 2189 gaim_debug_misc("socks5 proxy", "Connect didn't block.\n"); | 2179 gaim_debug_info("socks5 proxy", "Connected immediately.\n"); |
| 2190 | 2180 |
| 2191 len = sizeof(error); | 2181 len = sizeof(error); |
| 2192 | 2182 |
| 2193 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) | 2183 if (getsockopt(connect_info->fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) |
| 2194 { | 2184 { |
| 2195 close(fd); | 2185 close(connect_info->fd); |
| 2186 connect_info->fd = -1; | |
| 2196 return -1; | 2187 return -1; |
| 2197 } | 2188 } |
| 2198 | 2189 |
| 2199 s5_canwrite(connect_info, fd, GAIM_INPUT_WRITE); | 2190 s5_canwrite(connect_info, connect_info->fd, GAIM_INPUT_WRITE); |
| 2200 } | 2191 } |
| 2201 | 2192 |
| 2202 return fd; | 2193 return connect_info->fd; |
| 2203 } | 2194 } |
| 2204 | 2195 |
| 2196 /** | |
| 2197 * This function iterates through a list of IP addresses and attempts | |
| 2198 * to connect to each one. This is called after the hostname is | |
| 2199 * resolved, and if a connection attempt fails. | |
| 2200 */ | |
| 2205 static void try_connect(GaimProxyConnectInfo *connect_info) | 2201 static void try_connect(GaimProxyConnectInfo *connect_info) |
| 2206 { | 2202 { |
| 2207 size_t addrlen; | 2203 size_t addrlen; |
| 2208 struct sockaddr *addr; | 2204 struct sockaddr *addr; |
| 2209 int ret = -1; | 2205 int ret = -1; |
| 2354 g_return_val_if_fail(port > 0, NULL); | 2350 g_return_val_if_fail(port > 0, NULL); |
| 2355 g_return_val_if_fail(connect_cb != NULL, NULL); | 2351 g_return_val_if_fail(connect_cb != NULL, NULL); |
| 2356 /* g_return_val_if_fail(error_cb != NULL, NULL); *//* TODO: Soon! */ | 2352 /* g_return_val_if_fail(error_cb != NULL, NULL); *//* TODO: Soon! */ |
| 2357 | 2353 |
| 2358 connect_info = g_new0(GaimProxyConnectInfo, 1); | 2354 connect_info = g_new0(GaimProxyConnectInfo, 1); |
| 2355 connect_info->fd = -1; | |
| 2359 connect_info->connect_cb = connect_cb; | 2356 connect_info->connect_cb = connect_cb; |
| 2360 connect_info->error_cb = error_cb; | 2357 connect_info->error_cb = error_cb; |
| 2361 connect_info->data = data; | 2358 connect_info->data = data; |
| 2362 connect_info->host = g_strdup(host); | 2359 connect_info->host = g_strdup(host); |
| 2363 connect_info->port = port; | 2360 connect_info->port = port; |
| 2416 g_return_val_if_fail(port > 0, NULL); | 2413 g_return_val_if_fail(port > 0, NULL); |
| 2417 g_return_val_if_fail(connect_cb != NULL, NULL); | 2414 g_return_val_if_fail(connect_cb != NULL, NULL); |
| 2418 /* g_return_val_if_fail(error_cb != NULL, NULL); *//* TODO: Soon! */ | 2415 /* g_return_val_if_fail(error_cb != NULL, NULL); *//* TODO: Soon! */ |
| 2419 | 2416 |
| 2420 connect_info = g_new0(GaimProxyConnectInfo, 1); | 2417 connect_info = g_new0(GaimProxyConnectInfo, 1); |
| 2418 connect_info->fd = -1; | |
| 2421 connect_info->connect_cb = connect_cb; | 2419 connect_info->connect_cb = connect_cb; |
| 2422 connect_info->error_cb = error_cb; | 2420 connect_info->error_cb = error_cb; |
| 2423 connect_info->data = data; | 2421 connect_info->data = data; |
| 2424 connect_info->host = g_strdup(host); | 2422 connect_info->host = g_strdup(host); |
| 2425 connect_info->port = port; | 2423 connect_info->port = port; |
| 2435 connect_infos = g_slist_prepend(connect_infos, connect_info); | 2433 connect_infos = g_slist_prepend(connect_infos, connect_info); |
| 2436 | 2434 |
| 2437 return connect_info; | 2435 return connect_info; |
| 2438 } | 2436 } |
| 2439 | 2437 |
| 2438 void | |
| 2439 gaim_proxy_connect_cancel(GaimProxyConnectInfo *connect_info) | |
| 2440 { | |
| 2441 gaim_proxy_connect_info_destroy(connect_info); | |
| 2442 } | |
| 2440 | 2443 |
| 2441 static void | 2444 static void |
| 2442 proxy_pref_cb(const char *name, GaimPrefType type, | 2445 proxy_pref_cb(const char *name, GaimPrefType type, |
| 2443 gconstpointer value, gpointer data) | 2446 gconstpointer value, gpointer data) |
| 2444 { | 2447 { |