Mercurial > pidgin.yaz
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 } |