src/proxy.c

changeset 14165
0f09ac2220f4
parent 14161
e959d0e476be
child 14170
f611621bc8a0
equal deleted inserted replaced
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 {

mercurial