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 }