Version 1.8.0.0
[socat.git] / xio-proxy.c
blobda9fcea47993e78aac06fe516b2fe85c1fd16a59
1 /* source: xio-proxy.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* this file contains the source for opening addresses of HTTP proxy CONNECT
6 type */
8 #include "xiosysincludes.h"
10 #if WITH_PROXY
12 #include "xioopen.h"
13 #include "xio-socket.h"
14 #include "xio-ip.h"
15 #include "xio-ipapp.h"
16 #include "xio-ascii.h" /* for base64 encoding of authentication */
18 #include "xio-proxy.h"
21 #define PROXYPORT "8080"
23 static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc);
25 const struct optdesc opt_proxyport = { "proxyport", NULL, OPT_PROXYPORT, GROUP_HTTP, PH_LATE, TYPE_STRING, OFUNC_SPEC };
26 const struct optdesc opt_ignorecr = { "ignorecr", NULL, OPT_IGNORECR, GROUP_HTTP, PH_LATE, TYPE_BOOL, OFUNC_SPEC };
27 const struct optdesc opt_http_version = { "http-version", NULL, OPT_HTTP_VERSION, GROUP_HTTP, PH_LATE, TYPE_STRING, OFUNC_SPEC };
28 const struct optdesc opt_proxy_resolve = { "proxy-resolve", "resolve", OPT_PROXY_RESOLVE, GROUP_HTTP, PH_LATE, TYPE_BOOL, OFUNC_SPEC };
29 const struct optdesc opt_proxy_authorization = { "proxy-authorization", "proxyauth", OPT_PROXY_AUTHORIZATION, GROUP_HTTP, PH_LATE, TYPE_STRING, OFUNC_SPEC };
30 const struct optdesc opt_proxy_authorization_file = { "proxy-authorization-file", "proxyauthfile", OPT_PROXY_AUTHORIZATION_FILE, GROUP_HTTP, PH_LATE, TYPE_STRING, OFUNC_SPEC };
32 const struct addrdesc xioaddr_proxy_connect = { "PROXY", 3, xioopen_proxy_connect, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_TCP|GROUP_HTTP|GROUP_CHILD|GROUP_RETRY, 0, 0, 0 HELP(":<proxy-server>:<host>:<port>") };
35 /*0#define CONNLEN 40*/ /* "CONNECT 123.156.189.123:65432 HTTP/1.0\r\n\0" */
36 #define CONNLEN 281 /* "CONNECT <255bytes>:65432 HTTP/1.0\r\n\0" */
38 /* states during receiving answer */
39 enum {
40 XIOSTATE_HTTP1, /* 0 or more bytes of first line received, no \r */
41 XIOSTATE_HTTP2, /* first line received including \r */
42 XIOSTATE_HTTP3, /* received status and \r\n */
43 XIOSTATE_HTTP4, /* within header */
44 XIOSTATE_HTTP5, /* within header, \r */
45 XIOSTATE_HTTP6, /* received status and 1 or more headers, \r\n */
46 XIOSTATE_HTTP7, /* received status line, ev. headers, \r\n\r */
47 XIOSTATE_HTTP8, /* complete answer received */
48 XIOSTATE_ERROR /* error during HTTP headers */
49 } ;
52 /* get buflen bytes from proxy server;
53 handles EINTR;
54 returns <0 when error occurs
56 static ssize_t
57 xioproxy_recvbytes(struct single *sfd, char *buff, size_t buflen, int level) {
58 ssize_t result;
59 do {
60 /* we need at least buflen bytes... */
61 result = Read(sfd->fd, buff, buflen);
62 } while (result < 0 && errno == EINTR); /*! EAGAIN? */
63 if (result < 0) {
64 Msg4(level, "read(%d, %p, "F_Zu"): %s",
65 sfd->fd, buff, buflen, strerror(errno));
66 return result;
68 if (result == 0) {
69 Msg(level, "proxy_connect: connection closed by proxy");
71 return result;
75 #define BUFLEN 2048
78 static int xioopen_proxy_connect(
79 int argc,
80 const char *argv[],
81 struct opt *opts,
82 int xioflags,
83 xiofile_t *xxfd,
84 const struct addrdesc *addrdesc)
86 /* we expect the form: host:host:port */
87 struct single *sfd = &xxfd->stream;
88 struct opt *opts0 = NULL;
89 struct proxyvars struct_proxyvars = { 0 }, *proxyvars = &struct_proxyvars;
90 /* variables to be filled with address option values */
91 bool dofork = false;
92 /* */
93 int pf = PF_UNSPEC;
94 union sockaddr_union us_sa, *us = &us_sa;
95 socklen_t uslen = sizeof(us_sa);
96 struct addrinfo *themlist, *themp;
97 struct addrinfo **ai_sorted;
98 int i;
99 const char *proxyname; char *proxyport = NULL;
100 const char *targetname, *targetport;
101 int ipproto = IPPROTO_TCP;
102 bool needbind = false;
103 bool lowport = false;
104 int socktype = SOCK_STREAM;
105 int level;
106 int result;
108 if (argc != 4) {
109 xio_syntax(argv[0], 3, argc-1, addrdesc->syntax);
110 return STAT_NORETRY;
112 proxyname = argv[1];
113 targetname = argv[2];
114 targetport = argv[3];
116 if (sfd->howtoend == END_UNSPEC)
117 sfd->howtoend = END_SHUTDOWN;
118 if (applyopts_single(sfd, opts, PH_INIT) < 0)
119 return -1;
120 applyopts(sfd, 1, opts, PH_INIT);
122 retropt_int(opts, OPT_SO_TYPE, &socktype);
124 retropt_bool(opts, OPT_FORK, &dofork);
126 if (retropt_string(opts, OPT_PROXYPORT, &proxyport) < 0) {
127 if ((proxyport = strdup(PROXYPORT)) == NULL) {
128 errno = ENOMEM; return -1;
132 result = _xioopen_proxy_prepare(proxyvars, opts, targetname, targetport,
133 sfd->para.socket.ip.ai_flags);
134 if (result != STAT_OK)
135 return result;
137 Notice4("opening connection to %s:%u via proxy %s:%s",
138 proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport);
140 i = 0;
141 do { /* loop over retries (failed connect and proxy-request attempts) */
143 level = E_INFO;
145 result =
146 _xioopen_ipapp_prepare(opts, &opts0, proxyname, proxyport,
147 &pf, ipproto,
148 sfd->para.socket.ip.ai_flags,
149 &themlist, us, &uslen,
150 &needbind, &lowport, socktype);
151 if (result != STAT_OK)
152 return result;
154 /* Count addrinfo entries */
155 themp = themlist;
156 i = 0;
157 while (themp != NULL) {
158 ++i;
159 themp = themp->ai_next;
161 ai_sorted = Calloc((i+1), sizeof(struct addrinfo *));
162 if (ai_sorted == NULL)
163 return STAT_RETRYLATER;
164 /* Generate a list of addresses sorted by preferred ip version */
165 _xio_sort_ip_addresses(themlist, ai_sorted);
167 /* Loop over themlist */
168 i = 0;
169 themp = ai_sorted[i++];
170 while (themp != NULL) {
171 Notice4("opening connection to %s:%u via proxy %s:%s",
172 proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport);
173 #if WITH_RETRY
174 if (sfd->forever || sfd->retry || ai_sorted[i] != NULL) {
175 level = E_INFO;
176 } else
177 #endif /* WITH_RETRY */
178 level = E_ERROR;
180 result =
181 _xioopen_connect(sfd,
182 needbind?us:NULL, uslen,
183 themp->ai_addr, themp->ai_addrlen,
184 opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, lowport, level);
185 if (result == STAT_OK)
186 break;
187 themp = ai_sorted[i++];
188 if (themp == NULL) {
189 result = STAT_RETRYLATER;
191 switch (result) {
192 case STAT_OK: break;
193 #if WITH_RETRY
194 case STAT_RETRYLATER:
195 case STAT_RETRYNOW:
196 if (sfd->forever || sfd->retry) {
197 --sfd->retry;
198 if (result == STAT_RETRYLATER)
199 Nanosleep(&sfd->intervall, NULL);
200 continue;
202 #endif /* WITH_RETRY */
203 default:
204 free(ai_sorted);
205 xiofreeaddrinfo(themlist);
206 return result;
209 xiofreeaddrinfo(themlist);
210 applyopts(sfd, -1, opts, PH_ALL);
212 if ((result = _xio_openlate(sfd, opts)) < 0)
213 return result;
215 result = _xioopen_proxy_connect(sfd, proxyvars, level);
216 switch (result) {
217 case STAT_OK: break;
218 #if WITH_RETRY
219 case STAT_RETRYLATER:
220 case STAT_RETRYNOW:
221 if (sfd->forever || sfd->retry--) {
222 if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL);
223 continue;
225 #endif /* WITH_RETRY */
226 default:
227 return result;
230 if (dofork) {
231 xiosetchilddied(); /* set SIGCHLD handler */
234 #if WITH_RETRY
235 if (dofork) {
236 pid_t pid;
237 int level = E_ERROR;
238 if (sfd->forever || sfd->retry) {
239 level = E_WARN;
241 while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
242 if (sfd->forever || --sfd->retry) {
243 Nanosleep(&sfd->intervall, NULL); continue;
245 return STAT_RETRYLATER;
248 if (pid == 0) { /* child process */
249 sfd->forever = false; sfd->retry = 0;
250 break;
253 /* parent process */
254 Close(sfd->fd);
255 Nanosleep(&sfd->intervall, NULL);
256 dropopts(opts, PH_ALL);
257 opts = copyopts(opts0, GROUP_ALL);
258 continue;
259 } else
260 #endif /* WITH_RETRY */
262 break;
265 } while (true); /* end of complete open loop - drop out on success */
267 Notice4("successfully connected to %s:%u via proxy %s:%s",
268 proxyvars->targetaddr, proxyvars->targetport,
269 proxyname, proxyport);
271 return 0;
275 int _xioopen_proxy_prepare(
276 struct proxyvars *proxyvars,
277 struct opt *opts,
278 const char *targetname,
279 const char *targetport,
280 const int ai_flags[2]) {
281 union sockaddr_union host;
282 socklen_t socklen = sizeof(host);
283 int rc;
285 retropt_bool(opts, OPT_IGNORECR, &proxyvars->ignorecr);
286 retropt_bool(opts, OPT_PROXY_RESOLVE, &proxyvars->doresolve);
287 retropt_string(opts, OPT_HTTP_VERSION, &proxyvars->version);
288 retropt_string(opts, OPT_PROXY_AUTHORIZATION, &proxyvars->authstring);
289 retropt_string(opts, OPT_PROXY_AUTHORIZATION_FILE, &proxyvars->authfile);
291 if (proxyvars->authfile) {
292 int authfd;
293 off_t length;
294 ssize_t bytes;
296 /* if we have a file containing authentication credentials and they were also
297 provided on the command line, something is misspecified */
298 if (proxyvars->authstring) {
299 Error("Only one of options proxy-authorization and proxy-authorization-file allowed");
300 return STAT_NORETRY;
302 authfd = Open(proxyvars->authfile, O_RDONLY, 0);
303 if (authfd < 0) {
304 Error2("open(\"%s\", O_RDONLY): %s", proxyvars->authfile, strerror(errno));
305 return STAT_NORETRY;
307 /* go to the end of our proxy auth file to
308 figure out how long our proxy auth is */
309 if ((length = Lseek(authfd, 0, SEEK_END)) < 0) {
310 Error2("lseek(<%s>, 0, SEEK_END): %s",
311 proxyvars->authfile, strerror(errno));
312 return STAT_RETRYLATER;
314 proxyvars->authstring = Malloc(length+1);
315 /* go back to the beginning */
316 Lseek(authfd, 0, SEEK_SET);
317 /* read our proxy info from the file */
318 if ((bytes = Read(authfd, proxyvars->authstring, (size_t) length)) < 0) {
319 Error3("read(<%s>, , "F_Zu"): %s", proxyvars->authfile, length, strerror(errno));
320 free(proxyvars->authstring);
321 Close(authfd);
322 return STAT_NORETRY;
324 if (bytes < length) {
325 Error3("read(<%s>, , "F_Zu"): got only "F_Zu" bytes",
326 proxyvars->authfile, length, bytes);
327 Close(authfd);
328 return STAT_NORETRY;
330 proxyvars->authstring[bytes] = '\0'; /* string termination */
331 Close(authfd);
334 if (proxyvars->doresolve) {
335 /* currently we only resolve to IPv4 addresses. This is in accordance to
336 RFC 2396; however once it becomes clear how IPv6 addresses should be
337 represented in CONNECT commands this code might need to be extended */
338 rc = xioresolve(targetname, targetport, PF_INET/*!?*/,
339 SOCK_STREAM, IPPROTO_TCP,
340 &host, &socklen, ai_flags);
341 if (rc != STAT_OK) {
342 proxyvars->targetaddr = strdup(targetname);
343 } else {
344 #define LEN 16 /* www.xxx.yyy.zzz\0 */
345 if ((proxyvars->targetaddr = Malloc(LEN)) == NULL) {
346 return STAT_RETRYLATER;
348 snprintf(proxyvars->targetaddr, LEN, "%u.%u.%u.%u",
349 ((unsigned char *)&host.ip4.sin_addr.s_addr)[0],
350 ((unsigned char *)&host.ip4.sin_addr.s_addr)[1],
351 ((unsigned char *)&host.ip4.sin_addr.s_addr)[2],
352 ((unsigned char *)&host.ip4.sin_addr.s_addr)[3]);
353 #undef LEN
355 } else {
356 proxyvars->targetaddr = strdup(targetname);
359 proxyvars->targetport = htons(parseport(targetport, IPPROTO_TCP));
361 return STAT_OK;
364 int _xioopen_proxy_connect(struct single *sfd,
365 struct proxyvars *proxyvars,
366 int level) {
367 size_t offset;
368 char request[CONNLEN]; /* HTTP connection request line */
369 int rv;
370 char buff[BUFLEN+1]; /* for receiving HTTP reply headers */
371 #if CONNLEN > BUFLEN
372 #error not enough buffer space
373 #endif
374 char textbuff[2*BUFLEN+1]; /* just for sanitizing print data */
375 char *eol = buff;
376 int state;
377 ssize_t sresult;
379 /* generate proxy request header - points to final target */
380 if (proxyvars->version == NULL) {
381 proxyvars->version = "1.0";
383 rv = snprintf(request, CONNLEN, "CONNECT %s:%u HTTP/%s\r\n",
384 proxyvars->targetaddr, proxyvars->targetport, proxyvars->version);
385 if (rv >= CONNLEN || rv < 0) {
386 Error("_xioopen_proxy_connect(): PROXY CONNECT buffer too small");
387 return -1;
390 /* send proxy CONNECT request (target addr+port) */
391 * xiosanitize(request, strlen(request), textbuff) = '\0';
392 Info1("sending \"%s\"", textbuff);
393 /* write errors are assumed to always be hard errors, no retry */
394 if (writefull(sfd->fd, request, strlen(request)) < 0) {
395 Msg4(level, "write(%d, %p, "F_Zu"): %s",
396 sfd->fd, request, strlen(request), strerror(errno));
397 if (Close(sfd->fd) < 0) {
398 Info2("close(%d): %s", sfd->fd, strerror(errno));
400 return STAT_RETRYLATER;
403 if (proxyvars->authstring) {
404 /* send proxy authentication header */
405 # define XIOAUTHHEAD "Proxy-authorization: Basic "
406 # define XIOAUTHLEN 27
407 static const char *authhead = XIOAUTHHEAD;
408 # define HEADLEN 256
409 char *header, *next;
411 /* ...\r\n\0 */
412 if ((header =
413 Malloc(XIOAUTHLEN+((strlen(proxyvars->authstring)+2)/3)*4+3))
414 == NULL) {
415 return -1;
417 strcpy(header, authhead);
418 next = xiob64encodeline(proxyvars->authstring,
419 strlen(proxyvars->authstring),
420 strchr(header, '\0'));
421 *next = '\0';
422 Info1("sending \"%s\\r\\n\"", header);
423 *next++ = '\r'; *next++ = '\n'; *next++ = '\0';
424 if (writefull(sfd->fd, header, strlen(header)) < 0) {
425 Msg4(level, "write(%d, %p, "F_Zu"): %s",
426 sfd->fd, header, strlen(header), strerror(errno));
427 if (Close(sfd->fd) < 0) {
428 Info2("close(%d): %s", sfd->fd, strerror(errno));
430 return STAT_RETRYLATER;
433 free(header);
436 Info("sending \"\\r\\n\"");
437 if (writefull(sfd->fd, "\r\n", 2) < 0) {
438 Msg2(level, "write(%d, \"\\r\\n\", 2): %s",
439 sfd->fd, strerror(errno));
440 if (Close(sfd->fd) < 0) {
441 Info2("close(%d): %s", sfd->fd, strerror(errno));
443 return STAT_RETRYLATER;
446 /* request is kept for later error messages */
447 *strstr(request, " HTTP") = '\0';
449 /* receive proxy answer; looks like "HTTP/1.0 200 .*\r\nHeaders..\r\n\r\n" */
450 /* socat version 1 depends on a valid fd for data transfer; address
451 therefore cannot buffer data. So, to prevent reading beyond the end of
452 the answer headers, only single bytes are read. puh. */
453 state = XIOSTATE_HTTP1;
454 offset = 0; /* up to where the buffer is filled (relative) */
455 /*eol;*/ /* points to the first lineterm of the current line */
456 do {
457 sresult = xioproxy_recvbytes(sfd, buff+offset, 1, level);
458 if (sresult <= 0) {
459 state = XIOSTATE_ERROR;
460 break; /* leave read cycles */
463 switch (state) {
465 case XIOSTATE_HTTP1:
466 /* 0 or more bytes of first line received, no '\r' yet */
467 if (*(buff+offset) == '\r') {
468 eol = buff+offset;
469 state = XIOSTATE_HTTP2;
470 break;
472 if (proxyvars->ignorecr && *(buff+offset) == '\n') {
473 eol = buff+offset;
474 state = XIOSTATE_HTTP3;
475 break;
477 break;
479 case XIOSTATE_HTTP2:
480 /* first line received including '\r' */
481 if (*(buff+offset) != '\n') {
482 state = XIOSTATE_HTTP1;
483 break;
485 state = XIOSTATE_HTTP3;
486 break;
488 case XIOSTATE_HTTP3:
489 /* received status (first line) and "\r\n" */
490 if (*(buff+offset) == '\r') {
491 state = XIOSTATE_HTTP7;
492 break;
494 if (proxyvars->ignorecr && *(buff+offset) == '\n') {
495 state = XIOSTATE_HTTP8;
496 break;
498 state = XIOSTATE_HTTP4;
499 break;
501 case XIOSTATE_HTTP4:
502 /* within header */
503 if (*(buff+offset) == '\r') {
504 eol = buff+offset;
505 state = XIOSTATE_HTTP5;
506 break;
508 if (proxyvars->ignorecr && *(buff+offset) == '\n') {
509 eol = buff+offset;
510 state = XIOSTATE_HTTP6;
511 break;
513 break;
515 case XIOSTATE_HTTP5:
516 /* within header, '\r' received */
517 if (*(buff+offset) != '\n') {
518 state = XIOSTATE_HTTP4;
519 break;
521 state = XIOSTATE_HTTP6;
522 break;
524 case XIOSTATE_HTTP6:
525 /* received status (first line) and 1 or more headers, "\r\n" */
526 if (*(buff+offset) == '\r') {
527 state = XIOSTATE_HTTP7;
528 break;
530 if (proxyvars->ignorecr && *(buff+offset) == '\n') {
531 state = XIOSTATE_HTTP8;
532 break;
534 state = XIOSTATE_HTTP4;
535 break;
537 case XIOSTATE_HTTP7:
538 /* received status (first line), 0 or more headers, "\r\n\r" */
539 if (*(buff+offset) == '\n') {
540 state = XIOSTATE_HTTP8;
541 break;
543 if (*(buff+offset) == '\r') {
544 if (proxyvars->ignorecr) {
545 break; /* ignore it, keep waiting for '\n' */
546 } else {
547 state = XIOSTATE_HTTP5;
549 break;
551 state = XIOSTATE_HTTP4;
552 break;
555 ++offset;
557 /* end of status line reached */
558 if (state == XIOSTATE_HTTP3) {
559 char *ptr;
560 /* set a terminating null - on or after CRLF? */
561 *(buff+offset) = '\0';
563 * xiosanitize(buff, Min(offset, (sizeof(textbuff)-1)>>1), textbuff)
564 = '\0';
565 Info1("proxy_connect: received answer \"%s\"", textbuff);
566 *eol = '\0';
567 * xiosanitize(buff, Min(strlen(buff), (sizeof(textbuff)-1)>>1),
568 textbuff) = '\0';
569 if (strncmp(buff, "HTTP/1.0 ", 9) &&
570 strncmp(buff, "HTTP/1.1 ", 9)) {
571 /* invalid answer */
572 Msg1(level, "proxy: invalid answer \"%s\"", textbuff);
573 return STAT_RETRYLATER;
575 ptr = buff+9;
577 /* skip multiple spaces */
578 while (*ptr == ' ') ++ptr;
580 /* HTTP answer */
581 if (strncmp(ptr, "200", 3)) {
582 /* not ok */
583 /* CERN:
584 "HTTP/1.0 200 Connection established"
585 "HTTP/1.0 400 Invalid request "CONNECT 10.244.9.3:8080 HTTP/1.0" (unknown method)"
586 "HTTP/1.0 403 Forbidden - by rule"
587 "HTTP/1.0 407 Proxy Authentication Required"
588 Proxy-Authenticate: Basic realm="Squid proxy-caching web server"
589 > 50 72 6f 78 79 2d 61 75 74 68 6f 72 69 7a 61 74 Proxy-authorizat
590 > 69 6f 6e 3a 20 42 61 73 69 63 20 61 57 4e 6f 63 ion: Basic aWNoc
591 > 32 56 73 59 6e 4e 30 4f 6e 4e 30 63 6d 56 75 5a 2VsYnN0OnN0cmVuZ
592 > 32 64 6c 61 47 56 70 62 51 3d 3d 0d 0a 2dlaGVpbQ==..
593 b64encode("username:password")
594 "HTTP/1.0 500 Can't connect to host"
596 /* Squid:
597 "HTTP/1.0 400 Bad Request"
598 "HTTP/1.0 403 Forbidden"
599 "HTTP/1.0 503 Service Unavailable"
600 interesting header: "X-Squid-Error: ERR_CONNECT_FAIL 111" */
601 /* Apache:
602 "HTTP/1.0 400 Bad Request"
603 "HTTP/1.1 405 Method Not Allowed"
605 /* WTE:
606 "HTTP/1.1 200 Connection established"
607 "HTTP/1.1 404 Host not found or not responding, errno: 79"
608 "HTTP/1.1 404 Host not found or not responding, errno: 32"
609 "HTTP/1.1 404 Host not found or not responding, errno: 13"
611 /* IIS:
612 "HTTP/1.1 404 Object Not Found"
614 ptr += 3;
615 while (*ptr == ' ') ++ptr;
617 Msg2(level, "%s: %s", request, ptr);
618 return STAT_RETRYLATER;
621 /* ok!! */
622 /* "HTTP/1.0 200 Connection established" */
623 /*Info1("proxy: \"%s\"", textbuff+13);*/
624 offset = 0;
626 } else if (state == XIOSTATE_HTTP6) {
627 /* end of a header line reached */
628 char *endp;
630 /* set a terminating null */
631 *(buff+offset) = '\0';
633 endp =
634 xiosanitize(buff, Min(offset, (sizeof(textbuff)-1)>>1),
635 textbuff);
636 *endp = '\0';
637 Info1("proxy_connect: received header \"%s\"", textbuff);
638 offset = 0;
641 } while (state != XIOSTATE_HTTP8 && offset < BUFLEN);
643 if (state == XIOSTATE_ERROR) {
644 return STAT_RETRYLATER;
647 if (offset >= BUFLEN) {
648 Msg1(level, "proxy answer exceeds %d bytes, aborting", BUFLEN);
649 return STAT_NORETRY;
652 return STAT_OK;
655 #endif /* WITH_PROXY */