comparison src/proxy.c @ 13200:33bef17125c2

[gaim-migrate @ 15563] This is the soon-to-be-infamous nonblocking network activity patch that I've been working on. Feel free to yell at me if this makes you unhappy. committer: Tailor Script <tailor@pidgin.im>
author Daniel Atallah <daniel.atallah@gmail.com>
date Thu, 09 Feb 2006 04:17:56 +0000
parents 31a3a9af1494
children de4f1fb08088
comparison
equal deleted inserted replaced
13199:d8f238864c88 13200:33bef17125c2
47 int port; 47 int port;
48 gint inpa; 48 gint inpa;
49 GaimProxyInfo *gpi; 49 GaimProxyInfo *gpi;
50 GaimAccount *account; 50 GaimAccount *account;
51 GSList *hosts; 51 GSList *hosts;
52 guchar *write_buffer;
53 gsize write_buf_len;
54 gsize written_len;
55 GaimInputFunction read_cb;
56 guchar *read_buffer;
57 gsize read_buf_len;
58 gsize read_len;
52 }; 59 };
53 60
54 static void try_connect(struct PHB *); 61 static void try_connect(struct PHB *);
55 62
56 static const char *socks5errors[] = { 63 static const char *socks5errors[] = {
252 } 259 }
253 /************************************************************************** 260 /**************************************************************************
254 * Proxy API 261 * Proxy API
255 **************************************************************************/ 262 **************************************************************************/
256 263
257 #ifdef __unix__ 264 #ifdef __unix__
258 265
259 /* 266 /*
260 * This structure represents both a pending DNS request and 267 * This structure represents both a pending DNS request and
261 * a free child process. 268 * a free child process.
262 */ 269 */
954 961
955 try_connect(phb); 962 try_connect(phb);
956 return; 963 return;
957 } 964 }
958 965
959 fcntl(source, F_SETFL, 0);
960 gaim_input_remove(phb->inpa); 966 gaim_input_remove(phb->inpa);
961 967
962 if (phb->account == NULL || 968 if (phb->account == NULL ||
963 gaim_account_get_connection(phb->account) != NULL) { 969 gaim_account_get_connection(phb->account) != NULL) {
964 970
1025 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 1031 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
1026 gaim_debug_error("proxy", "getsockopt failed.\n"); 1032 gaim_debug_error("proxy", "getsockopt failed.\n");
1027 close(fd); 1033 close(fd);
1028 return -1; 1034 return -1;
1029 } 1035 }
1030 fcntl(fd, F_SETFL, 0);
1031 phb->port = fd; /* bleh */ 1036 phb->port = fd; /* bleh */
1032 gaim_timeout_add(50, clean_connect, phb); /* we do this because we never 1037 gaim_timeout_add(50, clean_connect, phb); /* we do this because we never
1033 want to call our callback 1038 want to call our callback
1034 before we return. */ 1039 before we return. */
1035 } 1040 }
1036 1041
1037 return fd; 1042 return fd;
1038 } 1043 }
1039 1044
1045 static void
1046 proxy_do_write(gpointer data, gint source, GaimInputCondition cond)
1047 {
1048 struct PHB *phb = data;
1049 const char * request = phb->write_buffer + phb->written_len;
1050 gsize request_len = phb->write_buf_len - phb->written_len;
1051
1052 int ret = write(source, request, request_len);
1053
1054 if(ret < 0 && errno == EAGAIN)
1055 return;
1056 else if(ret < 0) {
1057 gaim_input_remove(phb->inpa);
1058 close(source);
1059 g_free(phb->write_buffer);
1060 phb->write_buffer = NULL;
1061 try_connect(phb);
1062 return;
1063 } else if (ret < request_len) {
1064 phb->written_len += ret;
1065 return;
1066 }
1067
1068 gaim_input_remove(phb->inpa);
1069 g_free(phb->write_buffer);
1070 phb->write_buffer = NULL;
1071
1072 /* register the response handler for the response */
1073 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, phb->read_cb, phb);
1074 }
1075
1040 #define HTTP_GOODSTRING "HTTP/1.0 200" 1076 #define HTTP_GOODSTRING "HTTP/1.0 200"
1041 #define HTTP_GOODSTRING2 "HTTP/1.1 200" 1077 #define HTTP_GOODSTRING2 "HTTP/1.1 200"
1042 1078
1043 static void 1079 static void
1044 http_complete(struct PHB *phb, gint source) 1080 http_complete(struct PHB *phb, gint source)
1045 { 1081 {
1046 gaim_debug_info("http proxy", "proxy connection established\n"); 1082 gaim_debug_info("http proxy", "proxy connection established\n");
1047 if(source < 0) { 1083 if(!phb->account || phb->account->gc) {
1048 try_connect(phb);
1049 } else if(!phb->account || phb->account->gc) {
1050 phb->func(phb->data, source, GAIM_INPUT_READ); 1084 phb->func(phb->data, source, GAIM_INPUT_READ);
1051 g_free(phb->host); 1085 }
1052 g_free(phb); 1086 g_free(phb->host);
1053 } 1087 g_free(phb);
1054 } 1088 }
1055 1089
1056 1090
1057 /* read the response to the CONNECT request, if we are requesting a non-port-80 tunnel */ 1091 /* read the response to the CONNECT request, if we are requesting a non-port-80 tunnel */
1058 static void 1092 static void
1059 http_canread(gpointer data, gint source, GaimInputCondition cond) 1093 http_canread(gpointer data, gint source, GaimInputCondition cond)
1060 { 1094 {
1061 int nlc = 0; 1095 int len, headers_len, status = 0;
1062 int pos = 0; 1096 gboolean error;
1063 int minor, major, status = 0, error=0;
1064 struct PHB *phb = data; 1097 struct PHB *phb = data;
1065 char inputline[8192], *p; 1098 guchar *p;
1066 1099 gsize max_read;
1067 1100
1068 while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) { 1101 if(phb->read_buffer == NULL) {
1069 if (inputline[pos - 1] == '\n') 1102 phb->read_buf_len = 8192;
1070 nlc++; 1103 phb->read_buffer = g_malloc(phb->read_buf_len);
1071 else if (inputline[pos - 1] != '\r') 1104 phb->read_len = 0;
1072 nlc = 0; 1105 }
1073 } 1106
1074 inputline[pos] = '\0'; 1107 p = phb->read_buffer + phb->read_len - 1;
1075 1108 max_read = phb->read_buf_len - phb->read_len;
1076 error = strncmp(inputline, "HTTP/", 5) != 0; 1109
1110 len = read(source, p, max_read);
1111 if(len < 0 && errno == EAGAIN)
1112 return;
1113 else if(len < 0) {
1114 close(source);
1115 source = -1;
1116 g_free(phb->read_buffer);
1117 phb->read_buffer = NULL;
1118 gaim_input_remove(phb->inpa);
1119 phb->inpa = 0;
1120 http_complete(phb, source);
1121 return;
1122 } else {
1123 phb->read_len += len;
1124 }
1125 p[len] = '\0';
1126
1127 if((p = g_strstr_len(phb->read_buffer, phb->read_len, "\r\n\r\n"))) {
1128 *p = '\0';
1129 headers_len = (p - phb->read_buffer) + 4;
1130 } else if(len == max_read)
1131 headers_len = len;
1132 else
1133 return;
1134
1135 error = strncmp(phb->read_buffer, "HTTP/", 5) != 0;
1077 if(!error) { 1136 if(!error) {
1078 p = inputline + 5; 1137 char *c;
1079 major = strtol(p, &p, 10); 1138 int major;
1080 error = (major==0) || (*p != '.'); 1139 p = phb->read_buffer + 5;
1140 c = p;
1141 major = strtol(c, &c, 10);
1142 p = c;
1143 error = (major == 0) || (*p != '.');
1081 if(!error) { 1144 if(!error) {
1145 int minor;
1082 p++; 1146 p++;
1083 minor = strtol(p, &p, 10); 1147 c = p;
1084 error = (*p!=' '); 1148 minor = strtol(c, &c, 10);
1149 p = c;
1150 error = (*p != ' ');
1085 if(!error) { 1151 if(!error) {
1086 p++; 1152 p++;
1087 status = strtol(p, &p, 10); 1153 c = p;
1088 error = (*p!=' '); 1154 status = strtol(c, &c, 10);
1155 p = c;
1156 error = (*p != ' ');
1089 } 1157 }
1090 } 1158 }
1091 } 1159 }
1092 1160
1093 /* Read the contents */ 1161 /* Read the contents */
1094 p = g_strrstr(inputline, "Content-Length: "); 1162 p = g_strrstr(phb->read_buffer, "Content-Length: ");
1095 if(p != NULL) { 1163 if(p != NULL) {
1096 gchar *tmp; 1164 gchar *tmp;
1097 int len = 0; 1165 int len = 0;
1098 char tmpc; 1166 char tmpc;
1099 p += strlen("Content-Length: "); 1167 p += strlen("Content-Length: ");
1100 tmp = strchr(p, '\r'); 1168 tmp = strchr(p, '\r');
1101 *tmp = 0; 1169 *tmp = '\0';
1102 len = atoi(p); 1170 len = atoi(p);
1103 *tmp = '\r'; 1171 *tmp = '\r';
1104 while(len--) read(source, &tmpc, 1); 1172
1105 } 1173 /* Compensate for what has already been read */
1174 len -= phb->read_len - headers_len;
1175 /* I'm assuming that we're doing this to prevent the server from
1176 complaining / breaking since we don't read the whole page */
1177 while(len--) {
1178 /* TODO: deal with EAGAIN (and other errors) better */
1179 if (read(source, &tmpc, 1) < 0 && errno != EAGAIN)
1180 break;
1181 }
1182 }
1183
1106 if(error) { 1184 if(error) {
1107 gaim_debug_error("proxy", 1185 gaim_debug_error("proxy",
1108 "Unable to parse proxy's response: %s\n", inputline); 1186 "Unable to parse proxy's response: %s\n",
1187 phb->read_buffer);
1109 close(source); 1188 close(source);
1110 source=-1; 1189 source = -1;
1111 } 1190 g_free(phb->read_buffer);
1112 else if(status!=200) { 1191 phb->read_buffer = NULL;
1192 gaim_input_remove(phb->inpa);
1193 phb->inpa = 0;
1194 http_complete(phb, source);
1195 return;
1196 } else if(status != 200) {
1113 gaim_debug_error("proxy", 1197 gaim_debug_error("proxy",
1114 "Proxy server replied with:\n%s\n", inputline); 1198 "Proxy server replied with:\n%s\n",
1199 phb->read_buffer);
1200
1115 1201
1116 /* XXX: why in the hell are we calling gaim_connection_error() here? */ 1202 /* XXX: why in the hell are we calling gaim_connection_error() here? */
1117 if ( status == 407 /* Proxy Auth */ ) { 1203 if(status == 407 /* Proxy Auth */) {
1118 gchar *ntlm; 1204 gchar *ntlm;
1119 if( (ntlm = g_strrstr(inputline, "Proxy-Authenticate: NTLM "))) { /* Check for Type-2 */ 1205 if((ntlm = g_strrstr(phb->read_buffer, "Proxy-Authenticate: NTLM "))) { /* Check for Type-2 */
1120 gchar *nonce = ntlm; 1206 gchar *nonce = ntlm;
1121 gchar *domain = (gchar*)gaim_proxy_info_get_username(phb->gpi); 1207 gchar *domain = (gchar*)gaim_proxy_info_get_username(phb->gpi);
1122 gchar *username; 1208 gchar *username;
1123 gchar *request; 1209 gchar *request;
1124 gchar *response; 1210 gchar *response;
1126 char *msg = g_strdup_printf(_("Proxy connection error %d"), status); 1212 char *msg = g_strdup_printf(_("Proxy connection error %d"), status);
1127 close(source); 1213 close(source);
1128 source = -1; 1214 source = -1;
1129 gaim_connection_error(phb->account->gc, msg); 1215 gaim_connection_error(phb->account->gc, msg);
1130 g_free(msg); 1216 g_free(msg);
1131 gaim_input_remove(phb->inpa); 1217 gaim_input_remove(phb->inpa);
1218 g_free(phb->read_buffer);
1219 g_free(phb->host);
1220 g_free(phb);
1132 return; 1221 return;
1133 } 1222 }
1134 *username = 0; 1223 *username = '\0';
1135 username ++; 1224 username++;
1136 ntlm += strlen("Proxy-Authenticate: NTLM "); 1225 ntlm += strlen("Proxy-Authenticate: NTLM ");
1137 while(*nonce != '\r' && *nonce != '\0') nonce ++; 1226 while(*nonce != '\r' && *nonce != '\0') nonce ++;
1138 *nonce = 0; 1227 *nonce = '\0';
1139 nonce = gaim_ntlm_parse_type2(ntlm, NULL); 1228 nonce = gaim_ntlm_parse_type2(ntlm, NULL);
1140 response = gaim_ntlm_gen_type3(username, (gchar*)gaim_proxy_info_get_password(phb->gpi), (gchar*)gaim_proxy_info_get_host(phb->gpi), domain, nonce, NULL); 1229 response = gaim_ntlm_gen_type3(username,
1230 (gchar*) gaim_proxy_info_get_password(phb->gpi),
1231 (gchar*) gaim_proxy_info_get_host(phb->gpi),
1232 domain, nonce, NULL);
1141 username--; 1233 username--;
1142 *username = '\\'; 1234 *username = '\\';
1143 request = g_strdup_printf("CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\nProxy-Authorization: NTLM %s\r\nProxy-Connection: Keep-Alive\r\n\r\n", 1235 request = g_strdup_printf(
1144 phb->host, phb->port, phb->host, phb->port, 1236 "CONNECT %s:%d HTTP/1.1\r\n"
1145 response); 1237 "Host: %s:%d\r\n"
1146 write(source, request, strlen(request)); 1238 "Proxy-Authorization: NTLM %s\r\n"
1147 g_free(request); 1239 "Proxy-Connection: Keep-Alive\r\n\r\n",
1240 phb->host, phb->port, phb->host,
1241 phb->port, response);
1148 g_free(response); 1242 g_free(response);
1243
1244 gaim_input_remove(phb->inpa);
1245 g_free(phb->read_buffer);
1246 phb->read_buffer = NULL;
1247
1248 phb->write_buffer = request;
1249 phb->write_buf_len = strlen(request);
1250 phb->written_len = 0;
1251
1252 phb->read_cb = http_canread;
1253
1254 phb->inpa = gaim_input_add(source,
1255 GAIM_INPUT_WRITE, proxy_do_write, phb);
1256
1257 proxy_do_write(phb, source, cond);
1149 return; 1258 return;
1150 } else if((ntlm = g_strrstr(inputline, "Proxy-Authenticate: NTLM"))) { /* Empty message */ 1259 } else if((ntlm = g_strrstr(phb->read_buffer, "Proxy-Authenticate: NTLM"))) { /* Empty message */
1151 gchar request[2048]; 1260 gchar request[2048];
1152 gchar *domain = (gchar*)gaim_proxy_info_get_username(phb->gpi); 1261 gchar *domain = (gchar*) gaim_proxy_info_get_username(phb->gpi);
1153 gchar *username; 1262 gchar *username;
1154 int request_len; 1263 int request_len;
1155 if(!(username = strchr(domain, '\\'))) { 1264 if(!(username = strchr(domain, '\\'))) {
1156 char *msg = g_strdup_printf(_("Proxy connection error %d"), status); 1265 char *msg = g_strdup_printf(_("Proxy connection error %d"), status);
1157 close(source); 1266 close(source);
1158 source = -1; 1267 source = -1;
1159 gaim_connection_error(phb->account->gc, msg); 1268 gaim_connection_error(phb->account->gc, msg);
1160 g_free(msg); 1269 g_free(msg);
1161 gaim_input_remove(phb->inpa); 1270 gaim_input_remove(phb->inpa);
1271 g_free(phb->read_buffer);
1272 g_free(phb->host);
1273 g_free(phb);
1162 return; 1274 return;
1163 } 1275 }
1164 *username = 0; 1276 *username = '\0';
1165 1277
1166 request_len = g_snprintf(request, sizeof(request), 1278 request_len = g_snprintf(request, sizeof(request),
1167 "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", 1279 "CONNECT %s:%d HTTP/1.1\r\n"
1168 phb->host, phb->port, phb->host, phb->port); 1280 "Host: %s:%d\r\n",
1281 phb->host, phb->port,
1282 phb->host, phb->port);
1169 1283
1170 g_return_if_fail(request_len < sizeof(request)); 1284 g_return_if_fail(request_len < sizeof(request));
1171 request_len += g_snprintf(request + request_len, 1285 request_len += g_snprintf(request + request_len,
1172 sizeof(request) - request_len, 1286 sizeof(request) - request_len,
1173 "Proxy-Authorization: NTLM %s\r\nProxy-Connection: Keep-Alive\r\n\r\n", gaim_ntlm_gen_type1((gchar*)gaim_proxy_info_get_host(phb->gpi),domain)); 1287 "Proxy-Authorization: NTLM %s\r\n"
1288 "Proxy-Connection: Keep-Alive\r\n\r\n",
1289 gaim_ntlm_gen_type1(
1290 (gchar*) gaim_proxy_info_get_host(phb->gpi),
1291 domain));
1174 *username = '\\'; 1292 *username = '\\';
1175 write(source, request, request_len); 1293
1294 gaim_input_remove(phb->inpa);
1295 g_free(phb->read_buffer);
1296 phb->read_buffer = NULL;
1297
1298 phb->write_buffer = g_strndup(request,
1299 request_len);
1300 phb->write_buf_len = request_len;
1301 phb->written_len = 0;
1302
1303 phb->read_cb = http_canread;
1304
1305 phb->inpa = gaim_input_add(source,
1306 GAIM_INPUT_WRITE, proxy_do_write, phb);
1307
1308 proxy_do_write(phb, source, cond);
1176 return; 1309 return;
1177 } else { 1310 } else {
1178 char *msg = g_strdup_printf(_("Proxy connection error %d"), status); 1311 char *msg = g_strdup_printf(_("Proxy connection error %d"), status);
1179 close(source); 1312 close(source);
1180 source = -1; 1313 source = -1;
1181 gaim_connection_error(phb->account->gc, msg); 1314 gaim_connection_error(phb->account->gc, msg);
1182 g_free(msg); 1315 g_free(msg);
1316 gaim_input_remove(phb->inpa);
1317 g_free(phb->read_buffer);
1318 g_free(phb->host);
1319 g_free(phb);
1183 } 1320 }
1184 } 1321 }
1185 if ( status == 403 /* Forbidden */ ) { 1322 if(status == 403 /* Forbidden */ ) {
1186 gchar *msg = g_strdup_printf(_("Access denied: proxy server forbids port %d tunnelling."), phb->port); 1323 gchar *msg = g_strdup_printf(_("Access denied: proxy server forbids port %d tunnelling."), phb->port);
1187 gaim_connection_error(phb->account->gc, msg); 1324 gaim_connection_error(phb->account->gc, msg);
1188 g_free(msg); 1325 g_free(msg);
1326 gaim_input_remove(phb->inpa);
1327 g_free(phb->read_buffer);
1328 g_free(phb->host);
1329 g_free(phb);
1189 } else { 1330 } else {
1190 char *msg = g_strdup_printf(_("Proxy connection error %d"), status); 1331 char *msg = g_strdup_printf(_("Proxy connection error %d"), status);
1191 gaim_connection_error(phb->account->gc, msg); 1332 gaim_connection_error(phb->account->gc, msg);
1192 g_free(msg); 1333 g_free(msg);
1334 gaim_input_remove(phb->inpa);
1335 g_free(phb->read_buffer);
1336 g_free(phb->host);
1337 g_free(phb);
1193 } 1338 }
1194 } else { 1339 } else {
1340 gaim_input_remove(phb->inpa);
1341 g_free(phb->read_buffer);
1342 phb->read_buffer = NULL;
1195 http_complete(phb, source); 1343 http_complete(phb, source);
1196 } 1344 return;
1197 1345 }
1198 gaim_input_remove(phb->inpa); 1346 }
1199 return; 1347
1200 } 1348
1201 1349
1202 static void 1350 static void
1203 http_canwrite(gpointer data, gint source, GaimInputCondition cond) 1351 http_canwrite(gpointer data, gint source, GaimInputCondition cond)
1204 { 1352 {
1205 char request[8192]; 1353 char request[8192];
1220 1368
1221 try_connect(phb); 1369 try_connect(phb);
1222 return; 1370 return;
1223 } 1371 }
1224 1372
1225 gaim_debug_info("proxy", "using CONNECT tunnelling for %s:%d\n", phb->host, phb->port); 1373 gaim_debug_info("proxy", "using CONNECT tunnelling for %s:%d\n",
1374 phb->host, phb->port);
1226 request_len = g_snprintf(request, sizeof(request), 1375 request_len = g_snprintf(request, sizeof(request),
1227 "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", 1376 "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n",
1228 phb->host, phb->port, phb->host, phb->port); 1377 phb->host, phb->port, phb->host, phb->port);
1229 1378
1230 if (gaim_proxy_info_get_username(phb->gpi) != NULL) { 1379 if (gaim_proxy_info_get_username(phb->gpi) != NULL) {
1231 char *t1, *t2; 1380 char *t1, *t2;
1232 t1 = g_strdup_printf("%s:%s", 1381 t1 = g_strdup_printf("%s:%s",
1233 gaim_proxy_info_get_username(phb->gpi), 1382 gaim_proxy_info_get_username(phb->gpi),
1234 gaim_proxy_info_get_password(phb->gpi) ? 1383 gaim_proxy_info_get_password(phb->gpi) ?
1235 gaim_proxy_info_get_password(phb->gpi) : ""); 1384 gaim_proxy_info_get_password(phb->gpi) : "");
1236 t2 = gaim_base64_encode((const guchar *)t1, strlen(t1)); 1385 t2 = gaim_base64_encode((const guchar *)t1, strlen(t1));
1237 g_free(t1); 1386 g_free(t1);
1238 g_return_if_fail(request_len < sizeof(request)); 1387 g_return_if_fail(request_len < sizeof(request));
1239 1388
1240 request_len += g_snprintf(request + request_len, 1389 request_len += g_snprintf(request + request_len,
1241 sizeof(request) - request_len, 1390 sizeof(request) - request_len,
1242 "Proxy-Authorization: Basic %s\r\nProxy-Authorization: NTLM %s\r\nProxy-Connection: Keep-Alive\r\n", t2, gaim_ntlm_gen_type1((gchar*)gaim_proxy_info_get_host(phb->gpi),"")); 1391 "Proxy-Authorization: Basic %s\r\n"
1392 "Proxy-Authorization: NTLM %s\r\n"
1393 "Proxy-Connection: Keep-Alive\r\n", t2,
1394 gaim_ntlm_gen_type1(
1395 (gchar*)gaim_proxy_info_get_host(phb->gpi),""));
1243 g_free(t2); 1396 g_free(t2);
1244 } 1397 }
1245 1398
1246 g_return_if_fail(request_len < sizeof(request)); 1399 g_return_if_fail(request_len < sizeof(request));
1247 strcpy(request + request_len, "\r\n"); 1400 strcpy(request + request_len, "\r\n");
1248 request_len += 2; 1401 request_len += 2;
1249 1402 phb->write_buffer = g_strndup(request, request_len);
1250 if (write(source, request, request_len) < 0) { 1403 phb->write_buf_len = request_len;
1251 close(source); 1404 phb->written_len = 0;
1252 1405
1253 try_connect(phb); 1406 phb->read_cb = http_canread;
1254 return; 1407
1255 } 1408 phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write,
1256 1409 phb);
1257 /* register the response handler for the CONNECT request */ 1410
1258 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb); 1411 proxy_do_write(phb, source, cond);
1259 } 1412 }
1260 1413
1261 static int 1414 static int
1262 proxy_connect_http(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) 1415 proxy_connect_http(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
1263 { 1416 {
1305 len = sizeof(error); 1458 len = sizeof(error);
1306 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 1459 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
1307 close(fd); 1460 close(fd);
1308 return -1; 1461 return -1;
1309 } 1462 }
1310 fcntl(fd, F_SETFL, 0);
1311 http_canwrite(phb, fd, GAIM_INPUT_WRITE); 1463 http_canwrite(phb, fd, GAIM_INPUT_WRITE);
1312 } 1464 }
1313 1465
1314 return fd; 1466 return fd;
1315 } 1467 }
1468
1316 1469
1317 static void 1470 static void
1318 s4_canread(gpointer data, gint source, GaimInputCondition cond) 1471 s4_canread(gpointer data, gint source, GaimInputCondition cond)
1319 { 1472 {
1320 unsigned char packet[12];
1321 struct PHB *phb = data; 1473 struct PHB *phb = data;
1474 char *buf;
1475 int len, max_read;
1476
1477 /* This is really not going to block under normal circumstances, but to
1478 * be correct, we deal with the unlikely scenario */
1479
1480 if (phb->read_buffer == NULL) {
1481 phb->read_buf_len = 12;
1482 phb->read_buffer = g_malloc(phb->read_buf_len);
1483 phb->read_len = 0;
1484 }
1485
1486 buf = phb->read_buffer + phb->read_len - 1;
1487 max_read = phb->read_buf_len - phb->read_len;
1488
1489 len = read(source, buf, max_read);
1490
1491 if ((len < 0 && errno == EAGAIN) || len + phb->read_len < 4)
1492 return;
1493 else if (len + phb->read_len >= 4) {
1494 if (phb->read_buffer[1] == 90) {
1495 if (phb->account == NULL ||
1496 gaim_account_get_connection(phb->account) != NULL) {
1497
1498 phb->func(phb->data, source, GAIM_INPUT_READ);
1499 }
1500
1501 gaim_input_remove(phb->inpa);
1502 g_free(phb->read_buffer);
1503 g_free(phb->host);
1504 g_free(phb);
1505 return;
1506 }
1507 }
1322 1508
1323 gaim_input_remove(phb->inpa); 1509 gaim_input_remove(phb->inpa);
1324 1510 g_free(phb->read_buffer);
1325 memset(packet, 0, sizeof(packet)); 1511 phb->read_buffer = NULL;
1326
1327 if (read(source, packet, 9) >= 4 && packet[1] == 90) {
1328 if (phb->account == NULL ||
1329 gaim_account_get_connection(phb->account) != NULL) {
1330
1331 phb->func(phb->data, source, GAIM_INPUT_READ);
1332 }
1333
1334 g_free(phb->host);
1335 g_free(phb);
1336 return;
1337 }
1338 1512
1339 close(source); 1513 close(source);
1340 1514
1341 try_connect(phb); 1515 try_connect(phb);
1342 } 1516 }
1343 1517
1344 static void 1518 static void
1345 s4_canwrite(gpointer data, gint source, GaimInputCondition cond) 1519 s4_canwrite(gpointer data, gint source, GaimInputCondition cond)
1346 { 1520 {
1347 unsigned char packet[12]; 1521 unsigned char packet[9];
1348 struct hostent *hp; 1522 struct hostent *hp;
1349 struct PHB *phb = data; 1523 struct PHB *phb = data;
1350 socklen_t len; 1524 socklen_t len;
1351 int error = ETIMEDOUT; 1525 int error = ETIMEDOUT;
1352 1526
1361 close(source); 1535 close(source);
1362 1536
1363 try_connect(phb); 1537 try_connect(phb);
1364 return; 1538 return;
1365 } 1539 }
1366 fcntl(source, F_SETFL, 0);
1367 1540
1368 /* 1541 /*
1369 * The socks4 spec doesn't include support for doing host name 1542 * The socks4 spec doesn't include support for doing host name
1370 * lookups by the proxy. Some socks4 servers do this via 1543 * lookups by the proxy. Some socks4 servers do this via
1371 * extensions to the protocol. Since we don't know if a 1544 * extensions to the protocol. Since we don't know if a
1388 packet[5] = (unsigned char)(hp->h_addr_list[0])[1]; 1561 packet[5] = (unsigned char)(hp->h_addr_list[0])[1];
1389 packet[6] = (unsigned char)(hp->h_addr_list[0])[2]; 1562 packet[6] = (unsigned char)(hp->h_addr_list[0])[2];
1390 packet[7] = (unsigned char)(hp->h_addr_list[0])[3]; 1563 packet[7] = (unsigned char)(hp->h_addr_list[0])[3];
1391 packet[8] = 0; 1564 packet[8] = 0;
1392 1565
1393 if (write(source, packet, 9) != 9) { 1566 phb->write_buffer = g_strndup(packet, sizeof(packet));
1394 close(source); 1567 phb->write_buf_len = sizeof(packet);
1395 1568 phb->written_len = 0;
1396 try_connect(phb); 1569 phb->read_cb = s4_canread;
1397 return; 1570
1398 } 1571 phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, phb);
1399 1572
1400 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb); 1573 proxy_do_write(phb, source, cond);
1401 } 1574 }
1402 1575
1403 static int 1576 static int
1404 proxy_connect_socks4(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) 1577 proxy_connect_socks4(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
1405 { 1578 {
1441 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 1614 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
1442 close(fd); 1615 close(fd);
1443 return -1; 1616 return -1;
1444 } 1617 }
1445 1618
1446 fcntl(fd, F_SETFL, 0);
1447 s4_canwrite(phb, fd, GAIM_INPUT_WRITE); 1619 s4_canwrite(phb, fd, GAIM_INPUT_WRITE);
1448 } 1620 }
1449 1621
1450 return fd; 1622 return fd;
1451 } 1623 }
1452 1624
1453 static void 1625 static void
1454 s5_canread_again(gpointer data, gint source, GaimInputCondition cond) 1626 s5_canread_again(gpointer data, gint source, GaimInputCondition cond)
1455 { 1627 {
1456 unsigned char buf[512]; 1628 guchar *dest, *buf;
1457 struct PHB *phb = data; 1629 struct PHB *phb = data;
1458 1630 int len;
1459 gaim_input_remove(phb->inpa); 1631
1632 if (phb->read_buffer == NULL) {
1633 phb->read_buf_len = 512;
1634 phb->read_buffer = g_malloc(phb->read_buf_len);
1635 phb->read_len = 0;
1636 }
1637
1638 dest = phb->read_buffer + phb->read_len;
1639 buf = phb->read_buffer;
1640
1460 gaim_debug_info("socks5 proxy", "Able to read again.\n"); 1641 gaim_debug_info("socks5 proxy", "Able to read again.\n");
1461 1642
1462 if (read(source, buf, 4) < 4) { 1643 len = read(source, dest, (phb->read_buf_len - phb->read_len));
1644 if(len < 0 && errno == EAGAIN)
1645 return;
1646 else if(len < 0) {
1463 gaim_debug_warning("socks5 proxy", "or not...\n"); 1647 gaim_debug_warning("socks5 proxy", "or not...\n");
1464 close(source); 1648 close(source);
1465 1649 gaim_input_remove(phb->inpa);
1650 g_free(phb->read_buffer);
1651 phb->read_buffer = NULL;
1466 try_connect(phb); 1652 try_connect(phb);
1467 return; 1653 return;
1468 } 1654 }
1655 phb->read_len += len;
1656
1657 if(phb->read_len < 4)
1658 return;
1659
1469 if ((buf[0] != 0x05) || (buf[1] != 0x00)) { 1660 if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
1470 if ((buf[0] == 0x05) && (buf[1] < 0x09)) 1661 if ((buf[0] == 0x05) && (buf[1] < 0x09))
1471 gaim_debug_error("socks5 proxy", socks5errors[buf[1]]); 1662 gaim_debug_error("socks5 proxy", socks5errors[buf[1]]);
1472 else 1663 else
1473 gaim_debug_error("socks5 proxy", "Bad data.\n"); 1664 gaim_debug_error("socks5 proxy", "Bad data.\n");
1474 close(source); 1665 close(source);
1475 1666 gaim_input_remove(phb->inpa);
1667 g_free(phb->read_buffer);
1668 phb->read_buffer = NULL;
1476 try_connect(phb); 1669 try_connect(phb);
1477 return; 1670 return;
1478 } 1671 }
1479 1672
1480 /* Skip past BND.ADDR */ 1673 /* Skip past BND.ADDR */
1481 switch(buf[3]) { 1674 switch(buf[3]) {
1482 case 0x01: /* the address is a version-4 IP address, with a length of 4 octets */ 1675 case 0x01: /* the address is a version-4 IP address, with a length of 4 octets */
1483 read(source, buf, 4); 1676 if(phb->read_len < 4 + 4)
1677 return;
1678 buf += 4 + 4;
1484 break; 1679 break;
1485 case 0x03: /* the address field contains a fully-qualified domain name. The first 1680 case 0x03: /* the address field contains a fully-qualified domain name. The first
1486 octet of the address field contains the number of octets of name that 1681 octet of the address field contains the number of octets of name that
1487 follow, there is no terminating NUL octet. */ 1682 follow, there is no terminating NUL octet. */
1488 read(source, buf, 1); 1683 if(phb->read_len < 4 + 1)
1489 read(source, buf, buf[0]); 1684 return;
1685 buf += 4 + 1;
1686 if(phb->read_len < 4 + 1 + buf[0])
1687 return;
1688 buf += buf[0];
1490 break; 1689 break;
1491 case 0x04: /* the address is a version-6 IP address, with a length of 16 octets */ 1690 case 0x04: /* the address is a version-6 IP address, with a length of 16 octets */
1492 read(source, buf, 16); 1691 if(phb->read_len < 4 + 16)
1692 return;
1693 buf += 4 + 16;
1493 break; 1694 break;
1494 } 1695 }
1495 1696
1697 if(phb->read_len < (buf - phb->read_buffer) + 2)
1698 return;
1699
1496 /* Skip past BND.PORT */ 1700 /* Skip past BND.PORT */
1497 read(source, buf, 2); 1701 buf += 2;
1498 1702
1499 if (phb->account == NULL || 1703 if (phb->account == NULL ||
1500 gaim_account_get_connection(phb->account) != NULL) { 1704 gaim_account_get_connection(phb->account) != NULL) {
1501 1705
1502 phb->func(phb->data, source, GAIM_INPUT_READ); 1706 phb->func(phb->data, source, GAIM_INPUT_READ);
1503 } 1707 }
1504 1708
1709 gaim_input_remove(phb->inpa);
1710 g_free(phb->read_buffer);
1505 g_free(phb->host); 1711 g_free(phb->host);
1506 g_free(phb); 1712 g_free(phb);
1507 } 1713 }
1508 1714
1509 static void 1715 static void
1510 s5_sendconnect(gpointer data, gint source) 1716 s5_sendconnect(gpointer data, gint source)
1511 { 1717 {
1512 unsigned char buf[512];
1513 struct PHB *phb = data; 1718 struct PHB *phb = data;
1514 int hlen = strlen(phb->host); 1719 int hlen = strlen(phb->host);
1515 1720 phb->write_buf_len = 5 + hlen + 1;
1516 buf[0] = 0x05; 1721 phb->write_buffer = g_malloc(phb->write_buf_len);
1517 buf[1] = 0x01; /* CONNECT */ 1722 phb->written_len = 0;
1518 buf[2] = 0x00; /* reserved */ 1723
1519 buf[3] = 0x03; /* address type -- host name */ 1724 phb->write_buffer[0] = 0x05;
1520 buf[4] = hlen; 1725 phb->write_buffer[1] = 0x01; /* CONNECT */
1521 memcpy(buf + 5, phb->host, hlen); 1726 phb->write_buffer[2] = 0x00; /* reserved */
1522 buf[5 + hlen] = phb->port >> 8; 1727 phb->write_buffer[3] = 0x03; /* address type -- host name */
1523 buf[5 + hlen + 1] = phb->port & 0xff; 1728 phb->write_buffer[4] = hlen;
1524 1729 memcpy(phb->write_buffer + 5, phb->host, hlen);
1525 if (write(source, buf, (5 + hlen + 2)) < (5 + hlen + 2)) { 1730 phb->write_buffer[5 + hlen] = phb->port >> 8;
1526 close(source); 1731 phb->write_buffer[5 + hlen + 1] = phb->port & 0xff;
1527 1732
1528 try_connect(phb); 1733 phb->read_cb = s5_canread_again;
1529 return; 1734
1530 } 1735 phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, phb);
1531 1736 proxy_do_write(phb, source, GAIM_INPUT_WRITE);
1532 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb); 1737
1533 } 1738 }
1534 1739
1535 static void 1740 static void
1536 s5_readauth(gpointer data, gint source, GaimInputCondition cond) 1741 s5_readauth(gpointer data, gint source, GaimInputCondition cond)
1537 { 1742 {
1538 unsigned char buf[512];
1539 struct PHB *phb = data; 1743 struct PHB *phb = data;
1744 int len;
1745
1746 if (phb->read_buffer == NULL) {
1747 phb->read_buf_len = 2;
1748 phb->read_buffer = g_malloc(phb->read_buf_len);
1749 phb->read_len = 0;
1750 }
1751
1752 gaim_debug_info("socks5 proxy", "Got auth response.\n");
1753
1754 len = read(source, phb->read_buffer + phb->read_len,
1755 phb->read_buf_len - phb->read_len);
1756 if(len < 0 && errno == EAGAIN)
1757 return;
1758 else if(len < 0) {
1759 close(source);
1760 gaim_input_remove(phb->inpa);
1761 g_free(phb->read_buffer);
1762 phb->read_buffer = NULL;
1763 try_connect(phb);
1764 return;
1765 }
1766 phb->read_len += len;
1767
1768 if (phb->read_len < 2)
1769 return;
1540 1770
1541 gaim_input_remove(phb->inpa); 1771 gaim_input_remove(phb->inpa);
1542 gaim_debug_info("socks5 proxy", "Got auth response.\n"); 1772
1543 1773 if ((phb->read_buffer[0] != 0x01) || (phb->read_buffer[1] != 0x00)) {
1544 if (read(source, buf, 2) < 2) {
1545 close(source); 1774 close(source);
1546 1775 g_free(phb->read_buffer);
1776 phb->read_buffer = NULL;
1547 try_connect(phb); 1777 try_connect(phb);
1548 return; 1778 return;
1549 } 1779 }
1550 1780
1551 if ((buf[0] != 0x01) || (buf[1] != 0x00)) { 1781 g_free(phb->read_buffer);
1552 close(source); 1782 phb->read_buffer = NULL;
1553
1554 try_connect(phb);
1555 return;
1556 }
1557 1783
1558 s5_sendconnect(phb, source); 1784 s5_sendconnect(phb, source);
1559 } 1785 }
1560 1786
1561 static void hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd, unsigned char * response) 1787 static void hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd, unsigned char * response)
1602 } 1828 }
1603 1829
1604 static void 1830 static void
1605 s5_readchap(gpointer data, gint source, GaimInputCondition cond) 1831 s5_readchap(gpointer data, gint source, GaimInputCondition cond)
1606 { 1832 {
1607 unsigned char buf[260]; 1833 guchar *cmdbuf, *buf;
1608 unsigned char cmdbuf[20];
1609 struct PHB *phb = data; 1834 struct PHB *phb = data;
1610 1835 int len, navas, currentav;
1611 int navas, currentav; 1836
1612
1613 gaim_input_remove(phb->inpa);
1614 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Got CHAP response.\n"); 1837 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Got CHAP response.\n");
1615 1838
1616 if (read(source, cmdbuf, 2) < 2) { 1839 if (phb->read_buffer == NULL) {
1840 phb->read_buf_len = 20;
1841 phb->read_buffer = g_malloc(phb->read_buf_len);
1842 phb->read_len = 0;
1843 }
1844
1845 len = read(source, phb->read_buffer + phb->read_len,
1846 phb->read_buf_len - phb->read_len);
1847
1848 if(len < 0 && errno == EAGAIN)
1849 return;
1850 else if(len < 0) {
1617 close(source); 1851 close(source);
1618 1852 gaim_input_remove(phb->inpa);
1853 g_free(phb->read_buffer);
1854 phb->read_buffer = NULL;
1619 try_connect(phb); 1855 try_connect(phb);
1620 return; 1856 return;
1621 } 1857 }
1622 1858 phb->read_len += len;
1623 if (cmdbuf[0] != 0x01) { 1859
1860 if (phb->read_len < 2)
1861 return;
1862
1863 cmdbuf = phb->read_buffer;
1864
1865 if (*cmdbuf != 0x01) {
1624 close(source); 1866 close(source);
1625 1867 gaim_input_remove(phb->inpa);
1868 g_free(phb->read_buffer);
1869 phb->read_buffer = NULL;
1626 try_connect(phb); 1870 try_connect(phb);
1627 return; 1871 return;
1628 } 1872 }
1629 1873 cmdbuf++;
1630 navas = cmdbuf[1]; 1874
1875 navas = *cmdbuf;
1876 cmdbuf++;
1631 1877
1632 for (currentav = 0; currentav < navas; currentav++) { 1878 for (currentav = 0; currentav < navas; currentav++) {
1633 if (read(source, cmdbuf, 2) < 2) { 1879 if (phb->read_len - (cmdbuf - phb->read_buffer) < 2)
1634 close(source);
1635
1636 try_connect(phb);
1637 return; 1880 return;
1638 } 1881 if (phb->read_len - (cmdbuf - phb->read_buffer) < cmdbuf[1])
1639 if (read(source, buf, cmdbuf[1]) < cmdbuf[1]) {
1640 close(source);
1641
1642 try_connect(phb);
1643 return; 1882 return;
1644 } 1883 buf = cmdbuf + 2;
1645 switch (cmdbuf[0]) { 1884 switch (cmdbuf[0]) {
1646 case 0x00: 1885 case 0x00:
1647 /* Did auth work? */ 1886 /* Did auth work? */
1648 if (buf[0] == 0x00) { 1887 if (buf[0] == 0x00) {
1888 gaim_input_remove(phb->inpa);
1889 g_free(phb->read_buffer);
1890 phb->read_buffer = NULL;
1649 /* Success */ 1891 /* Success */
1650 s5_sendconnect(phb, source); 1892 s5_sendconnect(phb, source);
1651 return; 1893 return;
1652 } else { 1894 } else {
1653 /* Failure */ 1895 /* Failure */
1654 gaim_debug_warning("proxy", "socks5 CHAP authentication " 1896 gaim_debug_warning("proxy",
1655 "failed. Disconnecting..."); 1897 "socks5 CHAP authentication "
1898 "failed. Disconnecting...");
1656 close(source); 1899 close(source);
1657 1900 gaim_input_remove(phb->inpa);
1901 g_free(phb->read_buffer);
1902 phb->read_buffer = NULL;
1658 try_connect(phb); 1903 try_connect(phb);
1659 return; 1904 return;
1660 } 1905 }
1661 break; 1906 break;
1662 case 0x03: 1907 case 0x03:
1663 /* Server wants our credentials */ 1908 /* Server wants our credentials */
1909
1910 phb->write_buf_len = 16 + 4;
1911 phb->write_buffer = g_malloc(phb->write_buf_len);
1912 phb->written_len = 0;
1913
1664 hmacmd5_chap(buf, cmdbuf[1], 1914 hmacmd5_chap(buf, cmdbuf[1],
1665 gaim_proxy_info_get_password(phb->gpi), 1915 gaim_proxy_info_get_password(phb->gpi),
1666 buf + 4); 1916 phb->write_buffer + 4);
1667 buf[0] = 0x01; 1917 phb->write_buffer[0] = 0x01;
1668 buf[1] = 0x01; 1918 phb->write_buffer[1] = 0x01;
1669 buf[2] = 0x04; 1919 phb->write_buffer[2] = 0x04;
1670 buf[3] = 0x10; 1920 phb->write_buffer[3] = 0x10;
1671 if (write(source, buf, 20) < 20) { 1921
1672 close(source); 1922 gaim_input_remove(phb->inpa);
1673 1923 g_free(phb->read_buffer);
1674 try_connect(phb); 1924 phb->read_buffer = NULL;
1675 return; 1925
1676 } 1926 phb->read_cb = s5_readchap;
1927
1928 phb->inpa = gaim_input_add(source,
1929 GAIM_INPUT_WRITE, proxy_do_write, phb);
1930
1931 proxy_do_write(phb, source, GAIM_INPUT_WRITE);
1677 break; 1932 break;
1678 case 0x11: 1933 case 0x11:
1679 /* Server wants to select an algorithm */ 1934 /* Server wants to select an algorithm */
1680 if (buf[0] != 0x85) { 1935 if (buf[0] != 0x85) {
1681 /* Only currently support HMAC-MD5 */ 1936 /* Only currently support HMAC-MD5 */
1682 gaim_debug_warning("proxy", "Server tried to select an " 1937 gaim_debug_warning("proxy",
1683 "algorithm that we did not advertise " 1938 "Server tried to select an "
1684 "as supporting. This is a violation " 1939 "algorithm that we did not advertise "
1685 "of the socks5 CHAP specification. " 1940 "as supporting. This is a violation "
1686 "Disconnecting..."); 1941 "of the socks5 CHAP specification. "
1942 "Disconnecting...");
1687 close(source); 1943 close(source);
1688 1944 gaim_input_remove(phb->inpa);
1945 g_free(phb->read_buffer);
1946 phb->read_buffer = NULL;
1689 try_connect(phb); 1947 try_connect(phb);
1690 return; 1948 return;
1691 } 1949 }
1692 break; 1950 break;
1693 } 1951 }
1952 cmdbuf = buf + cmdbuf[1];
1694 } 1953 }
1695 /* Fell through. We ran out of CHAP events to process, but haven't 1954 /* Fell through. We ran out of CHAP events to process, but haven't
1696 * succeeded or failed authentication - there may be more to come. 1955 * succeeded or failed authentication - there may be more to come.
1697 * If this is the case, come straight back here. */ 1956 * If this is the case, come straight back here. */
1698 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readchap, phb);
1699 } 1957 }
1700 1958
1701 static void 1959 static void
1702 s5_canread(gpointer data, gint source, GaimInputCondition cond) 1960 s5_canread(gpointer data, gint source, GaimInputCondition cond)
1703 { 1961 {
1704 unsigned char buf[512];
1705 struct PHB *phb = data; 1962 struct PHB *phb = data;
1963 int len;
1964
1965 if (phb->read_buffer == NULL) {
1966 phb->read_buf_len = 2;
1967 phb->read_buffer = g_malloc(phb->read_buf_len);
1968 phb->read_len = 0;
1969 }
1970
1971 gaim_debug_info("socks5 proxy", "Able to read.\n");
1972
1973 len = read(source, phb->read_buffer + phb->read_len,
1974 phb->read_buf_len - phb->read_len);
1975 if(len < 0 && errno == EAGAIN)
1976 return;
1977 else if(len < 0) {
1978 close(source);
1979 gaim_input_remove(phb->inpa);
1980 g_free(phb->read_buffer);
1981 phb->read_buffer = NULL;
1982 try_connect(phb);
1983 return;
1984 }
1985 phb->read_len += len;
1986
1987 if (phb->read_len < 2)
1988 return;
1706 1989
1707 gaim_input_remove(phb->inpa); 1990 gaim_input_remove(phb->inpa);
1708 gaim_debug_info("socks5 proxy", "Able to read.\n"); 1991
1709 1992 if ((phb->read_buffer[0] != 0x05) || (phb->read_buffer[1] == 0xff)) {
1710 if (read(source, buf, 2) < 2) {
1711 close(source); 1993 close(source);
1712 1994 g_free(phb->read_buffer);
1995 phb->read_buffer = NULL;
1713 try_connect(phb); 1996 try_connect(phb);
1714 return; 1997 return;
1715 } 1998 }
1716 1999
1717 if ((buf[0] != 0x05) || (buf[1] == 0xff)) { 2000 if (phb->read_buffer[1] == 0x02) {
1718 close(source); 2001 gsize i, j;
1719
1720 try_connect(phb);
1721 return;
1722 }
1723
1724 if (buf[1] == 0x02) {
1725 unsigned int i, j;
1726 const char *u, *p; 2002 const char *u, *p;
1727 2003
1728 u = gaim_proxy_info_get_username(phb->gpi); 2004 u = gaim_proxy_info_get_username(phb->gpi);
1729 p = gaim_proxy_info_get_password(phb->gpi); 2005 p = gaim_proxy_info_get_password(phb->gpi);
1730 2006
1731 i = (u == NULL) ? 0 : strlen(u); 2007 i = (u == NULL) ? 0 : strlen(u);
1732 j = (p == NULL) ? 0 : strlen(p); 2008 j = (p == NULL) ? 0 : strlen(p);
1733 2009
1734 buf[0] = 0x01; /* version 1 */ 2010 phb->write_buf_len = 1 + 1 + i + 1 + j;
1735 buf[1] = i; 2011 phb->write_buffer = g_malloc(phb->write_buf_len);
2012 phb->written_len = 0;
2013
2014 phb->write_buffer[0] = 0x01; /* version 1 */
2015 phb->write_buffer[1] = i;
1736 if (u != NULL) 2016 if (u != NULL)
1737 memcpy(buf + 2, u, i); 2017 memcpy(phb->write_buffer + 2, u, i);
1738 buf[2 + i] = j; 2018 phb->write_buffer[2 + i] = j;
1739 if (p != NULL) 2019 if (p != NULL)
1740 memcpy(buf + 2 + i + 1, p, j); 2020 memcpy(phb->write_buffer + 2 + i + 1, p, j);
1741 2021
1742 if (write(source, buf, 3 + i + j) < 3 + i + j) { 2022 g_free(phb->read_buffer);
1743 close(source); 2023 phb->read_buffer = NULL;
1744 2024
1745 try_connect(phb); 2025 phb->read_cb = s5_readauth;
1746 return; 2026
1747 } 2027 phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE,
1748 2028 proxy_do_write, phb);
1749 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readauth, phb); 2029
1750 } else if (buf[1] == 0x03) { 2030 proxy_do_write(phb, source, GAIM_INPUT_WRITE);
1751 unsigned int userlen; 2031
2032 return;
2033 } else if (phb->read_buffer[1] == 0x03) {
2034 gsize userlen;
1752 userlen = strlen(gaim_proxy_info_get_username(phb->gpi)); 2035 userlen = strlen(gaim_proxy_info_get_username(phb->gpi));
1753 buf[0] = 0x01; 2036
1754 buf[1] = 0x02; 2037 phb->write_buf_len = 7 + userlen;
1755 buf[2] = 0x11; 2038 phb->write_buffer = g_malloc(phb->write_buf_len);
1756 buf[3] = 0x01; 2039 phb->written_len = 0;
1757 buf[4] = 0x85; 2040
1758 buf[5] = 0x02; 2041 phb->write_buffer[0] = 0x01;
1759 buf[6] = userlen; 2042 phb->write_buffer[1] = 0x02;
1760 memcpy(buf + 7, gaim_proxy_info_get_username(phb->gpi), userlen); 2043 phb->write_buffer[2] = 0x11;
1761 if (write(source, buf, 7 + userlen) < 7 + userlen) { 2044 phb->write_buffer[3] = 0x01;
1762 close(source); 2045 phb->write_buffer[4] = 0x85;
1763 2046 phb->write_buffer[5] = 0x02;
1764 try_connect(phb); 2047 phb->write_buffer[6] = userlen;
1765 return; 2048 memcpy(phb->write_buffer + 7,
1766 } 2049 gaim_proxy_info_get_username(phb->gpi), userlen);
1767 2050
1768 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readchap, phb); 2051 g_free(phb->read_buffer);
1769 } 2052 phb->read_buffer = NULL;
1770 else { 2053
2054 phb->read_cb = s5_readchap;
2055
2056 phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE,
2057 proxy_do_write, phb);
2058
2059 proxy_do_write(phb, source, GAIM_INPUT_WRITE);
2060
2061 return;
2062 } else {
2063 g_free(phb->read_buffer);
2064 phb->read_buffer = NULL;
2065
1771 s5_sendconnect(phb, source); 2066 s5_sendconnect(phb, source);
1772 } 2067 }
1773 } 2068 }
1774 2069
1775 static void 2070 static void
1776 s5_canwrite(gpointer data, gint source, GaimInputCondition cond) 2071 s5_canwrite(gpointer data, gint source, GaimInputCondition cond)
1777 { 2072 {
1778 unsigned char buf[512]; 2073 unsigned char buf[5];
1779 int i; 2074 int i;
1780 struct PHB *phb = data; 2075 struct PHB *phb = data;
1781 socklen_t len; 2076 socklen_t len;
1782 int error = ETIMEDOUT; 2077 int error = ETIMEDOUT;
1783 2078
1791 close(source); 2086 close(source);
1792 2087
1793 try_connect(phb); 2088 try_connect(phb);
1794 return; 2089 return;
1795 } 2090 }
1796 fcntl(source, F_SETFL, 0);
1797 2091
1798 i = 0; 2092 i = 0;
1799 buf[0] = 0x05; /* SOCKS version 5 */ 2093 buf[0] = 0x05; /* SOCKS version 5 */
1800 2094
1801 if (gaim_proxy_info_get_username(phb->gpi) != NULL) { 2095 if (gaim_proxy_info_get_username(phb->gpi) != NULL) {
1809 buf[1] = 0x01; 2103 buf[1] = 0x01;
1810 buf[2] = 0x00; 2104 buf[2] = 0x00;
1811 i = 3; 2105 i = 3;
1812 } 2106 }
1813 2107
1814 if (write(source, buf, i) < i) { 2108 phb->write_buf_len = i;
1815 gaim_debug_error("socks5 proxy", "Unable to write\n"); 2109 phb->write_buffer = g_malloc(phb->write_buf_len);
1816 close(source); 2110 memcpy(phb->write_buffer, buf, i);
1817 2111
1818 try_connect(phb); 2112 phb->read_cb = s5_canread;
1819 return; 2113
1820 } 2114 phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, phb);
1821 2115 proxy_do_write(phb, source, GAIM_INPUT_WRITE);
1822 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb);
1823 } 2116 }
1824 2117
1825 static int 2118 static int
1826 proxy_connect_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) 2119 proxy_connect_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
1827 { 2120 {
1865 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 2158 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
1866 close(fd); 2159 close(fd);
1867 return -1; 2160 return -1;
1868 } 2161 }
1869 2162
1870 fcntl(fd, F_SETFL, 0);
1871 s5_canwrite(phb, fd, GAIM_INPUT_WRITE); 2163 s5_canwrite(phb, fd, GAIM_INPUT_WRITE);
1872 } 2164 }
1873 2165
1874 return fd; 2166 return fd;
1875 } 2167 }
2046 g_free(phb); 2338 g_free(phb);
2047 return -1; 2339 return -1;
2048 } 2340 }
2049 2341
2050 return gaim_gethostbyname_async(connecthost, connectport, 2342 return gaim_gethostbyname_async(connecthost, connectport,
2051 connection_host_resolved, phb); 2343 connection_host_resolved, phb);
2052 } 2344 }
2053 2345
2054 int 2346 int
2055 gaim_proxy_connect_socks5(GaimProxyInfo *gpi, const char *host, int port, 2347 gaim_proxy_connect_socks5(GaimProxyInfo *gpi, const char *host, int port,
2056 GaimInputFunction func, gpointer data) 2348 GaimInputFunction func, gpointer data)
2062 phb->func = func; 2354 phb->func = func;
2063 phb->data = data; 2355 phb->data = data;
2064 phb->host = g_strdup(host); 2356 phb->host = g_strdup(host);
2065 phb->port = port; 2357 phb->port = port;
2066 2358
2067 return gaim_gethostbyname_async(gaim_proxy_info_get_host(gpi), gaim_proxy_info_get_port(gpi), 2359 return gaim_gethostbyname_async(gaim_proxy_info_get_host(gpi),
2068 connection_host_resolved, phb); 2360 gaim_proxy_info_get_port(gpi), connection_host_resolved, phb);
2069 } 2361 }
2070 2362
2071 2363
2072 static void 2364 static void
2073 proxy_pref_cb(const char *name, GaimPrefType type, 2365 proxy_pref_cb(const char *name, GaimPrefType type,
2119 gaim_prefs_add_string("/core/proxy/username", ""); 2411 gaim_prefs_add_string("/core/proxy/username", "");
2120 gaim_prefs_add_string("/core/proxy/password", ""); 2412 gaim_prefs_add_string("/core/proxy/password", "");
2121 2413
2122 /* Setup callbacks for the preferences. */ 2414 /* Setup callbacks for the preferences. */
2123 handle = gaim_proxy_get_handle(); 2415 handle = gaim_proxy_get_handle();
2124 gaim_prefs_connect_callback(handle, "/core/proxy/type", 2416 gaim_prefs_connect_callback(handle, "/core/proxy/type", proxy_pref_cb,
2125 proxy_pref_cb, NULL); 2417 NULL);
2126 gaim_prefs_connect_callback(handle, "/core/proxy/host", 2418 gaim_prefs_connect_callback(handle, "/core/proxy/host", proxy_pref_cb,
2127 proxy_pref_cb, NULL); 2419 NULL);
2128 gaim_prefs_connect_callback(handle, "/core/proxy/port", 2420 gaim_prefs_connect_callback(handle, "/core/proxy/port", proxy_pref_cb,
2129 proxy_pref_cb, NULL); 2421 NULL);
2130 gaim_prefs_connect_callback(handle, "/core/proxy/username", 2422 gaim_prefs_connect_callback(handle, "/core/proxy/username",
2131 proxy_pref_cb, NULL); 2423 proxy_pref_cb, NULL);
2132 gaim_prefs_connect_callback(handle, "/core/proxy/password", 2424 gaim_prefs_connect_callback(handle, "/core/proxy/password",
2133 proxy_pref_cb, NULL); 2425 proxy_pref_cb, NULL);
2134 #ifdef _WIN32 2426 #ifdef _WIN32
2135 if(!g_thread_supported()) 2427 if(!g_thread_supported())
2136 g_thread_init(NULL); 2428 g_thread_init(NULL);
2137 #endif 2429 #endif
2138 } 2430 }