Substitute missing strcasestr(3)
[middleman.git] / src / protocol.c
1 /*
2     MiddleMan filtering proxy server
3     Copyright (C) 2002  Jason McLaughlin
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/time.h>
26 #include <ctype.h>
27 #include "../libntlm/ntlm.h"
28 #include "proto.h"
29
30 extern TEMPLATES *templates;
31 extern REWRITE_LIST *rewrite_list;
32 extern EXTERNAL *external;
33 extern MIME_LIST *mime_list;
34 extern REDIRECT_LIST *redirect_list;
35 extern KEYWORD_LIST *keyword_list;
36 extern THREADLIST threads[];
37
38 int protocol_start(CONNECTION * connection)
39 {
40         int proxyfd, x;
41
42         switch (connection->proxy_type) {
43         case PROXY_NORMAL:
44                 proxyfd = net_connect(connection->proxy_host, connection->proxy_port);
45                 if (proxyfd < 0)
46                         putlog(MMLOG_ERROR, "connection to proxy server failed");
47                 else
48                         connection->server = sock_new(proxyfd);
49
50                 break;
51         case PROXY_SOCKS4:
52                 proxyfd = net_connect(connection->proxy_host, connection->proxy_port);
53
54                 if (proxyfd >= 0) {
55                         connection->server = sock_new(proxyfd);
56
57                         x = net_socks4(connection, connection->header->host, connection->header->port);
58                         if (x < 0) {
59                                 putlog(MMLOG_ERROR, "connection to socks4 server failed");
60
61                                 template_send(templates, error_to_template(x), connection, 404);
62
63                                 return -1;
64                         }
65                 }
66
67                 break;
68         default:
69                 proxyfd = net_connect(connection->header->host, connection->header->port);
70                 if (proxyfd >= 0)
71                         connection->server = sock_new(proxyfd);
72         }
73
74         if (proxyfd < 0) {
75                 template_send(templates, error_to_template(proxyfd), connection, 404);
76
77                 connection->server = NULL;
78
79                 return -1;
80         }
81
82         return proxyfd;
83 }
84
85 int protocol_reconnect(CONNECTION * connection)
86 {
87         int ret;
88
89         sock_close(connection->server);
90         sock_flush(connection->client);
91
92         ret = protocol_start(connection);
93
94         return ret;
95 }
96
97 /*
98 This function handles HTTP requests
99 */
100 int protocol_http(CONNECTION * connection)
101 {
102         int auth = FALSE, ret;
103         char *headbuf, buf[8096], *ptr;
104         FILEBUF *filebuf, *ext_filebuf, *postdata = NULL;
105         struct EXTERNAL_LIST_LIST *external_list = NULL;
106         struct MIME_LIST_LIST *mime_list_list;
107         struct url_command_t **url_command;
108
109         pthread_mutex_lock(&threads[connection->thread].lock);
110         threads[connection->thread].flags |= THREAD_HTTP;
111         pthread_mutex_unlock(&threads[connection->thread].lock);
112
113         if (connection->header->content_length != -1) {
114                 /* the browser wants to send something to the remote site after the header (POST, PUT, etc.) */
115                 putlog(MMLOG_DEBUG, "post length: %d", connection->header->content_length);
116
117                 /* unfortunately, this _needs_ to be buffered.. otherwise we won't have any POST data
118                    after authentication */
119                 postdata = http_transfer_filebuf(connection, CLIENT);
120                 rewrite_do(rewrite_list, connection, postdata, REWRITE_POST, TRUE);
121
122                 connection->header->chunked = FALSE;
123                 connection->header->content_length = postdata->size;
124         }
125
126         header_send(connection->header, connection, SERVER, (connection->proxy_type == PROXY_NORMAL) ? HEADER_FORWARD : HEADER_DIRECT);
127
128         /* I really hate using goto's */
129       auth_finished:
130         if (postdata != NULL)
131                 net_filebuf_send(postdata, connection, SERVER);
132
133       reget_header:
134         headbuf = header_get(connection, SERVER, TIMEOUT);
135         if (headbuf == NULL) {
136                 if (!connection->keepalive_server && auth == FALSE) {
137                         template_send(templates, "noconnect", connection, 503);
138
139                         if (postdata != NULL)
140                                 filebuf_free(postdata);
141
142                         return -1;
143                 } else {
144                         /* keep-alive connection closed, or authorization is being done on
145                            a non-keepalive proxy */
146                         ret = protocol_reconnect(connection);
147                         if (ret == -1) {
148                                 if (postdata != NULL)
149                                         filebuf_free(postdata);
150
151                                 return -1;
152                         }
153
154                         header_send(connection->header, connection, SERVER, (connection->proxy_type == PROXY_NORMAL) ? HEADER_FORWARD : HEADER_DIRECT);
155                         if (postdata != NULL)
156                                 net_filebuf_send(postdata, connection, SERVER);
157
158                         headbuf = header_get(connection, SERVER, TIMEOUT);
159                         if (headbuf == NULL) {
160                                 if (postdata != NULL)
161                                         filebuf_free(postdata);
162
163                                 return -1;
164                         }
165                 }
166         }
167
168         /* pass the server's header through the rewrite rules */
169         filebuf = filebuf_new();
170         filebuf->data = headbuf;
171         filebuf->size = strlen(headbuf) + 1;
172
173         rewrite_do(rewrite_list, connection, filebuf, REWRITE_SERVER, TRUE);
174
175         headbuf = filebuf->data;
176         filebuf->data = NULL;
177
178         filebuf_free(filebuf);
179
180         connection->rheader = http_header_parse_response(headbuf);
181         if (connection->rheader == NULL) {
182                 if (postdata != NULL)
183                         filebuf_free(postdata);
184
185                 template_send(templates, "badresponse", connection, 400);
186                 xfree(headbuf);
187                 return -1;
188         }
189         xfree(headbuf);
190
191         if (connection->rheader->code == 100) {
192                 /* server sent notice to continue POST, go back and retrieve final header 
193                    (according to the RFC, the server should not send this unless the client sends an Expect:
194                    header; however... IIS will sometimes send this anyways)
195                 */
196                 http_header_free(connection->rheader);
197
198                 goto reget_header;
199         }
200
201         connection->rheader->host = xstrdup(connection->header->host);
202         connection->rheader->file = xstrdup(connection->header->file);
203
204         if (connection->keepalive_client) {
205                 if (connection->rheader->content_length == -1 && !connection->rheader->chunked) {
206                         /* connection can't be kept alive if we can't determine when the remote site's data ends */
207                         connection->keepalive_client = FALSE;
208                         connection->keepalive_server = FALSE;
209                 } else if (connection->rheader->keepalive == TRUE || connection->rheader->proxy_keepalive == TRUE)
210                         connection->keepalive_server = TRUE;
211                 else if (connection->rheader->version == HTTP_HTTP11)
212                         /* keep-alive is assumed for HTTP/1.1 unless any of the above conditions are met */
213                         connection->keepalive_server = TRUE;
214                 else
215                         connection->keepalive_server = FALSE;
216
217                 putlog(MMLOG_DEBUG, "keepalive_server = %d", connection->keepalive_server);
218         } else
219                 connection->keepalive_server = FALSE;
220
221         if (connection->rheader->code == 407 && connection->proxy_type == PROXY_NORMAL && auth == FALSE) {
222                 /* authenticate with the other proxy */
223                 if (proxy_authenticate(connection)) {
224                         http_header_free(connection->rheader);
225
226                         /* only try once */
227                         auth = TRUE;
228
229                         /* authentication finished, retrieve the header again */
230                         goto auth_finished;
231                 }
232                 FREE_AND_NULL(connection->rheader->proxy_authenticate);
233
234                 /* just continue, user will see 407 warning from proxy in browser */
235         }
236
237         if (postdata != NULL)
238                 filebuf_free(postdata);
239
240         /* send template instead of web server's content when a template name matches the code */
241         snprintf(buf, sizeof(buf), "%d", connection->rheader->code);
242         ret = template_send(templates, buf, connection, connection->rheader->code);
243         if (ret == TRUE)
244                 goto out;
245
246         if (connection->rheader->location != NULL && (connection->rheader->code == 301 || connection->rheader->code == 302)) {
247                 /* redirect_do will replace the Location: header if any rules match */
248                 redirect_do(redirect_list, connection, REDIRECT_HEADER);
249
250                 /* add URL command to location: header so the feature(s) are still bypassed when
251                    browser follows redirect */
252                 if (connection->header->url_command != NULL) {
253                         ptr = url_command_add(connection, connection->rheader->location);
254                         xfree(connection->rheader->location);
255                         connection->rheader->location = ptr;
256                 }
257         }
258
259         /* no message body regardless of what header says for HEAD requests and certain return codes */
260         if (!strcasecmp(connection->header->method, "HEAD") || connection->rheader->code == 304 || connection->rheader->code == 204 || (connection->rheader->code >= 100 && connection->rheader->code < 200)) {
261                 header_send(connection->rheader, connection, CLIENT, HEADER_RESP);
262
263                 goto out;
264         }
265
266         for (url_command = connection->header->url_command; url_command && *url_command; url_command++) {
267                 if (!strcasecmp((*url_command)->command, "mime")) {
268                         /* show matching MIME filter entries, if any, for the requested URL */
269                         mime_check_show(connection);
270
271                         connection->keepalive_server = FALSE;
272
273                         goto out;
274                 }
275         }
276
277         mime_list_list = mime_check(mime_list, connection);
278         if (mime_list_list != NULL) {
279                 putlog(MMLOG_MIME, "blocked mime-type %s from %s%s", (connection->rheader->content_type != NULL) ? connection->rheader->content_type : "(none)", connection->header->host, connection->header->file);
280
281                 template_send(templates, (mime_list_list->template != NULL) ? mime_list_list->template : (mime_list->dtemplate != NULL) ? mime_list->dtemplate : "blocked", connection, 200);
282
283                 /* we have to drop the connection since we're already transferring something */
284                 connection->keepalive_server = FALSE;
285
286                 pthread_rwlock_unlock(&mime_list->lock);
287
288                 goto out;
289         }
290         pthread_rwlock_unlock(&mime_list->lock);
291
292         putlog(MMLOG_REQUEST, "%s %s:%d%s", connection->header->method, connection->header->host, connection->header->port, connection->header->file);
293
294         if (BUFFERMAX == -1 || connection->rheader->content_length <= BUFFERMAX) {
295                 connection->buffer = rewrite_do(rewrite_list, connection, NULL, REWRITE_BODY, FALSE);
296
297                 if (connection->buffer == FALSE) {
298                         external_list = external_find(external, connection);
299                         pthread_rwlock_unlock(&external->lock);
300
301                         if (external_list != NULL)
302                                 connection->buffer = TRUE;
303                 }
304
305                 if (connection->buffer == FALSE)
306                         connection->buffer = keyword_check(keyword_list, connection, NULL, FALSE);
307
308                 if (connection->buffer == FALSE) {
309                         for (url_command = connection->header->url_command; url_command && *url_command; url_command++) {
310                                 if (!strcasecmp((*url_command)->command, "score"))
311                                         connection->buffer = TRUE;
312                         }
313                 }
314         } else
315                 connection->buffer = FALSE;
316
317         if (connection->buffer == FALSE) {
318                 header_send(connection->rheader, connection, CLIENT, HEADER_RESP);
319
320                 http_transfer(connection, SERVER);
321         } else {
322                 /* this file is to be buffered and processed before sending it to the browser */
323                 filebuf = http_transfer_filebuf(connection, SERVER);
324
325                 /* decompress gzip-encoded content */
326                 if (connection->rheader->content_encoding != NULL) {
327                         if (!strcasecmp(connection->rheader->content_encoding, "gzip")) {
328                                 ret = filebuf_ungzip(filebuf);
329                                 if (ret == FALSE) {
330                                         putlog(MMLOG_ERROR, "failed to decompress gzip encoded content");
331
332                                         /* failed to decompress, just send as-is */
333                                         goto bypass;
334                                 }
335                         } else {
336                                 putlog(MMLOG_WARN, "unhandled content encoding: %s", connection->rheader->content_encoding);
337
338                                 goto bypass;
339                         }
340                 }
341
342                 /* check keyword score */
343                 ret = keyword_check(keyword_list, connection, filebuf, TRUE);
344
345                 for (url_command = connection->header->url_command; url_command && *url_command; url_command++) {
346                         if (!strcasecmp((*url_command)->command, "score")) {
347                                 score_show(connection, ret);
348
349                                 filebuf_free(filebuf);
350
351                                 goto out;
352                         }
353                 }
354
355                 if (ret) {
356                         pthread_rwlock_rdlock(&keyword_list->lock);
357                         if (ret >= keyword_list->threshold) {
358                                 putlog(MMLOG_KEYWORDS, "page above threshold with a score of %d", ret);
359
360                                 template_send(templates, (keyword_list->template != NULL) ? keyword_list->template : "blocked", connection, 200);
361
362                                 pthread_rwlock_unlock(&keyword_list->lock);
363
364                                 filebuf_free(filebuf);
365
366                                 goto out;
367                         }
368
369                         pthread_rwlock_unlock(&keyword_list->lock);
370                 }
371
372                 rewrite_do(rewrite_list, connection, filebuf, REWRITE_BODY, TRUE);
373
374                 external_list = external_find(external, connection);
375                 if (external_list != NULL && external_list->exec != NULL) {
376                         ext_filebuf = external_exec(connection, external_list->exec, filebuf, external_list->type);
377                         if (ext_filebuf != NULL) {
378                                 filebuf_free(filebuf);
379                                 filebuf = ext_filebuf;
380
381                                 if (external_list->newmime != NULL) {
382                                         FREE_AND_NULL(connection->rheader->content_type);
383
384                                         if (!strcasecmp(external_list->newmime, "STDIN")) {
385                                                 /* try extracting Content-type header from external 
386                                                    program's output */
387                                                 connection->rheader->content_type = external_getmime(filebuf);
388                                         } else
389                                                 connection->rheader->content_type = xstrdup(external_list->newmime);
390                                 }
391                         } else
392                                 putlog(MMLOG_ERROR, "failed to execute external parser '%s'", external_list->exec);
393                 }
394                 pthread_rwlock_unlock(&external->lock);
395
396                 FREE_AND_NULL(connection->rheader->content_encoding);
397                 if (connection->header->accept_encoding != NULL) {
398                         /* compress the content if the browser supports gzip encoding */
399                         char *s, *accept_encoding_lower = xstrdup(connection->header->accept_encoding);
400
401                         /* strcasestr(3) is not available on some systems; use strstr(3) instead */
402                         for (s=accept_encoding_lower; *s; s++)
403                                 *s=tolower(*s);
404                         ptr = strstr(accept_encoding_lower, "gzip");
405                         if (ptr != NULL) {
406                                 ret = filebuf_gzip(filebuf);
407                                 if (ret == TRUE) connection->rheader->content_encoding = xstrdup("gzip");
408                         }
409                         free(accept_encoding_lower);
410                 }
411  
412                 bypass:
413                 putlog(MMLOG_DEBUG, "filebuf size: %d", filebuf->size);
414
415                 connection->rheader->chunked = FALSE;
416                 connection->rheader->content_length = filebuf->size;
417
418                 if (connection->keepalive_client != FALSE && connection->header->keepalive != FALSE && connection->header->proxy_keepalive != FALSE) {
419                         /* we may be able to do keep-alive now since the page was buffered and the size is now known */
420                         if (connection->header->keepalive == TRUE || connection->header->proxy_keepalive == TRUE)
421                                 connection->keepalive_client = TRUE;
422                         else if (connection->header->version == HTTP_HTTP11)
423                                 connection->keepalive_client = TRUE;
424                 }
425
426                 header_send(connection->rheader, connection, CLIENT, HEADER_RESP);
427                 net_filebuf_send(filebuf, connection, CLIENT);
428
429                 filebuf_free(filebuf);
430         }
431
432       out:
433         http_header_free(connection->rheader);
434
435         return 1;
436 }
437
438 /*
439 This function handles CONNECT requests (HTTPS)
440 */
441 int protocol_connect(CONNECTION * connection)
442 {
443         pthread_mutex_lock(&threads[connection->thread].lock);
444         threads[connection->thread].flags |= THREAD_CONNECT;
445         pthread_mutex_unlock(&threads[connection->thread].lock);
446
447         putlog(MMLOG_REQUEST, "CONNECT %s:%d", connection->header->host, connection->header->port);
448
449         if (connection->proxy_type == PROXY_NORMAL)
450                 header_send(connection->header, connection, SERVER, HEADER_FORWARD);
451         else
452                 putsock(connection->client, "HTTP/1.1 Connection Established\r\n\r\n");
453
454         net_proxy(connection, -1);
455
456         return 0;
457 }
458
459 /*
460 get size of next chunk for pages using chunked encoding
461 */
462 unsigned int next_chunksize(CONNECTION * connection, int flags)
463 {
464         int x;
465         char buf[64];
466
467         x = sock_getline((flags == SERVER) ? connection->server : connection->client, buf, sizeof(buf));
468         if (x <= 0)
469                 return 0;
470
471         return strtol(buf, NULL, 16);
472 }
473
474
475 /*
476 transfer http body from one socket to another
477 */
478 void http_transfer(CONNECTION * connection, int direction)
479 {
480         int x;
481         HEADER *header;
482
483         header = (direction == SERVER) ? connection->rheader : connection->header;
484
485         if (header->content_length != -1)
486                 net_transfer(connection, direction, header->content_length);
487         else if (header->chunked)
488                 do {
489                         x = next_chunksize(connection, direction);
490                         putsock((direction == SERVER) ? connection->client : connection->server, "%x\r\n", x);
491                         /* rfc says chunks must end in \r\n */
492                         net_transfer(connection, direction, x + 2);
493                 } while (x > 0);
494         else
495                 net_transfer(connection, direction, -1);
496
497 }
498
499 /*
500 transfer http body into filebuf
501 */
502 FILEBUF *http_transfer_filebuf(CONNECTION * connection, int direction)
503 {
504         int x;
505         FILEBUF *filebuf;
506         HEADER *header;
507         SOCKET *sock;
508
509         filebuf = filebuf_new();
510
511         header = (direction == SERVER) ? connection->rheader : connection->header;
512         sock = (direction == SERVER) ? connection->server : connection->client;
513
514         if (header->content_length != -1)
515                 net_filebuf_read(filebuf, connection, direction, header->content_length);
516         else if (header->chunked)
517                 do {
518                         x = next_chunksize(connection, direction);
519                         net_filebuf_read(filebuf, connection, direction, x);
520                         /* discard the \r\n from the server */
521                         sock_getline(sock, NULL, -1);
522                 } while (x > 0);
523         else
524                 net_filebuf_read(filebuf, connection, direction, -1);
525
526         return filebuf;
527 }
528
529 /* 
530 read the body and discard it 
531 */
532 void http_transfer_discard(CONNECTION * connection, int direction)
533 {
534         int x;
535         HEADER *header;
536         SOCKET *sock;
537
538         header = (direction == SERVER) ? connection->rheader : connection->header;
539         sock = (direction == SERVER) ? connection->server : connection->client;
540
541         if (header->content_length != -1)
542                 net_filebuf_read(NULL, connection, direction, header->content_length);
543         else if (header->chunked)
544                 do {
545                         x = next_chunksize(connection, direction);
546                         net_filebuf_read(NULL, connection, direction, x);
547                         /* discard the \r\n from the server */
548                         sock_getline(sock, NULL, -1);
549                 } while (x > 0);
550         else
551                 net_filebuf_read(NULL, connection, direction, -1);
552
553 }
554
555 int proxy_authenticate(CONNECTION * connection)
556 {
557         if (connection->rheader->proxy_authenticate == NULL)
558                 return FALSE;
559
560         if (!strcasecmp(connection->rheader->proxy_authenticate, "NTLM")) {
561                 /* NTLM authentication */
562                 return send_ntlm_response(connection);
563         } else if (!strncasecmp(connection->rheader->proxy_authenticate, "Basic", 5)) {
564                 /* Basic authentication */
565                 return send_basic_response(connection);
566         }
567
568         return FALSE;
569 }
570
571 /* perform an NTLM handshake, the sequence of events looks like this:
572     1: C -->  S   GET ...
573
574     2: C <--  S   401 Unauthorized
575                   Proxy-Authenticate: NTLM
576     
577     3: C  --> S   GET ...
578                   Proxy-Authorization: NTLM <base64-encoded type-1-message>
579     
580     4: C <--  S   401 Unauthorized
581                   Proxy-Authenticate: NTLM <base64-encoded type-2-message>
582     
583     5: C  --> S   GET ...
584                   Proxy-Authorization: NTLM <base64-encoded type-3-message>
585     
586     6: C <--  S   200 Ok
587 */
588 int send_ntlm_response(CONNECTION * connection)
589 {
590         int ret, oldkeep;
591         unsigned char buf[4096], buf2[4096], *headbuf;
592         HEADER *header;
593
594         /* client must do keep-alive during authentication */
595         oldkeep = connection->keepalive_server;
596         connection->keepalive_server = TRUE;
597
598         /* don't need the body of the 407 message */
599         http_transfer_discard(connection, SERVER);
600
601         /* send type-1 message */
602         /* note: buildSmbNtml* knows how to deal with NULL arguments */
603         buildSmbNtlmAuthRequest((tSmbNtlmAuthRequest *) buf, connection->proxy_username, connection->proxy_domain);
604         to64frombits(buf2, buf, SmbLength((tSmbNtlmAuthResponse *) buf));
605
606         FREE_AND_NULL(connection->proxy_auth);
607         snprintf(buf, sizeof(buf), "NTLM %s", buf2);
608         connection->proxy_auth = xstrdup(buf);
609
610         header_send(connection->header, connection, SERVER, HEADER_FORWARD);
611
612         headbuf = header_get(connection, SERVER, TIMEOUT);
613         if (headbuf == NULL) {
614                 /* squid and maybe some other proxies will disconnect after sending
615                    the first 407 message regardless of Connection header */
616                 ret = protocol_reconnect(connection);
617                 if (ret == -1)
618                         goto error;
619
620                 header_send(connection->header, connection, SERVER, HEADER_FORWARD);
621
622                 headbuf = header_get(connection, SERVER, TIMEOUT);
623                 if (headbuf == NULL)
624                         goto error;
625         }
626
627         header = http_header_parse_response(headbuf);
628         xfree(headbuf);
629         if (header == NULL)
630                 goto error;
631
632         http_header_free(connection->rheader);
633         connection->rheader = header;
634
635         if (header->proxy_authenticate == NULL || strncasecmp(header->proxy_authenticate, "NTLM ", 5))
636                 goto error;
637
638         http_transfer_discard(connection, SERVER);
639
640         /* parse type-2 message */
641         from64tobits(buf, &header->proxy_authenticate[5]);
642         buildSmbNtlmAuthResponse((tSmbNtlmAuthChallenge *) buf, (tSmbNtlmAuthResponse *) buf2, connection->proxy_username, connection->proxy_password);
643
644         /* send type-3 message */
645         to64frombits(buf, buf2, SmbLength((tSmbNtlmAuthResponse *) buf2));
646         snprintf(buf2, sizeof(buf2), "NTLM %s", buf);
647
648         FREE_AND_NULL(connection->proxy_auth);
649         connection->proxy_auth = xstrdup(buf2);
650
651         header_send(connection->header, connection, SERVER, HEADER_FORWARD);
652
653         connection->keepalive_server = oldkeep;
654
655         return TRUE;
656
657       error:
658         connection->keepalive_server = oldkeep;
659
660         return FALSE;
661 }
662
663 /* basic proxy authentication */
664 int send_basic_response(CONNECTION * connection)
665 {
666         char buf[4096], buf2[4096];
667
668         /* discard body of 407 authentication required message */
669         http_transfer_discard(connection, SERVER);
670
671         snprintf(buf, sizeof(buf), "%s:%s", (connection->proxy_username != NULL) ? connection->proxy_username : "", (connection->proxy_password != NULL) ? connection->proxy_password : "");
672         to64frombits(buf2, buf, strlen(buf));
673
674         snprintf(buf, sizeof(buf), "Basic %s", buf2);
675
676         FREE_AND_NULL(connection->proxy_auth);
677         connection->proxy_auth = xstrdup(buf);
678
679         header_send(connection->header, connection, SERVER, HEADER_FORWARD);
680
681         return TRUE;
682 }