Mercurial > pidgin.yaz
comparison src/proxy.c @ 4366:7ba9b56a8796
[gaim-migrate @ 4632]
Ok, so this is a patch by Nicolas Lichtmaier (niqueco). You've probably noticed
that when you try to sign an account on, the UI freezes for a while (esp. on poor
connections). This was because gethostbyname() blocks. Nicolas's patch here forks
a new process to resolve host names and returns them in a pipe. It makes things
smoother and faster. Thanks so much, Nicolas.
committer: Tailor Script <tailor@pidgin.im>
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Mon, 20 Jan 2003 19:26:56 +0000 |
parents | 43e396e94095 |
children | 101fa831f52f |
comparison
equal
deleted
inserted
replaced
4365:6e96ced6fb78 | 4366:7ba9b56a8796 |
---|---|
35 #include <sys/socket.h> | 35 #include <sys/socket.h> |
36 #include <netdb.h> | 36 #include <netdb.h> |
37 #include <netinet/in.h> | 37 #include <netinet/in.h> |
38 #include <arpa/inet.h> | 38 #include <arpa/inet.h> |
39 #include <unistd.h> | 39 #include <unistd.h> |
40 #include <signal.h> | |
40 #else | 41 #else |
41 #include <winsock.h> | 42 #include <winsock.h> |
42 #endif | 43 #endif |
43 | 44 |
44 #include <fcntl.h> | 45 #include <fcntl.h> |
53 #define GAIM_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) | 54 #define GAIM_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) |
54 #define GAIM_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) | 55 #define GAIM_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) |
55 | 56 |
56 char proxyhost[128] = { 0 }; | 57 char proxyhost[128] = { 0 }; |
57 int proxyport = 0; | 58 int proxyport = 0; |
58 int proxytype = 0; | 59 proxytype_t proxytype = PROXY_NONE; |
59 char proxyuser[128] = { 0 }; | 60 char proxyuser[128] = { 0 }; |
60 char proxypass[128] = { 0 }; | 61 char proxypass[128] = { 0 }; |
61 | 62 |
62 struct PHB { | 63 struct PHB { |
63 GaimInputFunction func; | 64 GaimInputFunction func; |
64 gpointer data; | 65 gpointer data; |
65 char *host; | 66 char *host; |
66 int port; | 67 int port; |
67 gint inpa; | 68 gint inpa; |
69 proxytype_t proxytype; | |
68 }; | 70 }; |
69 | 71 |
70 typedef struct _GaimIOClosure { | 72 typedef struct _GaimIOClosure { |
71 GaimInputFunction function; | 73 GaimInputFunction function; |
72 guint result; | 74 guint result; |
127 /* debug_printf("CLOSURE: removing input watcher %d\n", tag); */ | 129 /* debug_printf("CLOSURE: removing input watcher %d\n", tag); */ |
128 if (tag > 0) | 130 if (tag > 0) |
129 g_source_remove(tag); | 131 g_source_remove(tag); |
130 } | 132 } |
131 | 133 |
132 static struct sockaddr_in *gaim_gethostbyname(char *host, int port) | 134 |
133 { | 135 typedef void (*dns_callback_t)(struct sockaddr *addr, size_t addrlen, |
134 static struct sockaddr_in sin; | 136 gpointer data, const char *error_message); |
135 | 137 |
136 if (!inet_aton(host, &sin.sin_addr)) { | 138 #ifdef __unix__ |
139 | |
140 /* This structure represents both a pending DNS request and | |
141 * a free child process. | |
142 */ | |
143 typedef struct { | |
144 char *host; | |
145 int port; | |
146 int socktype; | |
147 dns_callback_t callback; | |
148 gpointer data; | |
149 gint inpa; | |
150 int fd_in, fd_out; | |
151 pid_t dns_pid; | |
152 } pending_dns_request_t; | |
153 | |
154 static GSList *free_dns_children = NULL; | |
155 static GQueue *queued_requests = NULL; | |
156 | |
157 static int number_of_dns_children = 0; | |
158 | |
159 const int MAX_DNS_CHILDREN = 2; | |
160 | |
161 typedef struct { | |
162 char hostname[512]; | |
163 int port; | |
164 int socktype; | |
165 } dns_params_t; | |
166 | |
167 typedef struct { | |
168 dns_params_t params; | |
169 dns_callback_t callback; | |
170 gpointer data; | |
171 } queued_dns_request_t; | |
172 | |
173 static int send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) | |
174 { | |
175 char ch; | |
176 int rc; | |
177 | |
178 /* Are you alive? */ | |
179 if(kill(req->dns_pid, 0) != 0) { | |
180 debug_printf("DNS child %d no longer exists\n", req->dns_pid); | |
181 return -1; | |
182 } | |
183 | |
184 /* Let's contact this lost child! */ | |
185 rc = write(req->fd_in, dns_params, sizeof(*dns_params)); | |
186 if(rc<0) { | |
187 debug_printf("Error writing to DNS child %d: %s\n", req->dns_pid, strerror(errno)); | |
188 return -1; | |
189 } | |
190 | |
191 g_return_val_if_fail(rc == sizeof(*dns_params), -1); | |
192 | |
193 /* Did you hear me? (This avoids some race conditions) */ | |
194 rc = read(req->fd_out, &ch, 1); | |
195 if(rc != 1 || ch!='Y') { | |
196 debug_printf("DNS child %d not responding: killing it!\n", req->dns_pid); | |
197 kill(req->dns_pid, SIGKILL); | |
198 return -1; | |
199 } | |
200 | |
201 debug_printf("Successfuly sent DNS request to child %d\n", req->dns_pid); | |
202 return 0; | |
203 } | |
204 | |
205 static void host_resolved(gpointer data, gint source, GaimInputCondition cond); | |
206 | |
207 static void release_dns_child(pending_dns_request_t *req) | |
208 { | |
209 g_free(req->host); | |
210 | |
211 if(queued_requests && !g_queue_is_empty(queued_requests)) { | |
212 queued_dns_request_t *r = g_queue_pop_head(queued_requests); | |
213 req->host = g_strdup(r->params.hostname); | |
214 req->port = r->params.port; | |
215 req->socktype = r->params.socktype; | |
216 req->callback = r->callback; | |
217 req->data = r->data; | |
218 | |
219 debug_printf("Processing queued DNS query for '%s' with child %d\n", req->host, req->dns_pid); | |
220 | |
221 if(send_dns_request_to_child(req, &(r->params)) != 0) { | |
222 g_free(req->host); | |
223 g_free(req); | |
224 req = NULL; | |
225 number_of_dns_children--; | |
226 | |
227 debug_printf("Intent of process queued query of '%s' failed, requeing...\n", | |
228 r->params.hostname); | |
229 g_queue_push_head(queued_requests, r); | |
230 } else { | |
231 req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); | |
232 g_free(r); | |
233 } | |
234 | |
235 } else { | |
236 req->host = NULL; | |
237 req->callback = NULL; | |
238 req->data = NULL; | |
239 free_dns_children = g_slist_append(free_dns_children, req); | |
240 } | |
241 } | |
242 | |
243 static void host_resolved(gpointer data, gint source, GaimInputCondition cond) | |
244 { | |
245 pending_dns_request_t *req = (pending_dns_request_t*)data; | |
246 int rc, err; | |
247 struct sockaddr *addr = NULL; | |
248 socklen_t addrlen; | |
249 | |
250 debug_printf("Host '%s' resolved\n", req->host); | |
251 gaim_input_remove(req->inpa); | |
252 | |
253 rc=read(req->fd_out, &err, sizeof(err)); | |
254 if((rc==4) && (err!=0)) { | |
255 char message[1024]; | |
256 g_snprintf(message, sizeof(message), "DNS error: %s (pid=%d)", | |
257 #ifdef HAVE_GETADDRINFO | |
258 gai_strerror(err), | |
259 #else | |
260 hstrerror(err), | |
261 #endif | |
262 req->dns_pid); | |
263 debug_printf("%s\n",message); | |
264 req->callback(NULL, 0, req->data, message); | |
265 release_dns_child(req); | |
266 return; | |
267 } | |
268 if(G_LIKELY(rc>0)) { | |
269 rc=read(req->fd_out, &addrlen, sizeof(addrlen)); | |
270 if(G_LIKELY(rc>0)) { | |
271 addr=g_malloc(addrlen); | |
272 rc=read(req->fd_out, addr, addrlen); | |
273 } | |
274 } | |
275 if(G_UNLIKELY(rc==-1)) { | |
276 char message[1024]; | |
277 g_snprintf(message, sizeof(message), "Error reading from DNS child: %s",strerror(errno)); | |
278 debug_printf("%s\n",message); | |
279 close(req->fd_out); | |
280 req->callback(NULL, 0, req->data, message); | |
281 g_free(req->host); | |
282 g_free(req); | |
283 number_of_dns_children--; | |
284 return; | |
285 } | |
286 if(G_UNLIKELY(rc==0)) { | |
287 char message[1024]; | |
288 g_snprintf(message, sizeof(message), "EOF reading from DNS child"); | |
289 close(req->fd_out); | |
290 debug_printf("%s\n",message); | |
291 req->callback(NULL, 0, req->data, message); | |
292 g_free(req->host); | |
293 g_free(req); | |
294 number_of_dns_children--; | |
295 return; | |
296 } | |
297 | |
298 /* wait4(req->dns_pid, NULL, WNOHANG, NULL); */ | |
299 | |
300 req->callback(addr, addrlen, req->data, NULL); | |
301 | |
302 g_free(addr); | |
303 | |
304 release_dns_child(req); | |
305 } | |
306 | |
307 static void trap_gdb_bug() | |
308 { | |
309 const char *message = | |
310 "Gaim's DNS child got a SIGTRAP signal. \n" | |
311 "This can be caused by trying to run gaim into gdb.\n" | |
312 "There's a known gdb bug which prevent this, supposedly gaim\n" | |
313 "should have detected you were using gdb and use an ugly hack,\n" | |
314 "check cope_with_gdb_brokenness() in proxy.c.\n\n" | |
315 "For more info about the bug check http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; | |
316 fputs("\n* * *\n",stderr); | |
317 fputs(message,stderr); | |
318 fputs("* * *\n\n",stderr); | |
319 execlp("xmessage","xmessage","-center", message, NULL); | |
320 _exit(1); | |
321 } | |
322 | |
323 static void cope_with_gdb_brokenness() | |
324 { | |
325 static gboolean already_done = FALSE; | |
326 char s[300], e[300]; | |
327 int n; | |
328 pid_t ppid; | |
329 | |
330 #ifdef __linux__ | |
331 if(already_done) | |
332 return; | |
333 already_done = TRUE; | |
334 ppid = getppid(); | |
335 sprintf(s,"/proc/%d/exe", ppid); | |
336 n = readlink(s, e, sizeof(e)); | |
337 e[MAX(n,sizeof(e)-1)] = '\0'; | |
338 | |
339 if(strstr(e,"gdb")) { | |
340 debug_printf("Debugger detected, performing useless query...\n"); | |
341 gethostbyname("x.x.x.x.x"); | |
342 } | |
343 #endif | |
344 } | |
345 | |
346 int gaim_gethostbyname_async(const char *hostname, int port, int socktype, dns_callback_t callback, gpointer data) | |
347 { | |
348 pending_dns_request_t *req = NULL; | |
349 dns_params_t dns_params; | |
350 | |
351 strncpy(dns_params.hostname, hostname, sizeof(dns_params.hostname)-1); | |
352 dns_params.hostname[sizeof(dns_params.hostname)-1] = '\0'; | |
353 dns_params.port = port; | |
354 dns_params.socktype = socktype; | |
355 | |
356 /* Is there a free available child? */ | |
357 while(free_dns_children && !req) { | |
358 GSList *l = free_dns_children; | |
359 free_dns_children = g_slist_remove_link(free_dns_children, l); | |
360 req = l->data; | |
361 g_slist_free(l); | |
362 | |
363 if(send_dns_request_to_child(req, &dns_params) != 0) { | |
364 g_free(req); | |
365 req = NULL; | |
366 number_of_dns_children--; | |
367 continue; | |
368 } | |
369 | |
370 } | |
371 | |
372 if(!req) { | |
373 int child_out[2], child_in[2]; | |
374 | |
375 if(number_of_dns_children >= MAX_DNS_CHILDREN) { | |
376 queued_dns_request_t *r = g_new(queued_dns_request_t, 1); | |
377 memcpy(&(r->params), &dns_params, sizeof(dns_params)); | |
378 r->callback = callback; | |
379 r->data = data; | |
380 if(!queued_requests) | |
381 queued_requests = g_queue_new(); | |
382 g_queue_push_tail(queued_requests, r); | |
383 debug_printf("DNS query for '%s' queued\n", hostname); | |
384 return 0; | |
385 } | |
386 | |
387 if(pipe(child_out) || pipe(child_in)) { | |
388 debug_printf("Could not create pipes: %s",strerror(errno)); | |
389 return -1; | |
390 } | |
391 | |
392 /* We need to create a new child. */ | |
393 req = g_new(pending_dns_request_t,1); | |
394 | |
395 cope_with_gdb_brokenness(); | |
396 | |
397 req->dns_pid=fork(); | |
398 if(req->dns_pid==0) { | |
399 const int zero = 0; | |
400 #ifdef HAVE_GETADDRINFO | |
401 struct addrinfo hints, *res; | |
402 char servname[20]; | |
403 int rc; | |
404 #else | |
405 struct sockaddr_in sin; | |
406 const socklen_t addrlen = sizeof(sin); | |
407 #endif | |
408 #ifdef HAVE_SIGNAL_H | |
409 signal(SIGHUP, SIG_DFL); | |
410 signal(SIGINT, SIG_DFL); | |
411 signal(SIGQUIT, SIG_DFL); | |
412 signal(SIGCHLD, SIG_DFL); | |
413 signal(SIGTERM, SIG_DFL); | |
414 signal(SIGTRAP, trap_gdb_bug); | |
415 #endif | |
416 | |
417 | |
418 close(child_out[0]); | |
419 close(child_in[1]); | |
420 | |
421 while(1) { | |
422 if(dns_params.hostname[0] == '\0') { | |
423 const char Y = 'Y'; | |
424 fd_set fds; | |
425 struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; | |
426 FD_ZERO(&fds); | |
427 FD_SET(child_in[0], &fds); | |
428 rc = select(child_in[0]+1, &fds, NULL, NULL, &tv); | |
429 if(!rc) { | |
430 if(opt_debug) | |
431 fprintf(stderr,"dns[%d]: nobody needs me... =(\n", getpid()); | |
432 break; | |
433 } | |
434 rc = read(child_in[0], &dns_params, sizeof(dns_params)); | |
435 if(rc < 0) { | |
436 perror("read()"); | |
437 break; | |
438 } | |
439 if(rc==0) { | |
440 if(opt_debug) | |
441 fprintf(stderr,"dns[%d]: Ops, father has gone, wait for me, wait...!\n", getpid()); | |
442 _exit(0); | |
443 } | |
444 if(dns_params.hostname[0] == '\0') { | |
445 fprintf(stderr, "dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); | |
446 _exit(1); | |
447 } | |
448 write(child_out[1], &Y, 1); | |
449 } | |
450 | |
451 #ifdef HAVE_GETADDRINFO | |
452 g_snprintf(servname, sizeof(servname), "%d", dns_params.port); | |
453 memset(&hints,0,sizeof(hints)); | |
454 hints.ai_socktype = dns_params.socktype; | |
455 rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); | |
456 if(rc) { | |
457 write(child_out[1], &rc, sizeof(int)); | |
458 close(child_out[1]); | |
459 if(opt_debug) | |
460 fprintf(stderr,"dns[%d] Error: getaddrinfo returned %d\n", | |
461 getpid(), rc); | |
462 dns_params.hostname[0] = '\0'; | |
463 continue; | |
464 } | |
465 write(child_out[1], &zero, sizeof(zero)); | |
466 write(child_out[1], &(res->ai_addrlen), sizeof(res->ai_addrlen)); | |
467 write(child_out[1], res->ai_addr, res->ai_addrlen); | |
468 freeaddrinfo(res); | |
469 #else | |
470 if (!inet_aton(hostname, &sin.sin_addr)) { | |
471 struct hostent *hp; | |
472 if(!(hp = gethostbyname(dns_params.hostname))) { | |
473 write(child_out[1], &h_errno, sizeof(int)); | |
474 close(child_out[1]); | |
475 if(opt_debug) | |
476 fprintf(stderr,"DNS Error: %s\n",hstrerror(h_errno)); | |
477 _exit(0); | |
478 } | |
479 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
480 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
481 sin.sin_family = hp->h_addrtype; | |
482 } else | |
483 sin.sin_family = AF_INET; | |
484 sin.sin_port = htons(dns_params.port); | |
485 write(child_out[1], &zero, sizeof(zero)); | |
486 write(child_out[1], &addrlen, sizeof(addrlen)); | |
487 write(child_out[1], &sin, addrlen); | |
488 #endif | |
489 dns_params.hostname[0] = '\0'; | |
490 } | |
491 close(child_out[1]); | |
492 close(child_in[0]); | |
493 _exit(0); | |
494 } | |
495 close(child_out[1]); | |
496 close(child_in[0]); | |
497 if(req->dns_pid==-1) { | |
498 debug_printf("Could not create child process for DNS: %s\n",strerror(errno)); | |
499 g_free(req); | |
500 return -1; | |
501 } | |
502 req->fd_in = child_in[1]; | |
503 req->fd_out = child_out[0]; | |
504 number_of_dns_children++; | |
505 debug_printf("Created new DNS child %d, there are now %d children.\n", | |
506 req->dns_pid, number_of_dns_children); | |
507 } | |
508 req->host=g_strdup(hostname); | |
509 req->port=port; | |
510 req->callback=callback; | |
511 req->data=data; | |
512 req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); | |
513 return 0; | |
514 } | |
515 #else | |
516 | |
517 typedef struct { | |
518 gpointer data; | |
519 size_t addrlen; | |
520 struct sockaddr *addr; | |
521 dns_callback_t callback; | |
522 } pending_dns_request_t; | |
523 | |
524 static gboolean host_resolved(gpointer data) | |
525 { | |
526 pending_dns_request_t *req = (pending_dns_request_t*)data; | |
527 req->callback(req->addr, req->addrlen, req->data, NULL); | |
528 g_free(req->addr); | |
529 g_free(req); | |
530 return FALSE; | |
531 } | |
532 | |
533 int gaim_gethostbyname_async(const char *hostname, int port, int socktype, dns_callback_t callback, gpointer data) | |
534 { | |
535 struct sockaddr_in sin; | |
536 pending_dns_request_t *req; | |
537 | |
538 if (!inet_aton(hostname, &sin.sin_addr)) { | |
137 struct hostent *hp; | 539 struct hostent *hp; |
138 if(!(hp = gethostbyname(host))) { | 540 if(!(hp = gethostbyname(hostname))) { |
139 debug_printf("gaim_gethostbyname(\"%s\", %d) failed: %s", | 541 debug_printf("gaim_gethostbyname(\"%s\", %d) failed: %s", |
140 host, port, hstrerror(h_errno)); | 542 hostname, port, hstrerror(h_errno)); |
141 return NULL; | 543 return -1; |
142 } | 544 } |
143 memset(&sin, 0, sizeof(struct sockaddr_in)); | 545 memset(&sin, 0, sizeof(struct sockaddr_in)); |
144 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | 546 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); |
145 sin.sin_family = hp->h_addrtype; | 547 sin.sin_family = hp->h_addrtype; |
146 } else | 548 } else |
147 sin.sin_family = AF_INET; | 549 sin.sin_family = AF_INET; |
148 sin.sin_port = htons(port); | 550 sin.sin_port = htons(port); |
149 | 551 |
150 return &sin; | 552 req = g_new(pending_dns_request_t, 1); |
151 } | 553 req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin)); |
554 req->addrlen = sizeof(sin); | |
555 req->data = data; | |
556 req->callback = callback; | |
557 g_timeout_add(10, host_resolved, req); | |
558 return 0; | |
559 } | |
560 | |
561 #endif | |
152 | 562 |
153 static void no_one_calls(gpointer data, gint source, GaimInputCondition cond) | 563 static void no_one_calls(gpointer data, gint source, GaimInputCondition cond) |
154 { | 564 { |
155 struct PHB *phb = data; | 565 struct PHB *phb = data; |
156 unsigned int len; | 566 unsigned int len; |
161 len = sizeof(error); | 571 len = sizeof(error); |
162 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { | 572 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { |
163 close(source); | 573 close(source); |
164 gaim_input_remove(phb->inpa); | 574 gaim_input_remove(phb->inpa); |
165 phb->func(phb->data, -1, GAIM_INPUT_READ); | 575 phb->func(phb->data, -1, GAIM_INPUT_READ); |
576 g_free(phb->host); | |
166 g_free(phb); | 577 g_free(phb); |
167 return; | 578 return; |
168 } | 579 } |
169 fcntl(source, F_SETFL, 0); | 580 fcntl(source, F_SETFL, 0); |
170 gaim_input_remove(phb->inpa); | 581 gaim_input_remove(phb->inpa); |
171 phb->func(phb->data, source, GAIM_INPUT_READ); | 582 phb->func(phb->data, source, GAIM_INPUT_READ); |
583 g_free(phb->host); | |
172 g_free(phb); | 584 g_free(phb); |
173 } | 585 } |
174 | 586 |
175 static gboolean clean_connect(gpointer data) | 587 static gboolean clean_connect(gpointer data) |
176 { | 588 { |
177 struct PHB *phb = data; | 589 struct PHB *phb = data; |
178 | 590 |
179 phb->func(phb->data, phb->port, GAIM_INPUT_READ); | 591 phb->func(phb->data, phb->port, GAIM_INPUT_READ); |
592 g_free(phb->host); | |
180 g_free(phb); | 593 g_free(phb); |
181 | 594 |
182 return FALSE; | 595 return FALSE; |
183 } | 596 } |
184 | 597 |
185 | 598 |
186 static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb) | 599 static int proxy_connect_none(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) |
187 { | 600 { |
188 struct sockaddr_in *sin; | |
189 int fd = -1; | 601 int fd = -1; |
190 | 602 |
191 debug_printf("connecting to %s:%d with no proxy\n", host, port); | 603 debug_printf("connecting to %s:%d with no proxy\n", phb->host, phb->port); |
192 | 604 |
193 if (!(sin = gaim_gethostbyname(host, port))) { | 605 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) { |
194 debug_printf("gethostbyname failed\n"); | 606 debug_printf("unable to create socket: %s\n", strerror(errno)); |
607 g_free(phb->host); | |
195 g_free(phb); | 608 g_free(phb); |
196 return -1; | 609 return -1; |
197 } | 610 } |
198 | |
199 if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { | |
200 debug_printf("unable to create socket\n"); | |
201 g_free(phb); | |
202 return -1; | |
203 } | |
204 fcntl(fd, F_SETFL, O_NONBLOCK); | 611 fcntl(fd, F_SETFL, O_NONBLOCK); |
205 | 612 |
206 if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) { | 613 if (connect(fd, (struct sockaddr *)addr, addrlen) < 0) { |
207 if ((errno == EINPROGRESS) || (errno == EINTR)) { | 614 if ((errno == EINPROGRESS) || (errno == EINTR)) { |
208 debug_printf("Connect would have blocked\n"); | 615 debug_printf("Connect would have blocked\n"); |
209 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, phb); | 616 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, phb); |
210 } else { | 617 } else { |
211 debug_printf("connect failed (errno %d)\n", errno); | 618 debug_printf("connect failed (errno %d)\n", errno); |
212 close(fd); | 619 close(fd); |
620 g_free(phb->host); | |
213 g_free(phb); | 621 g_free(phb); |
214 return -1; | 622 return -1; |
215 } | 623 } |
216 } else { | 624 } else { |
217 unsigned int len; | 625 unsigned int len; |
219 debug_printf("Connect didn't block\n"); | 627 debug_printf("Connect didn't block\n"); |
220 len = sizeof(error); | 628 len = sizeof(error); |
221 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { | 629 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { |
222 debug_printf("getsockopt failed\n"); | 630 debug_printf("getsockopt failed\n"); |
223 close(fd); | 631 close(fd); |
632 g_free(phb->host); | |
224 g_free(phb); | 633 g_free(phb); |
225 return -1; | 634 return -1; |
226 } | 635 } |
227 fcntl(fd, F_SETFL, 0); | 636 fcntl(fd, F_SETFL, 0); |
228 phb->port = fd; /* bleh */ | 637 phb->port = fd; /* bleh */ |
239 | 648 |
240 static void http_canread(gpointer data, gint source, GaimInputCondition cond) | 649 static void http_canread(gpointer data, gint source, GaimInputCondition cond) |
241 { | 650 { |
242 int nlc = 0; | 651 int nlc = 0; |
243 int pos = 0; | 652 int pos = 0; |
653 int minor, major, status, error=0; | |
244 struct PHB *phb = data; | 654 struct PHB *phb = data; |
245 char inputline[8192]; | 655 char inputline[8192], *p; |
246 | 656 |
247 gaim_input_remove(phb->inpa); | 657 gaim_input_remove(phb->inpa); |
248 | 658 |
249 while ((nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) { | 659 while ((nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) { |
250 if (inputline[pos - 1] == '\n') | 660 if (inputline[pos - 1] == '\n') |
251 nlc++; | 661 nlc++; |
252 else if (inputline[pos - 1] != '\r') | 662 else if (inputline[pos - 1] != '\r') |
253 nlc = 0; | 663 nlc = 0; |
254 } | 664 } |
255 inputline[pos] = '\0'; | 665 inputline[pos] = '\0'; |
256 | 666 |
257 debug_printf("Proxy says: %s\n", inputline); | 667 error = strncmp(inputline, "HTTP/", 5) != 0; |
258 | 668 if(!error) { |
259 if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) || | 669 p = inputline + 5; |
260 (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) { | 670 major = strtol(p, &p, 10); |
261 phb->func(phb->data, source, GAIM_INPUT_READ); | 671 error = (major==0) || (*p != '.'); |
262 g_free(phb->host); | 672 if(!error) { |
263 g_free(phb); | 673 p++; |
264 return; | 674 minor = strtol(p, &p, 10); |
265 } | 675 error = (*p!=' '); |
266 | 676 if(!error) { |
267 close(source); | 677 p++; |
268 phb->func(phb->data, -1, GAIM_INPUT_READ); | 678 status = strtol(p, &p, 10); |
679 error = (*p!=' '); | |
680 } | |
681 } | |
682 } | |
683 | |
684 if(error) { | |
685 debug_printf("Unable to parse proxy's response: %s\n", inputline); | |
686 close(source); | |
687 source=-1; | |
688 } else if(status!=200) { | |
689 debug_printf("Proxy reserver replied: (%s)\n", p); | |
690 close(source); | |
691 source=-1; | |
692 } | |
693 | |
694 phb->func(phb->data, source, GAIM_INPUT_READ); | |
269 g_free(phb->host); | 695 g_free(phb->host); |
270 g_free(phb); | 696 g_free(phb); |
271 return; | 697 return; |
272 } | 698 } |
273 | 699 |
274 static void http_canwrite(gpointer data, gint source, GaimInputCondition cond) | 700 static void http_canwrite(gpointer data, gint source, GaimInputCondition cond) |
275 { | 701 { |
276 char cmd[384]; | 702 char request[8192]; |
703 int request_len = 0; | |
277 struct PHB *phb = data; | 704 struct PHB *phb = data; |
278 unsigned int len; | 705 unsigned int len; |
279 int error = ETIMEDOUT; | 706 int error = ETIMEDOUT; |
280 | 707 |
281 debug_printf("Connected\n"); | 708 debug_printf("Connected\n"); |
287 phb->func(phb->data, -1, GAIM_INPUT_READ); | 714 phb->func(phb->data, -1, GAIM_INPUT_READ); |
288 g_free(phb->host); | 715 g_free(phb->host); |
289 g_free(phb); | 716 g_free(phb); |
290 return; | 717 return; |
291 } | 718 } |
292 fcntl(source, F_SETFL, 0); | 719 request_len = g_snprintf(request, sizeof(request), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port, |
293 g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port, | |
294 phb->host, phb->port); | 720 phb->host, phb->port); |
295 if (send(source, cmd, strlen(cmd), 0) < 0) { | |
296 close(source); | |
297 phb->func(phb->data, -1, GAIM_INPUT_READ); | |
298 g_free(phb->host); | |
299 g_free(phb); | |
300 return; | |
301 } | |
302 | 721 |
303 if (proxyuser) { | 722 if (proxyuser) { |
304 char *t1, *t2; | 723 char *t1, *t2; |
305 t1 = g_strdup_printf("%s:%s", proxyuser, proxypass); | 724 t1 = g_strdup_printf("%s:%s", proxyuser, proxypass); |
306 t2 = tobase64(t1); | 725 t2 = tobase64(t1); |
307 g_free(t1); | 726 g_free(t1); |
308 g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2); | 727 g_return_if_fail(request_len < sizeof(request)); |
728 request_len += g_snprintf(request + request_len, sizeof(request) - request_len, "Proxy-Authorization: Basic %s\r\n", t2); | |
309 g_free(t2); | 729 g_free(t2); |
310 if (send(source, cmd, strlen(cmd), 0) < 0) { | 730 } |
311 close(source); | 731 |
312 phb->func(phb->data, -1, GAIM_INPUT_READ); | 732 g_return_if_fail(request_len < sizeof(request)); |
313 g_free(phb->host); | 733 strcpy(request + request_len, "\r\n"); |
314 g_free(phb); | 734 request_len += 2; |
315 return; | 735 |
316 } | 736 if (write(source, request, request_len) < 0) { |
317 } | |
318 | |
319 g_snprintf(cmd, sizeof(cmd), "\r\n"); | |
320 if (send(source, cmd, strlen(cmd), 0) < 0) { | |
321 close(source); | 737 close(source); |
322 phb->func(phb->data, -1, GAIM_INPUT_READ); | 738 phb->func(phb->data, -1, GAIM_INPUT_READ); |
323 g_free(phb->host); | 739 g_free(phb->host); |
324 g_free(phb); | 740 g_free(phb); |
325 return; | 741 return; |
326 } | 742 } |
327 | 743 |
328 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb); | 744 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb); |
329 } | 745 } |
330 | 746 |
331 static int proxy_connect_http(char *host, unsigned short port, struct PHB *phb) | 747 static int proxy_connect_http(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) |
332 { | 748 { |
333 struct sockaddr_in *sin; | |
334 int fd = -1; | 749 int fd = -1; |
335 | 750 |
336 debug_printf("connecting to %s:%d via %s:%d using HTTP\n", host, port, proxyhost, proxyport); | 751 debug_printf("connecting to %s:%d via %s:%d using HTTP\n", phb->host, phb->port, proxyhost, proxyport); |
337 | 752 |
338 if (!(sin = gaim_gethostbyname(proxyhost, proxyport))) { | 753 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) { |
754 g_free(phb->host); | |
339 g_free(phb); | 755 g_free(phb); |
340 return -1; | 756 return -1; |
341 } | 757 } |
342 | 758 |
343 if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { | |
344 g_free(phb); | |
345 return -1; | |
346 } | |
347 | |
348 phb->host = g_strdup(host); | |
349 phb->port = port; | |
350 | |
351 fcntl(fd, F_SETFL, O_NONBLOCK); | 759 fcntl(fd, F_SETFL, O_NONBLOCK); |
352 | 760 |
353 if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) { | 761 if (connect(fd, addr, addrlen) < 0) { |
354 if ((errno == EINPROGRESS) || (errno == EINTR)) { | 762 if ((errno == EINPROGRESS) || (errno == EINTR)) { |
355 debug_printf("Connect would have blocked\n"); | 763 debug_printf("Connect would have blocked\n"); |
356 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, http_canwrite, phb); | 764 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, http_canwrite, phb); |
357 } else { | 765 } else { |
358 close(fd); | 766 close(fd); |
450 } | 858 } |
451 | 859 |
452 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb); | 860 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb); |
453 } | 861 } |
454 | 862 |
455 static int proxy_connect_socks4(char *host, unsigned short port, struct PHB *phb) | 863 static int proxy_connect_socks4(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) |
456 { | 864 { |
457 struct sockaddr_in *sin; | |
458 int fd = -1; | 865 int fd = -1; |
459 | 866 |
460 debug_printf("connecting to %s:%d via %s:%d using SOCKS4\n", host, port, proxyhost, proxyport); | 867 debug_printf("connecting to %s:%d via %s:%d using SOCKS4\n", phb->host, phb->port, proxyhost, proxyport); |
461 | 868 |
462 if (!(sin = gaim_gethostbyname(proxyhost, proxyport))) { | 869 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) { |
870 g_free(phb->host); | |
463 g_free(phb); | 871 g_free(phb); |
464 return -1; | 872 return -1; |
465 } | 873 } |
466 | 874 |
467 if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { | |
468 g_free(phb); | |
469 return -1; | |
470 } | |
471 | |
472 phb->host = g_strdup(host); | |
473 phb->port = port; | |
474 | |
475 fcntl(fd, F_SETFL, O_NONBLOCK); | 875 fcntl(fd, F_SETFL, O_NONBLOCK); |
476 if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) { | 876 if (connect(fd, addr, addrlen) < 0) { |
477 if ((errno == EINPROGRESS) || (errno == EINTR)) { | 877 if ((errno == EINPROGRESS) || (errno == EINTR)) { |
478 debug_printf("Connect would have blocked\n"); | 878 debug_printf("Connect would have blocked\n"); |
479 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, s4_canwrite, phb); | 879 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, s4_canwrite, phb); |
480 } else { | 880 } else { |
481 close(fd); | 881 close(fd); |
676 } | 1076 } |
677 | 1077 |
678 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb); | 1078 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb); |
679 } | 1079 } |
680 | 1080 |
681 static int proxy_connect_socks5(char *host, unsigned short port, struct PHB *phb) | 1081 static int proxy_connect_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) |
682 { | 1082 { |
683 struct sockaddr_in *sin; | |
684 int fd = -1; | 1083 int fd = -1; |
685 | 1084 |
686 debug_printf("connecting to %s:%d via %s:%d using SOCKS5\n", host, port, proxyhost, proxyport); | 1085 debug_printf("connecting to %s:%d via %s:%d using SOCKS5\n", phb->host, phb->port, proxyhost, proxyport); |
687 | 1086 |
688 if (!(sin = gaim_gethostbyname(proxyhost, proxyport))) { | 1087 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) { |
1088 g_free(phb->host); | |
689 g_free(phb); | 1089 g_free(phb); |
690 return -1; | 1090 return -1; |
691 } | 1091 } |
692 | 1092 |
693 if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { | |
694 g_free(phb); | |
695 return -1; | |
696 } | |
697 | |
698 phb->host = g_strdup(host); | |
699 phb->port = port; | |
700 | |
701 fcntl(fd, F_SETFL, O_NONBLOCK); | 1093 fcntl(fd, F_SETFL, O_NONBLOCK); |
702 if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) { | 1094 if (connect(fd, addr, addrlen) < 0) { |
703 if ((errno == EINPROGRESS) || (errno == EINTR)) { | 1095 if ((errno == EINPROGRESS) || (errno == EINTR)) { |
704 debug_printf("Connect would have blocked\n"); | 1096 debug_printf("Connect would have blocked\n"); |
705 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, s5_canwrite, phb); | 1097 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, s5_canwrite, phb); |
706 } else { | 1098 } else { |
707 close(fd); | 1099 close(fd); |
726 } | 1118 } |
727 | 1119 |
728 return fd; | 1120 return fd; |
729 } | 1121 } |
730 | 1122 |
1123 static void connection_host_resolved(struct sockaddr *addr, size_t addrlen, gpointer data, const char *error_message) | |
1124 { | |
1125 struct PHB *phb = (struct PHB*)data; | |
1126 | |
1127 if(!addr) | |
1128 { | |
1129 phb->func(phb->data, -1, GAIM_INPUT_READ); | |
1130 return; | |
1131 } | |
1132 | |
1133 switch(phb->proxytype) | |
1134 { | |
1135 case PROXY_NONE: | |
1136 proxy_connect_none(phb, addr, addrlen); | |
1137 break; | |
1138 case PROXY_HTTP: | |
1139 proxy_connect_http(phb, addr, addrlen); | |
1140 break; | |
1141 case PROXY_SOCKS4: | |
1142 proxy_connect_socks4(phb, addr, addrlen); | |
1143 break; | |
1144 case PROXY_SOCKS5: | |
1145 proxy_connect_socks5(phb, addr, addrlen); | |
1146 break; | |
1147 } | |
1148 } | |
1149 | |
731 int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data) | 1150 int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data) |
732 { | 1151 { |
1152 char *connecthost = host; | |
1153 int connectport = port; | |
733 struct PHB *phb = g_new0(struct PHB, 1); | 1154 struct PHB *phb = g_new0(struct PHB, 1); |
734 phb->func = func; | 1155 phb->func = func; |
735 phb->data = data; | 1156 phb->data = data; |
1157 phb->host = g_strdup(host); | |
1158 phb->port = port; | |
1159 phb->proxytype = proxytype; | |
736 | 1160 |
737 if (!host || !port || (port == -1) || !func) { | 1161 if (!host || !port || (port == -1) || !func) { |
1162 if(host) | |
1163 g_free(phb->host); | |
738 g_free(phb); | 1164 g_free(phb); |
739 return -1; | 1165 return -1; |
740 } | 1166 } |
741 #ifndef _WIN32 | 1167 |
742 sethostent(1); | 1168 if ((phb->proxytype!=PROXY_NONE) && (!proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1))) |
743 #endif | 1169 phb->proxytype=PROXY_NONE; |
744 if ((proxytype == PROXY_NONE) || !proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1)) | 1170 |
745 return proxy_connect_none(host, port, phb); | 1171 switch(phb->proxytype) |
746 else if (proxytype == PROXY_HTTP) | 1172 { |
747 return proxy_connect_http(host, port, phb); | 1173 case PROXY_NONE: |
748 else if (proxytype == PROXY_SOCKS4) | 1174 break; |
749 return proxy_connect_socks4(host, port, phb); | 1175 case PROXY_HTTP: |
750 else if (proxytype == PROXY_SOCKS5) | 1176 case PROXY_SOCKS4: |
751 return proxy_connect_socks5(host, port, phb); | 1177 case PROXY_SOCKS5: |
752 | 1178 connecthost=proxyhost; |
753 g_free(phb); | 1179 connectport=proxyport; |
754 return -1; | 1180 break; |
755 } | 1181 default: |
756 | 1182 g_free(phb->host); |
1183 g_free(phb); | |
1184 return -1; | |
1185 } | |
1186 | |
1187 gaim_gethostbyname_async(connecthost, connectport, SOCK_STREAM, connection_host_resolved, phb); | |
1188 return 1; | |
1189 } |