1 /* $Id: ncbi_connutil.c,v 6.254 2016/09/15 03:59:21 fukanchi Exp $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Denis Vakatov, Anton Lavrentiev
27 *
28 * File Description:
29 * Auxiliary API, mostly CONN-, URL-, and MIME-related
30 * (see in "ncbi_connutil.h" for more details).
31 *
32 */
33
34 #include "ncbi_ansi_ext.h"
35 #include "ncbi_connssl.h"
36 #include "ncbi_priv.h"
37 #include "ncbi_servicep.h"
38 #include <connect/ncbi_connutil.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <stdlib.h>
42
43 #define NCBI_USE_ERRCODE_X Connect_Util
44
45
46 #define CONN_NET_INFO_MAGIC 0x600DF00D
47
48 #define SizeOf(arr) (sizeof(arr) / sizeof((arr)[0]))
49
50
51 static TNCBI_BigCount s_FWPorts[1024 / sizeof(TNCBI_BigCount)] = { 0 };
52
53
x_StrcatCRLF(char * dst,const char * src)54 static char* x_StrcatCRLF(char* dst, const char* src)
55 {
56 size_t dstlen = dst && *dst ? strlen(dst) : 0;
57 size_t srclen = src && *src ? strlen(src) : 0;
58 if (dstlen || srclen) {
59 size_t len;
60 char* temp;
61 if (dstlen && dst[dstlen - 1] == '\n') {
62 if (--dstlen && dst[dstlen - 1] == '\r')
63 --dstlen;
64 }
65 if (srclen && src[srclen - 1] == '\n') {
66 if (--srclen && src[srclen - 1] == '\r')
67 --srclen;
68 }
69 len = (dstlen ? dstlen + 2 : 0) + (srclen ? srclen + 2 : 0) + 1;
70 if (!(temp = (char*)(dst ? realloc(dst, len) : malloc (len))))
71 return 0;
72 dst = temp;
73 if (dstlen) {
74 temp += dstlen;
75 memcpy(temp, "\r\n", 3);
76 temp += 2;
77 }
78 if (srclen) {
79 memcpy(temp, src, srclen);
80 temp += srclen;
81 memcpy(temp, "\r\n", 3);
82 }
83 }
84 return dst;
85 }
86
87
x_GetValue(const char * service,const char * param,char * value,size_t value_size,const char * def_value,int * generic)88 static const char* x_GetValue(const char* service, const char* param,
89 char* value, size_t value_size,
90 const char* def_value, int* /*bool*/ generic)
91 {
92 char buf[128];
93 const char* val;
94 char* s;
95
96 if (!value || value_size <= 0)
97 return 0;
98 *value = '\0';
99
100 if (!param || !*param)
101 return 0;
102
103 *generic = 0/*false*/;
104 if (service && *service) {
105 /* Service-specific inquiry */
106 int/*bool*/ end;
107 char temp[sizeof(buf)];
108 size_t slen = strlen(service);
109 size_t plen = strlen(param) + 1;
110 size_t len = slen + 1 + plen;
111
112 if (strncasecmp(param, DEF_CONN_REG_SECTION "_",
113 sizeof(DEF_CONN_REG_SECTION)) != 0) {
114 len += sizeof(DEF_CONN_REG_SECTION);
115 end = 0/*false*/;
116 } else
117 end = 1/*true*/;
118 if (len > sizeof(buf))
119 return 0;
120
121 /* First, environment search for 'service_CONN_param' */
122 s = (char*) memcpy(buf, service, slen) + slen;
123 *s++ = '_';
124 if (!end) {
125 memcpy(s, DEF_CONN_REG_SECTION, sizeof(DEF_CONN_REG_SECTION) - 1);
126 s += sizeof(DEF_CONN_REG_SECTION) - 1;
127 *s++ = '_';
128 }
129 memcpy(s, param, plen);
130 CORE_LOCK_READ;
131 if ((val = getenv(strupr((char*) memcpy(temp, buf, len)))) != 0
132 || (memcmp(temp, buf, len) != 0 && (val = getenv(buf)) != 0)) {
133 strncpy0(value, val, value_size - 1);
134 CORE_UNLOCK;
135 return value;
136 }
137 CORE_UNLOCK;
138
139 /* Next, search for 'CONN_param' in '[service]' registry section */
140 buf[slen++] = '\0';
141 s = buf + slen;
142 CORE_REG_GET(buf, s, value, value_size, end ? def_value : 0);
143 if (*value || end)
144 return value;
145 } else {
146 /* Common case. Form 'CONN_param' */
147 size_t plen = strlen(param) + 1;
148 if (strncasecmp(param, DEF_CONN_REG_SECTION "_",
149 sizeof(DEF_CONN_REG_SECTION)) != 0) {
150 if (sizeof(DEF_CONN_REG_SECTION) + plen > sizeof(buf))
151 return 0;
152 s = buf;
153 memcpy(s, DEF_CONN_REG_SECTION, sizeof(DEF_CONN_REG_SECTION) - 1);
154 s += sizeof(DEF_CONN_REG_SECTION) - 1;
155 *s++ = '_';
156 } else {
157 if (plen > sizeof(buf))
158 return 0;
159 s = buf;
160 }
161 memcpy(s, param, plen);
162 s = strupr(buf);
163 }
164
165 *generic = 1/*true*/;
166 /* Environment search for 'CONN_param' */
167 CORE_LOCK_READ;
168 if ((val = getenv(s)) != 0) {
169 strncpy0(value, val, value_size - 1);
170 CORE_UNLOCK;
171 return value;
172 }
173 CORE_UNLOCK;
174
175 /* Last resort: Search for 'param' in default registry section */
176 s += sizeof(DEF_CONN_REG_SECTION);
177 CORE_REG_GET(DEF_CONN_REG_SECTION, s, value, value_size, def_value);
178 return value;
179 }
180
181
s_GetValue(const char * service,const char * param,char * value,size_t value_size,const char * def_value,int * generic)182 static const char* s_GetValue(const char* service, const char* param,
183 char* value, size_t value_size,
184 const char* def_value, int* /*bool*/ generic)
185 {
186 const char* retval = x_GetValue(service, param,
187 value, value_size, def_value, generic);
188 if (retval) {
189 /*strip enveloping quotes*/
190 size_t len = strlen(value);
191 if (len > 1 && (value[0] == '"' || value[0] == '\'')
192 && strchr(value + 1, value[0]) == value + len - 1) {
193 if (len -= 2)
194 memcpy(value, value + 1, len);
195 value[len] = '\0';
196 }
197 assert(retval == value);
198 }
199 return retval;
200 }
201
202
ConnNetInfo_GetValue(const char * service,const char * param,char * value,size_t value_size,const char * def_value)203 extern const char* ConnNetInfo_GetValue(const char* service, const char* param,
204 char* value, size_t value_size,
205 const char* def_value)
206 {
207 int/*bool*/ dummy;
208 return s_GetValue(service, param, value, value_size, def_value, &dummy);
209 }
210
211
ConnNetInfo_Boolean(const char * str)212 extern int/*bool*/ ConnNetInfo_Boolean(const char* str)
213 {
214 return str && *str && (strcmp (str, "1") == 0 ||
215 strcasecmp(str, "on") == 0 ||
216 strcasecmp(str, "yes") == 0 ||
217 strcasecmp(str, "true") == 0)
218 ? 1/*true*/ : 0/*false*/;
219 }
220
221
x_ParseScheme(const char * str,size_t len)222 static EURLScheme x_ParseScheme(const char* str, size_t len)
223 {
224 if (len == 5 && strncasecmp(str, "https", len) == 0)
225 return eURL_Https;
226 if (len == 4 && strncasecmp(str, "http", len) == 0)
227 return eURL_Http;
228 if (len == 4 && strncasecmp(str, "file", len) == 0)
229 return eURL_File;
230 if (len == 3 && strncasecmp(str, "ftp", len) == 0)
231 return eURL_Ftp;
232 return eURL_Unspec;
233 }
234
235
x_Num(unsigned int num,char buf[])236 static const char* x_Num(unsigned int num, char buf[])
237 {
238 sprintf(buf, "(#%u)", num);
239 return buf;
240 }
241
242
x_Scheme(EURLScheme scheme,char buf[])243 static const char* x_Scheme(EURLScheme scheme, char buf[])
244 {
245 switch (scheme) {
246 case eURL_Unspec:
247 return 0;
248 case eURL_Https:
249 return "HTTPS";
250 case eURL_Http:
251 return "HTTP";
252 case eURL_File:
253 return "FILE";
254 case eURL_Ftp:
255 return "FTP";
256 default:
257 break;
258 }
259 return x_Num(scheme, buf);
260 }
261
262
x_ParseFirewall(const char * str,int generic)263 static EFWMode x_ParseFirewall(const char* str, int/*bool*/ generic)
264 {
265 if (!*str) /*NB: not actually necessary but faster*/
266 return eFWMode_Legacy;
267 if (strcasecmp(str, "adaptive") == 0 || ConnNetInfo_Boolean(str))
268 return eFWMode_Adaptive;
269 if (strcasecmp(str, "firewall") == 0)
270 return eFWMode_Firewall;
271 if (strcasecmp(str, "fallback") == 0)
272 return eFWMode_Fallback;
273 for (;;) {
274 int n;
275 unsigned short port;
276 if (sscanf(str, "%hu%n", &port, &n) < 1 || !port)
277 break;
278 if (generic)
279 SERV_AddFirewallPort(port);
280 str += n;
281 if (!*(str += strspn(str, " \t")))
282 return eFWMode_Fallback;
283 }
284 return eFWMode_Legacy;
285 }
286
287
s_InfoIsValid(const SConnNetInfo * info)288 static int/*bool*/ s_InfoIsValid(const SConnNetInfo* info)
289 {
290 assert(info->magic == CONN_NET_INFO_MAGIC);
291 return info->magic == CONN_NET_INFO_MAGIC ? 1/*true*/ : 0/*false*/;
292 }
293
294
295 /****************************************************************************
296 * ConnNetInfo API
297 */
298
299
ConnNetInfo_Create(const char * service)300 extern SConnNetInfo* ConnNetInfo_Create(const char* service)
301 {
302 #define REG_VALUE(name, value, def_value) \
303 s_GetValue(service, name, value, sizeof(value), def_value, &generic)
304
305 int/*bool*/ generic;
306 SConnNetInfo* info;
307 /* aux. storage */
308 char str[1024];
309 size_t len;
310 long val;
311 double dbl;
312 char* e;
313
314 len = service ? strlen(service) : 0;
315
316 /* NB: created *NOT* cleared up with all 0s */
317 if (!(info = (SConnNetInfo*) malloc(sizeof(*info) + len)))
318 return 0/*failure*/;
319 info->reserved = 0/*MBZ*/;
320
321 /* store the service name, which this structure has been created for */
322 memcpy((char*) info->svc, service ? service : "", ++len);
323
324 /* client host: default */
325 info->client_host[0] = '\0';
326
327 /* scheme */
328 info->scheme = eURL_Unspec;
329
330 /* request method */
331 REG_VALUE(REG_CONN_REQ_METHOD, str, DEF_CONN_REQ_METHOD);
332 if (!*str || strcasecmp(str, "ANY") == 0)
333 info->req_method = eReqMethod_Any;
334 else if (strcasecmp(str, "POST") == 0)
335 info->req_method = eReqMethod_Post;
336 else if (strcasecmp(str, "GET") == 0)
337 info->req_method = eReqMethod_Get;
338 /* NB: HEAD, CONNECT, etc not allowed here */
339
340 /* compatibility */
341 info->version = 0;
342
343 /* external mode */
344 REG_VALUE(REG_CONN_EXTERNAL, str, DEF_CONN_EXTERNAL);
345 info->external = ConnNetInfo_Boolean(str);
346
347 /* firewall mode */
348 REG_VALUE(REG_CONN_FIREWALL, str, DEF_CONN_FIREWALL);
349 info->firewall = x_ParseFirewall(str, generic);
350
351 /* stateless client */
352 REG_VALUE(REG_CONN_STATELESS, str, DEF_CONN_STATELESS);
353 info->stateless = ConnNetInfo_Boolean(str);
354
355 /* prohibit use of the local load balancer */
356 REG_VALUE(REG_CONN_LB_DISABLE, str, DEF_CONN_LB_DISABLE);
357 info->lb_disable = ConnNetInfo_Boolean(str);
358
359 /* level of debug printout */
360 REG_VALUE(REG_CONN_DEBUG_PRINTOUT, str, DEF_CONN_DEBUG_PRINTOUT);
361 if (ConnNetInfo_Boolean(str)
362 || (*str && strcasecmp(str, "some") == 0)) {
363 info->debug_printout = eDebugPrintout_Some;
364 } else if (*str && (strcasecmp(str, "data") == 0 ||
365 strcasecmp(str, "all") == 0)) {
366 info->debug_printout = eDebugPrintout_Data;
367 } else
368 info->debug_printout = eDebugPrintout_None;
369
370 /* username */
371 REG_VALUE(REG_CONN_USER, info->user, DEF_CONN_USER);
372
373 /* password */
374 REG_VALUE(REG_CONN_PASS, info->pass, DEF_CONN_PASS);
375
376 /* hostname */
377 REG_VALUE(REG_CONN_HOST, info->host, DEF_CONN_HOST);
378
379 /* port # */
380 REG_VALUE(REG_CONN_PORT, str, DEF_CONN_PORT);
381 errno = 0;
382 if (*str && (val = strtoul(str, &e, 10)) > 0 && !errno
383 && !*e && val < (1 << 16)) {
384 info->port = (unsigned short) val;
385 } else
386 info->port = 0/*default*/;
387
388 /* path */
389 REG_VALUE(REG_CONN_PATH, info->path, DEF_CONN_PATH);
390
391 /* args */
392 REG_VALUE(REG_CONN_ARGS, info->args, DEF_CONN_ARGS);
393
394 /* HTTP proxy server */
395 REG_VALUE(REG_CONN_HTTP_PROXY_HOST, info->http_proxy_host,
396 DEF_CONN_HTTP_PROXY_HOST);
397 if (info->http_proxy_host[0]) {
398 REG_VALUE(REG_CONN_HTTP_PROXY_PORT, str, DEF_CONN_HTTP_PROXY_PORT);
399 errno = 0;
400 if (*str && (val = strtoul(str, &e, 10)) > 0
401 && !errno && !*e && val < (1 << 16)) {
402 info->http_proxy_port = (unsigned short) val;
403 } else
404 info->http_proxy_port = 0/*none*/;
405 /* HTTP proxy username */
406 REG_VALUE(REG_CONN_HTTP_PROXY_USER, info->http_proxy_user,
407 DEF_CONN_HTTP_PROXY_USER);
408 /* HTTP proxy password */
409 REG_VALUE(REG_CONN_HTTP_PROXY_PASS, info->http_proxy_pass,
410 DEF_CONN_HTTP_PROXY_PASS);
411 /* HTTP proxy leakout */
412 REG_VALUE(REG_CONN_HTTP_PROXY_LEAK, str, DEF_CONN_HTTP_PROXY_LEAK);
413 info->http_proxy_leak = ConnNetInfo_Boolean(str);
414 } else {
415 info->http_proxy_port = 0;
416 info->http_proxy_user[0] = '\0';
417 info->http_proxy_pass[0] = '\0';
418 info->http_proxy_leak = 0;
419 }
420
421 /* push HTTP auth tags */
422 REG_VALUE(REG_CONN_HTTP_PUSH_AUTH, str, DEF_CONN_HTTP_PUSH_AUTH);
423 info->http_push_auth = ConnNetInfo_Boolean(str);
424
425 /* max. # of attempts to establish connection */
426 REG_VALUE(REG_CONN_MAX_TRY, str, 0);
427 val = atoi(str);
428 info->max_try = (unsigned short)(val > 0 ? val : DEF_CONN_MAX_TRY);
429
430 /* connection timeout */
431 REG_VALUE(REG_CONN_TIMEOUT, str, 0);
432 len = strlen(str);
433 if (len < 3 || 8 < len || strncasecmp(str, "infinite", len) != 0) {
434 if (!*str || (dbl = NCBI_simple_atof(str, &e)) < 0.0 || errno || *e)
435 dbl = DEF_CONN_TIMEOUT;
436 info->tmo.sec = (unsigned int) dbl;
437 info->tmo.usec = (unsigned int)((dbl - info->tmo.sec) * 1000000.0);
438 if (dbl && !(info->tmo.sec | info->tmo.usec))
439 info->tmo.usec = 1/*protect underflow*/;
440 info->timeout = &info->tmo;
441 } else
442 info->timeout = kInfiniteTimeout/*0*/;
443
444 /* HTTP user header */
445 REG_VALUE(REG_CONN_HTTP_USER_HEADER, str, DEF_CONN_HTTP_USER_HEADER);
446 info->http_user_header = *str ? x_StrcatCRLF(NULL, str) : 0;
447
448 /* default referer */
449 ConnNetInfo_GetValue(0, REG_CONN_HTTP_REFERER, str, sizeof(str),
450 DEF_CONN_HTTP_REFERER);
451 info->http_referer = *str ? strdup(str) : 0;
452
453 /* credentials */
454 info->credentials = 0;
455
456 /* magic */
457 info->magic = CONN_NET_INFO_MAGIC;
458
459 /* done */
460 return info;
461 #undef REG_VALUE
462 }
463
464
ConnNetInfo_ParseURL(SConnNetInfo * info,const char * url)465 extern int/*bool*/ ConnNetInfo_ParseURL(SConnNetInfo* info, const char* url)
466 {
467 /* URL elements and their parsed lengths as passed */
468 const char *user, *pass, *host, *path, *args;
469 size_t userlen, passlen, hostlen, pathlen, argslen;
470 EURLScheme scheme;
471 const char* s;
472 size_t len;
473 long port;
474 char* p;
475
476 if (!s_InfoIsValid(info) || !url)
477 return 0/*failure*/;
478
479 if (!*url)
480 return 1/*success*/;
481
482 if ((info->req_method & ~eReqMethod_v1) == eReqMethod_Connect) {
483 len = strlen(url);
484 s = (const char*) memchr(url, ':', len);
485 if (s)
486 len = (size_t)(s - url);
487 if (len >= sizeof(info->host))
488 return 0/*failure*/;
489 if (s) {
490 errno = 0;
491 port = strtol(++s, &p, 10);
492 if (errno || s == p || *p)
493 return 0/*failure*/;
494 if (!port || port ^ (port & 0xFFFF))
495 return 0/*failure*/;
496 info->port = port;
497 }
498 if (len) {
499 memcpy(info->host, url, len);
500 info->host[len] = '\0';
501 }
502 return 1/*success*/;
503 }
504
505 port = -1/*unassigned*/;
506
507 /* "scheme://user:pass@host:port" first [any optional] */
508 if ((s = strstr(url, "//")) != 0) {
509 if (s > url) {
510 if (s[-1] != ':')
511 return 0/*failure*/;
512 len = (size_t)(s - url) - 1;
513 if ((scheme = x_ParseScheme(url, len)) == eURL_Unspec)
514 return 0/*failure*/;
515 } else
516 scheme = (EURLScheme) info->scheme;
517 host = s + 2;
518 hostlen = strcspn(host, "/?#");
519 path = host + hostlen;
520
521 /* username:password */
522 if (!hostlen) {
523 user = pass = host = scheme == eURL_File ? "" : 0;
524 userlen = passlen = 0;
525 } else {
526 if (!(s = (const char*) memrchr(host, '@', hostlen))) {
527 user = pass = "";
528 userlen = passlen = 0;
529 } else {
530 user = host;
531 userlen = (size_t)(s - user);
532 host = ++s;
533 hostlen = (size_t)(path - s);
534 if (!hostlen)
535 return 0/*failure*/;
536 if (!(s = (const char*) memchr(user, ':', userlen))) {
537 pass = "";
538 passlen = 0;
539 } else {
540 userlen = (size_t)(s++ - user);
541 pass = s++;
542 passlen = (size_t)(host - s);
543 }
544 }
545
546 /* port, if any */
547 if ((s = (const char*) memchr(host, ':', hostlen)) != 0) {
548 if (!(hostlen = (size_t)(s - host)))
549 return 0/*failure*/;
550 errno = 0;
551 port = strtol(++s, &p, 10);
552 if (errno || s == p || p != path)
553 return 0/*failure*/;
554 if (!port || port ^ (port & 0xFFFF))
555 return 0/*failure*/;
556 } else
557 port = 0/*default*/;
558
559 if (userlen >= sizeof(info->user) ||
560 passlen >= sizeof(info->pass) ||
561 hostlen >= sizeof(info->host)) {
562 return 0/*failure*/;
563 }
564 }
565 } else {
566 scheme = (EURLScheme) info->scheme;
567 user = pass = host = 0;
568 userlen = passlen = hostlen = 0;
569 path = url;
570 }
571
572 pathlen = (scheme == eURL_Https || scheme == eURL_Http
573 ? strcspn(path, "?#") : strlen(path));
574 args = path + pathlen;
575
576 if (path != url || *path == '/') {
577 /* absolute path */
578 p = info->path;
579 len = 0;
580 if (!pathlen) {
581 path = "/";
582 pathlen = 1;
583 }
584 } else {
585 /* relative path */
586 if (!(p = strrchr(info->path, '/'))) {
587 p = info->path;
588 len = 0;
589 } else
590 len = (size_t)(++p - info->path);
591 if (!pathlen)
592 path = 0;
593 }
594 if (pathlen + len >= sizeof(info->path))
595 return 0/*failure*/;
596
597 /* arguments and fragment */
598 if (*args) {
599 const char* frag;
600 argslen = strlen(args);
601 if (*args == '#')
602 frag = args;
603 else if (!(frag = strchr(++args/*NB: args[0]=='?'*/, '#')))
604 frag = args + --argslen;
605 else
606 argslen--;
607 assert(!*frag || *frag == '#');
608
609 if (*frag) {
610 /* if there is a new fragment, the entire args get overridden */
611 if (!frag[1])
612 argslen--; /* don't store the empty fragment # */
613 if (argslen >= sizeof(info->args))
614 return 0/*failure*/;
615 len = 0;
616 } else if ((s = strchr(info->args, '#')) != 0) {
617 /* there is no new fragment, but there was the old one: keep it */
618 len = strlen(s);
619 if (argslen + len >= sizeof(info->args))
620 return 0/*failure*/;
621 memmove(info->args + argslen, s, len);
622 } else {
623 if (argslen >= sizeof(info->args))
624 return 0/*failure*/;
625 len = 0;
626 }
627 memcpy(info->args, args, argslen);
628 info->args[argslen + len] = '\0';
629 } else if ((scheme == eURL_Https || scheme == eURL_Http)
630 && (args = strchr(info->args, '#'))) {
631 /* keep the old fragment, if any, but drop all args */
632 memmove(info->args, args, strlen(args) + 1);
633 } else
634 info->args[0] = '\0';
635 if (path) {
636 memcpy(p, path, pathlen);
637 p[pathlen] = '\0';
638 }
639 if (user) {
640 assert(pass);
641 memcpy(info->user, user, userlen);
642 info->user[userlen] = '\0';
643 memcpy(info->pass, pass, passlen);
644 info->pass[passlen] = '\0';
645 }
646 if (port >= 0 || scheme == eURL_File)
647 info->port = port < 0 ? 0 : port;
648 if (host) {
649 memcpy(info->host, host, hostlen);
650 info->host[hostlen] = '\0';
651 }
652 info->scheme = scheme;
653 return 1/*success*/;
654 }
655
656
ConnNetInfo_SetUserHeader(SConnNetInfo * info,const char * user_header)657 extern int/*bool*/ ConnNetInfo_SetUserHeader(SConnNetInfo* info,
658 const char* user_header)
659 {
660 if (!s_InfoIsValid(info))
661 return 0/*failure*/;
662
663 if (info->http_user_header)
664 free((void*) info->http_user_header);
665 if (!user_header || !*user_header)
666 info->http_user_header = 0;
667 else if (!(info->http_user_header = x_StrcatCRLF(NULL, user_header)))
668 return 0/*failure*/;
669 return 1/*success*/;
670 }
671
672
ConnNetInfo_AppendUserHeader(SConnNetInfo * info,const char * user_header)673 extern int/*bool*/ ConnNetInfo_AppendUserHeader(SConnNetInfo* info,
674 const char* user_header)
675 {
676 char* new_header;
677
678 if (!s_InfoIsValid(info))
679 return 0/*failure*/;
680
681 if (!info->http_user_header || !*info->http_user_header)
682 return ConnNetInfo_SetUserHeader(info, user_header);
683
684 new_header = (char*) info->http_user_header;
685 if (!(new_header = x_StrcatCRLF(new_header, user_header)))
686 return 0/*failure*/;
687
688 info->http_user_header = new_header;
689 return 1/*success*/;
690 }
691
692
x_TagValueMatches(const char * oldval,size_t oldvallen,const char * newval,size_t newvallen)693 static int/*bool*/ x_TagValueMatches(const char* oldval, size_t oldvallen,
694 const char* newval, size_t newvallen)
695 {
696 assert(newvallen > 0);
697 while (oldvallen > 0) {
698 do {
699 if (!isspace((unsigned char)(*oldval)))
700 break;
701 ++oldval;
702 } while (--oldvallen > 0);
703 if (oldvallen < newvallen)
704 break;
705 if (strncasecmp(oldval, newval, newvallen) == 0
706 && (oldvallen == newvallen
707 || isspace((unsigned char) oldval[newvallen]))) {
708 return 1/*true*/;
709 }
710 assert(oldvallen > 0);
711 do {
712 if ( isspace((unsigned char)(*oldval)))
713 break;
714 ++oldval;
715 } while (--oldvallen > 0);
716 }
717 return 0/*false*/;
718 }
719
720
721 enum EUserHeaderOp {
722 eUserHeaderOp_Delete,
723 eUserHeaderOp_Extend,
724 eUserHeaderOp_Override
725 };
726
727
s_ModifyUserHeader(SConnNetInfo * info,const char * user_header,enum EUserHeaderOp op)728 static int/*bool*/ s_ModifyUserHeader(SConnNetInfo* info,
729 const char* user_header,
730 enum EUserHeaderOp op)
731 {
732 int/*bool*/ retval;
733 size_t newlinelen;
734 size_t newhdrlen;
735 char* newline;
736 char* newhdr;
737 size_t hdrlen;
738 char* hdr;
739
740 if (!s_InfoIsValid(info))
741 return 0/*failure*/;
742
743 if (!user_header || !(newhdrlen = strlen(user_header)))
744 return 1/*success*/;
745
746 if (!(hdr = (char*) info->http_user_header) || !(hdrlen = strlen(hdr))) {
747 if (op == eUserHeaderOp_Delete)
748 return 1/*success*/;
749 if (!hdr && !(hdr = strdup("")))
750 return 0/*failure*/;
751 hdrlen = 0;
752 }
753
754 /* NB: "user_header" can be part of "info->user_header",
755 * so create a copy of it even for delete operations! */
756 if (!(newhdr = (char*) malloc(newhdrlen + 1)))
757 return 0/*failure*/;
758 memcpy(newhdr, user_header, newhdrlen + 1);
759
760 retval = 1/*assume best: success*/;
761 for (newline = newhdr; *newline; newline += newlinelen) {
762 char* eol = strchr(newline, '\n');
763 char* eot = strchr(newline, ':');
764 size_t newtaglen;
765 char* newtagval;
766 size_t linelen;
767 size_t newlen;
768 char* line;
769 size_t off;
770
771 /* line & taglen */
772 newlinelen = (size_t)
773 (eol ? eol - newline + 1 : newhdr + newhdrlen - newline);
774 if (!eot || eot >= newline + newlinelen)
775 goto ignore;
776 if (!(newtaglen = (size_t)(eot - newline)))
777 goto ignore;
778
779 /* tag value */
780 newtagval = newline + newtaglen;
781 while (++newtagval < newline + newlinelen) {
782 if (!isspace((unsigned char)(*newtagval)))
783 break;
784 }
785 switch (op) {
786 case eUserHeaderOp_Override:
787 newlen = newtagval < newline + newlinelen ? newlinelen : 0;
788 break;
789 case eUserHeaderOp_Delete:
790 newlen = 0;
791 break;
792 case eUserHeaderOp_Extend:
793 /* NB: how much additional space is required */
794 if (!(newlen = newlinelen - (size_t)(newtagval - newline)))
795 goto ignore;
796 break;
797 default:
798 assert(0);
799 retval = 0/*failure*/;
800 newlen = 0;
801 break;
802 }
803 if (newlen && eol) {
804 if (eol[-1] == '\r')
805 newlen -= 2;
806 else
807 newlen--;
808 assert(newlen);
809 }
810
811 for (line = hdr; *line; line += linelen) {
812 size_t taglen;
813 char* temp;
814 size_t len;
815
816 eol = strchr(line, '\n');
817 eot = strchr(line, ':');
818
819 linelen = (size_t)(eol ? eol - line + 1 : hdr + hdrlen - line);
820 if (!eot || eot >= line + linelen)
821 continue;
822
823 taglen = (size_t)(eot - line);
824 if (newtaglen != taglen || strncasecmp(newline, line, taglen) != 0)
825 continue;
826 assert(0 < taglen && taglen <= linelen);
827
828 if (newlen) {
829 assert(op != eUserHeaderOp_Delete);
830 off = !eol ? 0 : eol[-1] != '\r' ? 1 : 2;
831 if (op == eUserHeaderOp_Extend) {
832 assert(line[taglen] == ':');
833 taglen++;
834 if (x_TagValueMatches(line + taglen, linelen-off - taglen,
835 newtagval, newlen)) {
836 goto ignore;
837 }
838 line += linelen-off;
839 linelen = off;
840 newlen++;
841 len = 0;
842 } else
843 len = linelen-off;
844 } else
845 len = 0/*==newlen*/;
846
847 off = (size_t)(line - hdr);
848 if (len < newlen) {
849 len = newlen - len;
850 if (!(temp = (char*) realloc(hdr, hdrlen + len + 1))) {
851 retval = 0/*failure*/;
852 goto ignore;
853 }
854 hdr = temp;
855 line = temp + off;
856 memmove(line + len, line, hdrlen - off + 1);
857 hdrlen += len;
858 linelen += len;
859 if (op == eUserHeaderOp_Extend) {
860 memcpy(line + 1, newtagval, newlen - 1);
861 *line = ' ';
862 newlen = 0;
863 break;
864 }
865 } else if (len > newlen) {
866 assert(op == eUserHeaderOp_Override);
867 hdrlen -= len;
868 memmove(line + newlen, line + len, hdrlen - off + 1);
869 hdrlen += newlen;
870 }
871 if (newlen) {
872 assert(op == eUserHeaderOp_Override);
873 memcpy(line, newline, newlen);
874 newlen = 0;
875 continue;
876 }
877
878 hdrlen -= linelen;
879 memmove(line, line + linelen, hdrlen - off + 1);
880 linelen = 0;
881 }
882
883 if (!newlen) {
884 ignore:
885 if (op == eUserHeaderOp_Delete)
886 continue;
887 off = (size_t)(newline - newhdr);
888 newhdrlen -= newlinelen;
889 memmove(newline, newline + newlinelen, newhdrlen - off + 1);
890 newlinelen = 0;
891 }
892 }
893
894 info->http_user_header = hdr;
895 if (retval && op != eUserHeaderOp_Delete)
896 retval = ConnNetInfo_AppendUserHeader(info, newhdr);
897 free(newhdr);
898
899 return retval;
900 }
901
902
ConnNetInfo_OverrideUserHeader(SConnNetInfo * info,const char * header)903 extern int/*bool*/ ConnNetInfo_OverrideUserHeader(SConnNetInfo* info,
904 const char* header)
905 {
906 return s_ModifyUserHeader(info, header, eUserHeaderOp_Override);
907 }
908
909
ConnNetInfo_DeleteUserHeader(SConnNetInfo * info,const char * header)910 extern void ConnNetInfo_DeleteUserHeader(SConnNetInfo* info,
911 const char* header)
912 {
913 verify(s_ModifyUserHeader(info, header, eUserHeaderOp_Delete));
914 }
915
916
ConnNetInfo_ExtendUserHeader(SConnNetInfo * info,const char * header)917 extern int/*bool*/ ConnNetInfo_ExtendUserHeader(SConnNetInfo* info,
918 const char* header)
919 {
920 return s_ModifyUserHeader(info, header, eUserHeaderOp_Extend);
921 }
922
923
ConnNetInfo_AppendArg(SConnNetInfo * info,const char * arg,const char * val)924 extern int/*bool*/ ConnNetInfo_AppendArg(SConnNetInfo* info,
925 const char* arg,
926 const char* val)
927 {
928 size_t len, used;
929
930 if (!s_InfoIsValid(info))
931 return 0/*failure*/;
932
933 if (!arg || !*arg)
934 return 1/*success*/;
935
936 used = strlen(info->args);
937 len = strlen(arg);
938
939 if (used + (used ? 1/*&*/ : 0) + len +
940 (val && *val ? 1/*=*/ + strlen(val) : 0) >= sizeof(info->args)) {
941 return 0/*failure*/;
942 }
943
944 if (used)
945 info->args[used++] = '&';
946 strcpy(info->args + used, arg);
947 if (val && *val) {
948 used += len;
949 info->args[used++] = '=';
950 strcpy(info->args + used, val);
951 }
952 return 1/*success*/;
953 }
954
955
ConnNetInfo_PrependArg(SConnNetInfo * info,const char * arg,const char * val)956 extern int/*bool*/ ConnNetInfo_PrependArg(SConnNetInfo* info,
957 const char* arg,
958 const char* val)
959 {
960 size_t len, off, used;
961
962 if (!s_InfoIsValid(info))
963 return 0/*failure*/;
964
965 if (!arg || !*arg)
966 return 1/*success*/;
967
968 used = strlen(info->args);
969 len = strlen(arg);
970 off = len + (val && *val ? 1/*=*/ + strlen(val) : 0) + (used? 1/*&*/ : 0);
971
972 if (off + used >= sizeof(info->args))
973 return 0/*failure*/;
974
975 if (used)
976 memmove(info->args + off, info->args, used + 1);
977 strcpy(info->args, arg);
978 if (val && *val) {
979 info->args[len++] = '=';
980 strcpy(info->args + len, val);
981 }
982 if (used)
983 info->args[off - 1] = '&';
984 return 1/*success*/;
985 }
986
987
ConnNetInfo_DeleteArg(SConnNetInfo * info,const char * arg)988 extern int/*bool*/ ConnNetInfo_DeleteArg(SConnNetInfo* info,
989 const char* arg)
990 {
991 int/*bool*/ deleted;
992 size_t argnamelen;
993 size_t arglen;
994 char* a;
995
996 if (!s_InfoIsValid(info) || !arg || !(argnamelen = strcspn(arg, "=&")))
997 return 0/*false*/;
998
999 deleted = 0/*false*/;
1000 for (a = info->args; *a; a += arglen) {
1001 if (*a == '&')
1002 a++;
1003 arglen = strcspn(a, "&");
1004 if (arglen < argnamelen || strncasecmp(a, arg, argnamelen) != 0 ||
1005 (a[argnamelen] && a[argnamelen] != '=' && a[argnamelen] != '&')) {
1006 continue;
1007 }
1008 if (!a[arglen]) {
1009 if (a == info->args)
1010 *a = '\0'; /* the only argument removed */
1011 else
1012 *--a = '\0'; /* last argument: also remove trailing '&' */
1013 return 1/*true*/;
1014 }
1015 arglen++; /* for intermediary args, eat the following '&' separator */
1016 memmove(a, a + arglen, strlen(a + arglen) + 1);
1017 deleted = 1/*true*/;
1018 arglen = 0;
1019 }
1020 return deleted;
1021 }
1022
1023
ConnNetInfo_DeleteAllArgs(SConnNetInfo * info,const char * args)1024 extern void ConnNetInfo_DeleteAllArgs(SConnNetInfo* info,
1025 const char* args)
1026 {
1027 if (!s_InfoIsValid(info) || !args)
1028 return;
1029
1030 while (*args) {
1031 const char* a = strchr(args, '&');
1032 if (!a)
1033 a = args + strlen(args);
1034 else
1035 a++;
1036 ConnNetInfo_DeleteArg(info, args);
1037 args = a;
1038 }
1039 }
1040
1041
ConnNetInfo_PreOverrideArg(SConnNetInfo * info,const char * arg,const char * val)1042 extern int/*bool*/ ConnNetInfo_PreOverrideArg(SConnNetInfo* info,
1043 const char* arg,
1044 const char* val)
1045 {
1046 if (!s_InfoIsValid(info))
1047 return 0/*failure*/;
1048
1049 if (!arg || !*arg)
1050 return 1/*success*/;
1051
1052 ConnNetInfo_DeleteAllArgs(info, arg);
1053 return ConnNetInfo_PrependArg(info, arg, val);
1054 }
1055
1056
ConnNetInfo_PostOverrideArg(SConnNetInfo * info,const char * arg,const char * val)1057 extern int/*bool*/ ConnNetInfo_PostOverrideArg(SConnNetInfo* info,
1058 const char* arg,
1059 const char* val)
1060 {
1061 if (!s_InfoIsValid(info))
1062 return 0/*failure*/;
1063
1064 if (!arg || !*arg)
1065 return 1/*success*/;
1066
1067 ConnNetInfo_DeleteAllArgs(info, arg);
1068 return ConnNetInfo_AppendArg(info, arg, val);
1069 }
1070
1071
x_IsSufficientAddress(const char * addr)1072 static int/*bool*/ x_IsSufficientAddress(const char* addr)
1073 {
1074 const char* c;
1075 return !strchr(addr, ' ')
1076 && (SOCK_isip(addr)
1077 || ((c = strchr(addr, '.')) != 0 && c[1] &&
1078 (c = strchr(c + 2, '.')) != 0 && c[1]));
1079 }
1080
1081
x_ClientAddress(const char * client_host,int local_host)1082 static const char* x_ClientAddress(const char* client_host,
1083 int/*bool*/ local_host)
1084 {
1085 const char* c = client_host;
1086 unsigned int ip;
1087 char addr[80];
1088 char* s;
1089
1090 assert(client_host);
1091 strncpy0(addr, client_host, sizeof(addr) - 1);
1092 if (UTIL_NcbiLocalHostName(addr) && (s = strdup(addr)) != 0)
1093 client_host = s; /*NB: this usually makes client_host insufficient*/
1094
1095 if ((client_host == c && x_IsSufficientAddress(client_host))
1096 || !(ip = *c && !local_host
1097 ? SOCK_gethostbyname(c)
1098 : SOCK_GetLocalHostAddress(eDefault))
1099 || SOCK_ntoa(ip, addr, sizeof(addr)) != 0
1100 || !(s = (char*) malloc(strlen(client_host) + strlen(addr) + 3))) {
1101 return client_host/*least we can do :-/*/;
1102 }
1103
1104 sprintf(s, "%s(%s)", client_host, addr);
1105 if (client_host != c)
1106 free((void*) client_host);
1107 for (client_host = s; *s; ++s) {
1108 if (*s == ' ')
1109 *s = '+';
1110 }
1111 return client_host;
1112 }
1113
1114
ConnNetInfo_SetupStandardArgs(SConnNetInfo * info,const char * service)1115 extern int/*bool*/ ConnNetInfo_SetupStandardArgs(SConnNetInfo* info,
1116 const char* service)
1117 {
1118 static const char kService[] = "service";
1119 static const char kAddress[] = "address";
1120 static const char kPlatform[] = "platform";
1121 int/*bool*/ local_host;
1122 const char* s;
1123
1124 if (!info || !s_InfoIsValid(info))
1125 return 0/*failed*/;
1126
1127 s = CORE_GetAppName();
1128 if (s && *s) {
1129 char ua[16 + 80];
1130 sprintf(ua, "User-Agent: %.80s", s);
1131 ConnNetInfo_ExtendUserHeader(info, ua);
1132 }
1133
1134 /* Dispatcher CGI args (may sacrifice some if they don't fit altogether) */
1135 if (!(s = CORE_GetPlatform()) || !*s)
1136 ConnNetInfo_DeleteArg(info, kPlatform);
1137 else
1138 ConnNetInfo_PreOverrideArg(info, kPlatform, s);
1139 local_host = !info->client_host[0];
1140 if (local_host &&
1141 !SOCK_gethostbyaddr(0, info->client_host, sizeof(info->client_host))) {
1142 SOCK_gethostname(info->client_host, sizeof(info->client_host));
1143 }
1144 if (!(s = x_ClientAddress(info->client_host, local_host)) || !*s)
1145 ConnNetInfo_DeleteArg(info, kAddress);
1146 else
1147 ConnNetInfo_PreOverrideArg(info, kAddress, s);
1148 if (s != info->client_host)
1149 free((void*) s);
1150 if (service) {
1151 if (!ConnNetInfo_PreOverrideArg(info, kService, service)) {
1152 ConnNetInfo_DeleteArg(info, kPlatform);
1153 if (!ConnNetInfo_PreOverrideArg(info, kService, service)) {
1154 ConnNetInfo_DeleteArg(info, kAddress);
1155 if (!ConnNetInfo_PreOverrideArg(info, kService, service))
1156 return 0/*failed*/;
1157 }
1158 }
1159 }
1160 return 1/*succeeded*/;
1161 }
1162
1163
ConnNetInfo_Clone(const SConnNetInfo * info)1164 extern SConnNetInfo* ConnNetInfo_Clone(const SConnNetInfo* info)
1165 {
1166 SConnNetInfo* x_info;
1167
1168 if (!info || !s_InfoIsValid(info))
1169 return 0;
1170
1171 if (!(x_info = (SConnNetInfo*) malloc(sizeof(*info) + strlen(info->svc))))
1172 return 0;
1173
1174 strcpy(x_info->client_host, info->client_host);
1175 x_info->scheme = info->scheme;
1176 x_info->req_method = info->req_method;
1177 x_info->version = info->version;
1178 x_info->external = info->external;
1179 x_info->firewall = info->firewall;
1180 x_info->stateless = info->stateless;
1181 x_info->lb_disable = info->lb_disable;
1182 x_info->debug_printout = info->debug_printout;
1183 x_info->http_push_auth = info->http_push_auth;
1184 x_info->http_proxy_leak = info->http_proxy_leak;
1185 x_info->reserved = info->reserved;
1186 strcpy(x_info->user, info->user);
1187 strcpy(x_info->pass, info->pass);
1188 strcpy(x_info->host, info->host);
1189 x_info->port = info->port;
1190 strcpy(x_info->path, info->path);
1191 strcpy(x_info->args, info->args);
1192 strcpy(x_info->http_proxy_host, info->http_proxy_host);
1193 x_info->http_proxy_port = info->http_proxy_port;
1194 strcpy(x_info->http_proxy_user, info->http_proxy_user);
1195 strcpy(x_info->http_proxy_pass, info->http_proxy_pass);
1196 x_info->max_try = info->max_try;
1197 x_info->http_user_header = 0;
1198 x_info->http_referer = 0;
1199 x_info->credentials = info->credentials;
1200
1201 if (info->http_user_header && *info->http_user_header
1202 && !(x_info->http_user_header = strdup(info->http_user_header))) {
1203 ConnNetInfo_Destroy(x_info);
1204 return 0;
1205 }
1206 if (info->http_referer && *info->http_referer
1207 && !(x_info->http_referer = strdup(info->http_referer))) {
1208 ConnNetInfo_Destroy(x_info);
1209 return 0;
1210 }
1211
1212 x_info->tmo = info->timeout ? *info->timeout : info->tmo;
1213 x_info->timeout = info->timeout ? &x_info->tmo : 0;
1214 strcpy((char*) x_info->svc, info->svc);
1215
1216 x_info->magic = CONN_NET_INFO_MAGIC;
1217 return x_info;
1218 }
1219
1220
x_BadMagic(unsigned int magic,char buf[])1221 static const char* x_BadMagic(unsigned int magic, char buf[])
1222 {
1223 sprintf(buf, "0x%08lX (INVALID != 0x%08lX)",
1224 (unsigned long) magic, (unsigned long) CONN_NET_INFO_MAGIC);
1225 return buf;
1226 }
1227
1228
x_Port(unsigned short port,char buf[])1229 static const char* x_Port(unsigned short port, char buf[])
1230 {
1231 assert(port);
1232 sprintf(buf, "%hu", port);
1233 return buf;
1234 }
1235
1236
x_ReqMethod(TReqMethod req_method,char buf[])1237 static const char* x_ReqMethod(TReqMethod req_method, char buf[])
1238 {
1239 int/*bool*/ v1 = req_method & eReqMethod_v1 ? 1/*true*/ : 0/*false*/;
1240 req_method &= ~eReqMethod_v1;
1241 switch (req_method) {
1242 case eReqMethod_Any:
1243 return v1 ? "ANY/1.1" : "ANY";
1244 case eReqMethod_Get:
1245 return v1 ? "GET/1.1" : "GET";
1246 case eReqMethod_Post:
1247 return v1 ? "POST/1.1" : "POST";
1248 case eReqMethod_Head:
1249 return v1 ? "HEAD/1.1" : "HEAD";
1250 case eReqMethod_Connect:
1251 return v1 ? "CONNECT/1.1" : "CONNECT";
1252 case eReqMethod_Put:
1253 return "PUT";
1254 case eReqMethod_Patch:
1255 return "PATCH";
1256 case eReqMethod_Trace:
1257 return "TRACE";
1258 case eReqMethod_Delete:
1259 return "DELETE";
1260 case eReqMethod_Options:
1261 return "OPTIONS";
1262 default:
1263 break;
1264 }
1265 return buf ? x_Num(req_method, buf) : 0;
1266 }
1267
1268
x_Firewall(unsigned int firewall)1269 static const char* x_Firewall(unsigned int firewall)
1270 {
1271 switch ((EFWMode) firewall) {
1272 case eFWMode_Adaptive:
1273 return "TRUE";
1274 case eFWMode_Firewall:
1275 return "FIREWALL";
1276 case eFWMode_Fallback:
1277 return "FALLBACK";
1278 default:
1279 assert(!firewall);
1280 break;
1281 }
1282 return "NONE";
1283 }
1284
1285
x_CredInfo(NCBI_CRED cred,char buf[])1286 static const char* x_CredInfo(NCBI_CRED cred, char buf[])
1287 {
1288 int who, what;
1289 if (!cred)
1290 return "NULL";
1291 who = (cred->type / 100) * 100;
1292 what = cred->type % 100;
1293 switch (who) {
1294 case eNcbiCred_GnuTls:
1295 switch (what) {
1296 case 0:
1297 return "(GNUTLS X.509 Cert)";
1298 default:
1299 sprintf(buf, "(GNUTLS #%u)", what);
1300 return buf;
1301 }
1302 default:
1303 break;
1304 }
1305 return x_Num(cred->type, buf);
1306 }
1307
1308
s_SaveStringQuot(char * s,const char * name,const char * str,int quote)1309 static void s_SaveStringQuot(char* s, const char* name,
1310 const char* str, int/*bool*/ quote)
1311 {
1312 sprintf(s + strlen(s), "%-16.16s: %s%s%s\n", name,
1313 str && quote ? "\"" : "",
1314 str ? str : "NULL",
1315 str && quote ? "\"" : "");
1316 }
1317
s_SaveString(char * s,const char * name,const char * str)1318 static void s_SaveString(char* s, const char* name, const char* str)
1319 {
1320 s_SaveStringQuot(s, name, str, 1);
1321 }
1322
s_SaveKeyval(char * s,const char * name,const char * str)1323 static void s_SaveKeyval(char* s, const char* name, const char* str)
1324 {
1325 assert(str);
1326 s_SaveStringQuot(s, name, str, 0);
1327 }
1328
s_SaveBool(char * s,const char * name,unsigned int bbb)1329 static void s_SaveBool(char* s, const char* name, unsigned int/*bool*/ bbb)
1330 {
1331 s_SaveKeyval(s, name, bbb ? "TRUE" : "FALSE");
1332 }
1333
s_SaveULong(char * s,const char * name,unsigned long lll)1334 static void s_SaveULong(char* s, const char* name, unsigned long lll)
1335 {
1336 sprintf(s + strlen(s), "%-16.16s: %lu\n", name, lll);
1337 }
1338
s_SaveUserHeader(char * s,const char * name,const char * uh,size_t uhlen)1339 static void s_SaveUserHeader(char* s, const char* name,
1340 const char* uh, size_t uhlen)
1341 {
1342 s += strlen(s);
1343 s += sprintf(s, "%-16.16s: ", name);
1344 if (uh) {
1345 *s++ = '"';
1346 memcpy(UTIL_PrintableString(uh, uhlen, s, 0/*reduce*/), "\"\n", 3);
1347 } else
1348 memcpy(s, "NULL\n", 6);
1349 }
1350
1351
ConnNetInfo_Log(const SConnNetInfo * info,ELOG_Level sev,LOG lg)1352 extern void ConnNetInfo_Log(const SConnNetInfo* info, ELOG_Level sev, LOG lg)
1353 {
1354 char buf[80];
1355 size_t uhlen;
1356 size_t len;
1357 char* s;
1358
1359 if (!info) {
1360 LOG_Write(lg, NCBI_C_ERRCODE_X, 10, sev, 0, 0, 0, 0,
1361 "ConnNetInfo_Log: NULL", 0, 0);
1362 return;
1363 }
1364
1365 uhlen = info->http_user_header ? strlen(info->http_user_header) : 0;
1366
1367 len = sizeof(*info) + 1024/*slack for all labels & keywords*/
1368 + UTIL_PrintableStringSize(info->http_user_header, uhlen)
1369 + (info->http_referer ? strlen(info->http_referer) : 0)
1370 + strlen(info->svc);
1371
1372 if (!(s = (char*) malloc(len))) {
1373 LOG_WRITE(lg, NCBI_C_ERRCODE_X, 11,
1374 sev == eLOG_Fatal ? eLOG_Fatal : eLOG_Error,
1375 "ConnNetInfo_Log: Cannot allocate temporary buffer");
1376 return;
1377 }
1378
1379 strcpy(s, "ConnNetInfo_Log\n"
1380 "#################### [BEGIN] SConnNetInfo:\n");
1381 if (!s_InfoIsValid(info))
1382 s_SaveKeyval(s, "magic", x_BadMagic(info->magic, buf));
1383 if (*info->svc)
1384 s_SaveString(s, "service", info->svc);
1385 else
1386 s_SaveKeyval(s, "service", "NONE");
1387 if (*info->client_host)
1388 s_SaveString(s, "client_host", info->client_host);
1389 else
1390 s_SaveKeyval(s, "client_host", "(default)");
1391 s_SaveKeyval (s, "scheme", (info->scheme
1392 ? x_Scheme((EURLScheme)info->scheme,
1393 buf)
1394 : "(unspec)"));
1395 s_SaveKeyval (s, "req_method", x_ReqMethod(info->req_method
1396 | (info->version
1397 ? eReqMethod_v1
1398 : 0), buf));
1399 #if defined(_DEBUG) && !defined(NDEBUG)
1400 s_SaveString (s, "user", info->user);
1401 #else
1402 s_SaveKeyval (s, "user", *info->user ? "(set)" : "\"\"");
1403 #endif /*_DEBUG && !NDEBUG*/
1404 if (*info->pass)
1405 s_SaveKeyval(s, "pass", *info->user ? "(set)" : "(ignored)");
1406 else
1407 s_SaveString(s, "pass", info->pass);
1408 s_SaveString (s, "host", info->host);
1409 s_SaveKeyval (s, "port", (info->port
1410 ? x_Port(info->port, buf)
1411 : *info->host
1412 ? "(default)"
1413 : "(none"));
1414 s_SaveString (s, "path", info->path);
1415 s_SaveString (s, "args", info->args);
1416 s_SaveString (s, "http_proxy_host", info->http_proxy_host);
1417 s_SaveKeyval (s, "http_proxy_port",(info->http_proxy_port
1418 ? x_Port(info->http_proxy_port, buf)
1419 : "(none)"));
1420 #if defined(_DEBUG) && !defined(NDEBUG)
1421 s_SaveString (s, "http_proxy_user", info->http_proxy_user);
1422 #else
1423 s_SaveKeyval (s, "http_proxy_user",(info->http_proxy_user[0]
1424 ? "(set)" : "\"\""));
1425 #endif /*_DEBUG && !NDEBUG*/
1426 if (*info->http_proxy_pass) {
1427 s_SaveKeyval(s, "http_proxy_pass",(info->http_proxy_pass[0]
1428 ? "(set)" : "(ignored)"));
1429 } else
1430 s_SaveString(s, "http_proxy_pass", info->http_proxy_pass);
1431 s_SaveBool (s, "http_proxy_leak", info->http_proxy_leak);
1432 s_SaveULong (s, "max_try", info->max_try);
1433 if (info->timeout) {
1434 s_SaveULong (s, "timeout(sec)", info->timeout->sec);
1435 s_SaveULong (s, "timeout(usec)", info->timeout->usec);
1436 } else
1437 s_SaveKeyval(s, "timeout", "INFINITE");
1438 s_SaveBool (s, "external", info->external);
1439 s_SaveKeyval (s, "firewall", x_Firewall(info->firewall));
1440 s_SaveBool (s, "stateless", info->stateless);
1441 s_SaveBool (s, "lb_disable", info->lb_disable);
1442 s_SaveKeyval (s, "debug_printout", (info->debug_printout
1443 == eDebugPrintout_None
1444 ? "NONE"
1445 : info->debug_printout
1446 == eDebugPrintout_Some
1447 ? "SOME"
1448 : info->debug_printout
1449 == eDebugPrintout_Data
1450 ? "DATA"
1451 : x_Num(info->debug_printout,buf)));
1452 s_SaveBool (s, "http_push_auth", info->http_push_auth);
1453 s_SaveUserHeader(s, "http_user_header",info->http_user_header, uhlen);
1454 s_SaveString (s, "http_referer", info->http_referer);
1455 if (info->credentials)
1456 s_SaveKeyval(s, "credentials", x_CredInfo(info->credentials, buf));
1457 strcat(s, "#################### [END] SConnNetInfo\n");
1458
1459 assert(strlen(s) < len);
1460 LOG_Write(lg, NCBI_C_ERRCODE_X, 12, sev, 0, 0, 0, 0, s, 0, 0);
1461 free(s);
1462 }
1463
1464
ConnNetInfo_URL(const SConnNetInfo * info)1465 extern char* ConnNetInfo_URL(const SConnNetInfo* info)
1466 {
1467 TReqMethod req_method;
1468 const char* scheme;
1469 size_t schlen;
1470 const char* path;
1471 const char* args;
1472 size_t len;
1473 char* url;
1474 char buf[40];
1475
1476 if (!info || !s_InfoIsValid(info))
1477 return 0/*failed*/;
1478
1479 req_method = info->req_method & ~eReqMethod_v1;
1480 scheme = x_Scheme((EURLScheme) info->scheme, buf);
1481 assert(!scheme || *scheme);
1482 if ((!scheme && req_method != eReqMethod_Connect) ||
1483 ( scheme && !isalpha((unsigned char)(*scheme)))) {
1484 return 0/*failure*/;
1485 }
1486
1487 if (req_method == eReqMethod_Connect) {
1488 scheme = "";
1489 schlen = 0;
1490 path = 0;
1491 args = "";
1492 len = 0;
1493 } else {
1494 assert(scheme);
1495 schlen = strlen(scheme);
1496 path = info->path;
1497 args = info->args;
1498 len = schlen+3/*://*/ + strlen(path) + (*args ? strlen(args) + 2 : 1);
1499 }
1500 len += strlen(info->host) + 7/*:port\0*/;
1501
1502 url = (char*) malloc(len);
1503 if (url) {
1504 assert(scheme && args);
1505 strlwr((char*) memcpy(url, scheme, schlen + 1));
1506 len = schlen;
1507 len += sprintf(url + len, &"://%s"[schlen ? 0 : 3], info->host);
1508 if (info->port || !path/*req_method == eReqMethod_Connect*/)
1509 len += sprintf(url + len, ":%hu", info->port);
1510 sprintf(url + len, "%s%s%s%s",
1511 &"/"[! path || *path == '/'], path ? path : "",
1512 &"?"[!*args || *args == '#'], args);
1513 }
1514 assert(!url || *url);
1515 return url;
1516 }
1517
1518
ConnNetInfo_SetTimeout(SConnNetInfo * info,const STimeout * timeout)1519 extern int/*bool*/ ConnNetInfo_SetTimeout(SConnNetInfo* info,
1520 const STimeout* timeout)
1521 {
1522 if (!s_InfoIsValid(info) || timeout == kDefaultTimeout)
1523 return 0/*failed*/;
1524 if (timeout) {
1525 info->tmo = *timeout;
1526 info->timeout = &info->tmo;
1527 } else
1528 info->timeout = kInfiniteTimeout/*0,timeout*/;
1529 return 1/*succeeded*/;
1530 }
1531
1532
ConnNetInfo_Destroy(SConnNetInfo * info)1533 extern void ConnNetInfo_Destroy(SConnNetInfo* info)
1534 {
1535 if (info) {
1536 ConnNetInfo_SetUserHeader(info, 0);
1537 if (info->http_referer) {
1538 free((void*) info->http_referer);
1539 info->http_referer = 0;
1540 }
1541 free(info);
1542 }
1543 }
1544
1545
1546
1547 /****************************************************************************
1548 * URL_Connect
1549 */
1550
1551
x_URLConnectErrorReturn(SOCK sock,EIO_Status status)1552 static EIO_Status x_URLConnectErrorReturn(SOCK sock, EIO_Status status)
1553 {
1554 if (sock) {
1555 SOCK_Abort(sock);
1556 SOCK_Close(sock);
1557 }
1558 return status;
1559 }
1560
1561
URL_ConnectEx(const char * host,unsigned short port,const char * path,const char * args,TReqMethod req_method,size_t content_length,const STimeout * o_timeout,const STimeout * rw_timeout,const char * user_hdr,NCBI_CRED cred,TSOCK_Flags flags,SOCK * sock)1562 extern EIO_Status URL_ConnectEx
1563 (const char* host,
1564 unsigned short port,
1565 const char* path,
1566 const char* args,
1567 TReqMethod req_method,
1568 size_t content_length,
1569 const STimeout* o_timeout,
1570 const STimeout* rw_timeout,
1571 const char* user_hdr,
1572 NCBI_CRED cred,
1573 TSOCK_Flags flags,
1574 SOCK* sock)
1575 {
1576 static const char kHttp[][12] = { " HTTP/1.0\r\n",
1577 " HTTP/1.1\r\n" };
1578 SOCK s;
1579 BUF buf;
1580 char* hdr;
1581 const char* str;
1582 SSOCK_Init init;
1583 const char* http;
1584 EIO_Status status;
1585 size_t hdr_len;
1586 size_t args_len;
1587 char temp[80];
1588 EReqMethod x_req_meth;
1589 size_t user_hdr_len = user_hdr && *user_hdr ? strlen(user_hdr) : 0;
1590
1591 /* sanity check first */
1592 if (!sock || !host || !*host || !path || !*path
1593 || (user_hdr && *user_hdr && user_hdr[user_hdr_len - 1] != '\n')){
1594 CORE_LOG_X(2, eLOG_Critical, "[URL_Connect] Bad argument(s)");
1595 if (sock) {
1596 s = *sock;
1597 *sock = 0;
1598 } else
1599 s = 0;
1600 return x_URLConnectErrorReturn(s, eIO_InvalidArg);
1601 }
1602 s = *sock;
1603 *sock = 0;
1604
1605 /*FIXME: the check to be removed*/
1606 if (cred && cred < (NCBI_CRED) 4096) {
1607 CORE_LOGF(eLOG_Critical,
1608 ("[URL_ConnectEx; http%s://%s:%hu%s%s] "
1609 " Obsolete feature 'encode_args' is no longer supported",
1610 &"s"[!(flags & fSOCK_Secure)],
1611 host, port, &"/"[*path == '/'], path));
1612 return x_URLConnectErrorReturn(s, eIO_InvalidArg);
1613 }
1614
1615 /* trim user_hdr */
1616 while (user_hdr_len) {
1617 if (!isspace((unsigned char) user_hdr[0]))
1618 break;
1619 ++user_hdr;
1620 --user_hdr_len;
1621 }
1622 while (user_hdr_len) {
1623 if (!isspace((unsigned char) user_hdr[user_hdr_len - 1]))
1624 break;
1625 --user_hdr_len;
1626 }
1627
1628 http = kHttp[req_method < eReqMethod_v1 ? 0 : 1];
1629 x_req_meth = (EReqMethod)(req_method & ~eReqMethod_v1);
1630 /* select request method and its verbal representation */
1631 if (x_req_meth == eReqMethod_Any)
1632 x_req_meth = content_length ? eReqMethod_Post : eReqMethod_Get;
1633 else if (content_length && (x_req_meth == eReqMethod_Head ||
1634 x_req_meth == eReqMethod_Get)) {
1635 CORE_LOGF_X(3, eLOG_Warning,
1636 ("[URL_Connect; http%s://%s:%hu%s%s] "
1637 " Content-Length (%lu) is ignored with request method %s",
1638 &"s"[!(flags & fSOCK_Secure)],
1639 host, port, &"/"[*path == '/'], path,
1640 (unsigned long) content_length,
1641 x_req_meth == eReqMethod_Get ? "GET" : "HEAD"));
1642 content_length = 0;
1643 }
1644
1645 if (!(str = x_ReqMethod(x_req_meth, 0))) {
1646 CORE_LOGF_X(4, eLOG_Error,
1647 ("[URL_Connect; http%s://%s:%hu%s%s] "
1648 " Unsupported request method %s",
1649 &"s"[!(flags & fSOCK_Secure)],
1650 host, port, &"/"[*path == '/'], path,
1651 x_ReqMethod(req_method, temp)));
1652 assert(0);
1653 return x_URLConnectErrorReturn(s, eIO_NotSupported);
1654 }
1655
1656 if (x_req_meth != eReqMethod_Connect) {
1657 if (!port)
1658 port = flags & fSOCK_Secure ? CONN_PORT_HTTPS : CONN_PORT_HTTP;
1659 args_len = args ? strcspn(args, "#") : 0;
1660 } else
1661 args_len = 0;
1662
1663 buf = 0;
1664 errno = 0;
1665 /* compose HTTP header */
1666 if (/* METHOD <path>[?<args>] HTTP/1.x\r\n */
1667 !BUF_Write(&buf, str, strlen(str)) ||
1668 !BUF_Write(&buf, " ", 1) ||
1669 !BUF_Write(&buf, path, strlen(path)) ||
1670 (args_len
1671 && (!BUF_Write(&buf, "?", 1) ||
1672 !BUF_Write(&buf, args, args_len))) ||
1673 !BUF_Write (&buf, http, sizeof(kHttp[0]) - 1) ||
1674
1675 /* Content-Length: <content_length>\r\n */
1676 (x_req_meth != eReqMethod_Connect && content_length
1677 && !BUF_Write(&buf, temp, (size_t)
1678 sprintf(temp, "Content-Length: %lu\r\n",
1679 (unsigned long) content_length))) ||
1680
1681 /* <user_header> */
1682 (user_hdr_len
1683 && !BUF_Write(&buf, user_hdr, user_hdr_len)) ||
1684
1685 /* header separator */
1686 !BUF_Write(&buf, "\r\n\r\n", user_hdr_len ? 4 : 2) ||
1687
1688 /* tunneled data */
1689 (x_req_meth == eReqMethod_Connect && content_length
1690 && !BUF_Write(&buf, args, content_length))) {
1691 int x_errno = errno;
1692 CORE_LOGF_ERRNO_X(5, eLOG_Error, x_errno,
1693 ("[URL_Connect; http%s://%s:%hu%s%s%s%.*s] "
1694 " Cannot build HTTP header",
1695 &"s"[!(flags & fSOCK_Secure)],
1696 host, port, &"/"[*path == '/'], path,
1697 &"?"[!args_len], (int) args_len, args));
1698 BUF_Destroy(buf);
1699 return x_URLConnectErrorReturn(s, eIO_Unknown);
1700 }
1701
1702 if (!(hdr = (char*) malloc(hdr_len = BUF_Size(buf)))
1703 || BUF_Read(buf, hdr, hdr_len) != hdr_len) {
1704 int x_errno = errno;
1705 CORE_LOGF_ERRNO_X(6, eLOG_Error, x_errno,
1706 ("[URL_Connect; http%s://%s:%hu%s%s%s%.*s] "
1707 " Cannot maintain HTTP header (%lu byte%s)",
1708 &"s"[!(flags & fSOCK_Secure)],
1709 host, port, &"/"[*path == '/'], path,
1710 &"?"[!args_len], (int) args_len, args,
1711 (unsigned long) hdr_len, &"s"[hdr_len == 1]));
1712 if (hdr)
1713 free(hdr);
1714 BUF_Destroy(buf);
1715 return x_URLConnectErrorReturn(s, eIO_Unknown);
1716 }
1717 BUF_Destroy(buf);
1718
1719 memset(&init, 0, sizeof(init));
1720 init.data = hdr;
1721 init.size = hdr_len;
1722 init.cred = cred;
1723
1724 if (s) {
1725 /* re-use existing connection */
1726 status = SOCK_CreateOnTopInternal(s/*old*/, 0, sock/*new*/,
1727 &init, flags);
1728 SOCK_Destroy(s);
1729 } else {
1730 /* connect to HTTPD */
1731 status = SOCK_CreateInternal(host, port, o_timeout, sock/*new*/,
1732 &init, flags);
1733 }
1734 free(hdr);
1735
1736 if (status != eIO_Success) {
1737 assert(!*sock);
1738 if (status == eIO_Timeout && o_timeout) {
1739 sprintf(temp, "[%u.%06u]",
1740 (unsigned int)(o_timeout->sec + o_timeout->usec/1000000),
1741 (unsigned int) (o_timeout->usec%1000000));
1742 } else
1743 *temp = '\0';
1744 CORE_LOGF_X(7, eLOG_Error,
1745 ("[URL_Connect; http%s://%s:%hu%s%s%s%.*s] "
1746 " Failed to %s: %s%s",
1747 &"s"[!(flags & fSOCK_Secure)],
1748 host, port, &"/"[*path == '/'], path,
1749 &"?"[!args_len], (int) args_len, args,
1750 s ? "use connection" : "connect",
1751 IO_StatusStr(status), temp));
1752 } else
1753 verify(SOCK_SetTimeout(*sock, eIO_ReadWrite, rw_timeout)==eIO_Success);
1754 return status;
1755 }
1756
1757
URL_Connect(const char * host,unsigned short port,const char * path,const char * args,EReqMethod req_method,size_t content_length,const STimeout * o_timeout,const STimeout * rw_timeout,const char * user_hdr,int encode_args,TSOCK_Flags flags)1758 extern SOCK URL_Connect
1759 (const char* host,
1760 unsigned short port,
1761 const char* path,
1762 const char* args,
1763 EReqMethod req_method,
1764 size_t content_length,
1765 const STimeout* o_timeout,
1766 const STimeout* rw_timeout,
1767 const char* user_hdr,
1768 int/*bool*/ encode_args,
1769 TSOCK_Flags flags)
1770 {
1771 static const char kHost[] = "Host: ";
1772 const char* x_hdr = user_hdr;
1773 char* x_args = 0;
1774 SOCK sock;
1775
1776 if (req_method >= eReqMethod_v1) {
1777 CORE_LOG_X(9, eLOG_Error,
1778 "[URL_Connect] Unsupported version of HTTP protocol");
1779 return 0;
1780 }
1781
1782 if (req_method != eReqMethod_Connect) {
1783 size_t x_add = 1/*true*/;
1784 while (x_hdr && *x_hdr) {
1785 if (x_hdr != user_hdr)
1786 x_hdr++;
1787 if (strncasecmp(x_hdr, kHost, sizeof(kHost) - 2) == 0) {
1788 x_add = 0/*false*/;
1789 break;
1790 }
1791 x_hdr = strchr(x_hdr, '\n');
1792 }
1793 if (x_add) {
1794 size_t x_len = host && *host ? strlen(host) : 0;
1795 char* x_host = x_len ? (char*) malloc(x_len + sizeof(kHost)+6) : 0;
1796 if (x_host) {
1797 memcpy(x_host, kHost, sizeof(kHost) - 1);
1798 memcpy(x_host + sizeof(kHost) - 1, host, x_len);
1799 x_len += sizeof(kHost) - 1;
1800 if (port)
1801 sprintf(x_host + x_len, ":%hu", port);
1802 else
1803 x_host[x_len] = '\0';
1804 if (!(x_hdr = x_StrcatCRLF(x_host, user_hdr))) {
1805 x_hdr = user_hdr;
1806 free(x_host);
1807 }
1808 }
1809 } else
1810 x_hdr = user_hdr;
1811
1812 if (args && encode_args && (x_add = strcspn(args, "#")) > 0) {
1813 /* URL-encode "args", if any specified */
1814 size_t size = 3 * x_add;
1815 size_t rd_len, wr_len;
1816 if (!(x_args = (char*) malloc(size + 1))) {
1817 CORE_LOGF_ERRNO_X(8, eLOG_Error, errno,
1818 ("[URL_Connect] Out of memory (%lu)",
1819 (unsigned long)(size + 1)));
1820 if (x_hdr != user_hdr)
1821 free((void*) x_hdr);
1822 return 0;
1823 }
1824 URL_Encode(args, x_add, &rd_len, x_args, size, &wr_len);
1825 assert(rd_len == x_add);
1826 assert(wr_len <= size);
1827 x_args[wr_len] = '\0';
1828 args = x_args;
1829 }
1830 }
1831
1832 sock = 0;
1833 verify(URL_ConnectEx(host, port, path, args,
1834 req_method, content_length,
1835 o_timeout, rw_timeout,
1836 x_hdr, 0/*cred*/, flags, &sock) == eIO_Success
1837 || !sock);
1838
1839 if (x_args)
1840 free(x_args);
1841 if (x_hdr != user_hdr)
1842 free((void*) x_hdr);
1843 return sock;
1844 }
1845
1846
1847
1848 /****************************************************************************
1849 * StripToPattern()
1850 */
1851
1852
1853 typedef EIO_Status (*FDoIO)
1854 (void* stream,
1855 void* buf,
1856 size_t size,
1857 size_t* n_read,
1858 EIO_Event what /* eIO_Read | eIO_Write (to pushback) */
1859 );
1860
s_StripToPattern(void * stream,FDoIO io_func,const void * pattern,size_t pattern_size,BUF * discard,size_t * n_discarded)1861 static EIO_Status s_StripToPattern
1862 (void* stream,
1863 FDoIO io_func,
1864 const void* pattern,
1865 size_t pattern_size,
1866 BUF* discard,
1867 size_t* n_discarded)
1868 {
1869 char* buf;
1870 EIO_Status status;
1871 size_t n_read;
1872 size_t buf_size;
1873 char x_buf[4096];
1874
1875 /* check args */
1876 if ( n_discarded )
1877 *n_discarded = 0;
1878 if ( !stream )
1879 return eIO_InvalidArg;
1880 if ( !pattern_size )
1881 pattern = 0;
1882
1883 /* allocate a temporary read buffer */
1884 buf_size = pattern ? pattern_size << 1 : pattern_size;
1885 if (buf_size <= sizeof(x_buf) && (pattern || !pattern_size)) {
1886 buf_size = sizeof(x_buf);
1887 buf = x_buf;
1888 } else if ( !(buf = (char*) malloc(buf_size)) )
1889 return eIO_Unknown;
1890
1891 if ( !pattern ) {
1892 /* read/discard the specified # of bytes or until EOF */
1893 size_t n_count = 0;
1894 char* xx_buf = buf;
1895 do {
1896 status = io_func(stream, xx_buf, buf_size, &n_read, eIO_Read);
1897 if (!n_read) {
1898 assert(status != eIO_Success);
1899 break;
1900 }
1901 if (discard) {
1902 if (!n_count && pattern_size) {
1903 assert(buf_size == pattern_size);
1904 /* first time enqueue the entire "buf" */
1905 if (!BUF_AppendEx(discard, buf, buf_size, buf, n_read))
1906 discard = 0;
1907 else
1908 buf = 0;
1909 } else if (!BUF_Write(discard, xx_buf, n_read))
1910 discard = 0;
1911 }
1912 n_count += n_read;
1913 if (pattern_size) {
1914 if (!(buf_size -= n_read))
1915 break;
1916 xx_buf += n_read;
1917 }
1918 } while (status == eIO_Success);
1919 if ( n_discarded )
1920 *n_discarded = n_count;
1921 } else {
1922 assert(pattern_size);
1923 n_read = 0;
1924 for (;;) {
1925 /* read; search for the pattern; store/count the discarded data */
1926 size_t x_read, n_stored;
1927
1928 assert(n_read < pattern_size);
1929 status = io_func(stream, buf + n_read, buf_size - n_read,
1930 &x_read, eIO_Read);
1931 if ( !x_read ) {
1932 assert(status != eIO_Success);
1933 break;
1934 }
1935 n_stored = n_read + x_read;
1936
1937 if (n_stored >= pattern_size) {
1938 /* search for the pattern */
1939 size_t n_check = n_stored - pattern_size + 1;
1940 const char* b;
1941 for (b = buf; n_check; b++, n_check--) {
1942 if (*b != *((const char*) pattern))
1943 continue;
1944 if (memcmp(b, pattern, pattern_size) == 0)
1945 break; /*found*/
1946 }
1947 if ( n_check ) {
1948 /* pattern found */
1949 size_t x_discarded = (size_t)(b - buf) + pattern_size;
1950 if ( discard )
1951 BUF_Write(discard, buf + n_read, x_discarded - n_read);
1952 if ( n_discarded )
1953 *n_discarded += x_discarded - n_read;
1954 /* return unused portion to the stream */
1955 status = io_func(stream, buf + x_discarded,
1956 n_stored - x_discarded, 0, eIO_Write);
1957 break; /*finished*/
1958 }
1959 }
1960
1961 /* pattern not found yet */
1962 if (discard && !BUF_Write(discard, buf + n_read, x_read))
1963 discard = 0;
1964 if ( n_discarded )
1965 *n_discarded += x_read;
1966
1967 if (n_stored >= pattern_size) {
1968 n_read = pattern_size - 1;
1969 memmove(buf, buf + n_stored - n_read, n_read);
1970 } else
1971 n_read = n_stored;
1972 }
1973 }
1974
1975 /* cleanup & exit */
1976 if (buf && buf != x_buf)
1977 free(buf);
1978 return status;
1979 }
1980
1981
s_CONN_IO(void * stream,void * buf,size_t size,size_t * n_read,EIO_Event what)1982 static EIO_Status s_CONN_IO
1983 (void* stream,
1984 void* buf,
1985 size_t size,
1986 size_t* n_read,
1987 EIO_Event what)
1988 {
1989 switch (what) {
1990 case eIO_Read:
1991 return CONN_Read((CONN) stream, buf, size, n_read, eIO_ReadPlain);
1992 case eIO_Write:
1993 assert(stream);
1994 return CONN_Pushback((CONN) stream, buf, size);
1995 default:
1996 break;
1997 }
1998 return eIO_InvalidArg;
1999 }
2000
CONN_StripToPattern(CONN conn,const void * pattern,size_t pattern_size,BUF * discard,size_t * n_discarded)2001 extern EIO_Status CONN_StripToPattern
2002 (CONN conn,
2003 const void* pattern,
2004 size_t pattern_size,
2005 BUF* discard,
2006 size_t* n_discarded)
2007 {
2008 return s_StripToPattern
2009 (conn, s_CONN_IO, pattern, pattern_size, discard, n_discarded);
2010 }
2011
s_SOCK_IO(void * stream,void * buf,size_t size,size_t * n_read,EIO_Event what)2012 static EIO_Status s_SOCK_IO
2013 (void* stream,
2014 void* buf,
2015 size_t size,
2016 size_t* n_read,
2017 EIO_Event what)
2018 {
2019 switch (what) {
2020 case eIO_Read:
2021 return SOCK_Read((SOCK) stream, buf, size, n_read, eIO_ReadPlain);
2022 case eIO_Write:
2023 return SOCK_Pushback((SOCK) stream, buf, size);
2024 default:
2025 break;
2026 }
2027 return eIO_InvalidArg;
2028 }
2029
SOCK_StripToPattern(SOCK sock,const void * pattern,size_t pattern_size,BUF * discard,size_t * n_discarded)2030 extern EIO_Status SOCK_StripToPattern
2031 (SOCK sock,
2032 const void* pattern,
2033 size_t pattern_size,
2034 BUF* discard,
2035 size_t* n_discarded)
2036 {
2037 return s_StripToPattern
2038 (sock, s_SOCK_IO, pattern, pattern_size, discard, n_discarded);
2039 }
2040
2041
s_BUF_IO(void * stream,void * buf,size_t size,size_t * n_read,EIO_Event what)2042 static EIO_Status s_BUF_IO
2043 (void* stream,
2044 void* buf,
2045 size_t size,
2046 size_t* n_read,
2047 EIO_Event what)
2048 {
2049 BUF b;
2050 switch (what) {
2051 case eIO_Read:
2052 *n_read = BUF_Read((BUF) stream, buf, size);
2053 return *n_read ? eIO_Success : eIO_Closed;
2054 case eIO_Write:
2055 assert(stream);
2056 b = (BUF) stream;
2057 return BUF_Pushback(&b, buf, size) ? eIO_Success : eIO_Unknown;
2058 default:
2059 break;
2060 }
2061 return eIO_InvalidArg;
2062 }
2063
BUF_StripToPattern(BUF buffer,const void * pattern,size_t pattern_size,BUF * discard,size_t * n_discarded)2064 extern EIO_Status BUF_StripToPattern
2065 (BUF buffer,
2066 const void* pattern,
2067 size_t pattern_size,
2068 BUF* discard,
2069 size_t* n_discarded)
2070 {
2071 return s_StripToPattern
2072 (buffer, s_BUF_IO, pattern, pattern_size, discard, n_discarded);
2073 }
2074
2075
2076
2077 /****************************************************************************
2078 * URL- Encoding/Decoding
2079 */
2080
2081
2082 /* Return integer (0..15) corresponding to the "ch" as a hex digit
2083 * Return -1 on error
2084 */
s_HexChar(char ch)2085 static int s_HexChar(char ch)
2086 {
2087 unsigned int rc = ch - '0';
2088 if (rc <= 9)
2089 return rc;
2090 rc = (ch | ' ') - 'a';
2091 return rc <= 5 ? (int) rc + 10 : -1;
2092 }
2093
2094
2095 /* The URL-encoding table
2096 */
2097 static const char s_EncodeTable[256][4] = {
2098 "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
2099 "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
2100 "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
2101 "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
2102 "+", "!", "%22", "%23", "$", "%25", "%26", "'",
2103 "(", ")", "*", "%2B", ",", "-", ".", "%2F",
2104 "0", "1", "2", "3", "4", "5", "6", "7",
2105 "8", "9", "%3A", "%3B", "%3C", "%3D", "%3E", "%3F",
2106 "%40", "A", "B", "C", "D", "E", "F", "G",
2107 "H", "I", "J", "K", "L", "M", "N", "O",
2108 "P", "Q", "R", "S", "T", "U", "V", "W",
2109 "X", "Y", "Z", "%5B", "%5C", "%5D", "%5E", "_",
2110 "%60", "a", "b", "c", "d", "e", "f", "g",
2111 "h", "i", "j", "k", "l", "m", "n", "o",
2112 "p", "q", "r", "s", "t", "u", "v", "w",
2113 "x", "y", "z", "%7B", "%7C", "%7D", "%7E", "%7F",
2114 "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
2115 "%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F",
2116 "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
2117 "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F",
2118 "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7",
2119 "%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF",
2120 "%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7",
2121 "%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF",
2122 "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7",
2123 "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF",
2124 "%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7",
2125 "%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF",
2126 "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7",
2127 "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF",
2128 "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7",
2129 "%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF"
2130 };
2131
2132 #define VALID_URL_SYMBOL(ch) (s_EncodeTable[(unsigned char)ch][0] != '%')
2133
2134
URL_DecodeEx(const void * src_buf,size_t src_size,size_t * src_read,void * dst_buf,size_t dst_size,size_t * dst_written,const char * allow_symbols)2135 extern int/*bool*/ URL_DecodeEx
2136 (const void* src_buf,
2137 size_t src_size,
2138 size_t* src_read,
2139 void* dst_buf,
2140 size_t dst_size,
2141 size_t* dst_written,
2142 const char* allow_symbols)
2143 {
2144 unsigned char* src = (unsigned char*) src_buf;
2145 unsigned char* dst = (unsigned char*) dst_buf;
2146
2147 *src_read = 0;
2148 *dst_written = 0;
2149 if (!src_size || !dst_size)
2150 return 1/*true*/;
2151 if (!src || !dst)
2152 return 0/*false*/;
2153
2154 for ( ; *src_read != src_size && *dst_written != dst_size;
2155 (*src_read)++, (*dst_written)++, src++, dst++) {
2156 switch ( *src ) {
2157 case '+': {
2158 *dst = ' ';
2159 break;
2160 }
2161 case '%': {
2162 int i1, i2;
2163 if (*src_read + 2 < src_size) {
2164 if ((i1 = s_HexChar(src[1])) != -1 &&
2165 (i2 = s_HexChar(src[2])) != -1) {
2166 *dst = (unsigned char)((i1 << 4) + i2);
2167 *src_read += 2;
2168 src += 2;
2169 break;
2170 }
2171 } else if (src != src_buf) {
2172 assert(*dst_written);
2173 return 1/*true*/;
2174 }
2175 if (!allow_symbols || *allow_symbols)
2176 return *dst_written ? 1/*true*/ : 0/*false*/;
2177 /*FALLTHRU*/
2178 }
2179 default: {
2180 if (VALID_URL_SYMBOL(*src)
2181 || (allow_symbols && (!*allow_symbols
2182 || strchr(allow_symbols, *src)))) {
2183 *dst = *src;
2184 } else
2185 return *dst_written ? 1/*true*/ : 0/*false*/;
2186 }
2187 }/*switch*/
2188 }
2189
2190 assert(src == (unsigned char*) src_buf + *src_read );
2191 assert(dst == (unsigned char*) dst_buf + *dst_written);
2192 return 1/*true*/;
2193 }
2194
2195
URL_Decode(const void * src_buf,size_t src_size,size_t * src_read,void * dst_buf,size_t dst_size,size_t * dst_written)2196 extern int/*bool*/ URL_Decode
2197 (const void* src_buf,
2198 size_t src_size,
2199 size_t* src_read,
2200 void* dst_buf,
2201 size_t dst_size,
2202 size_t* dst_written)
2203 {
2204 return URL_DecodeEx
2205 (src_buf, src_size, src_read, dst_buf, dst_size, dst_written, 0);
2206 }
2207
2208
URL_EncodeEx(const void * src_buf,size_t src_size,size_t * src_read,void * dst_buf,size_t dst_size,size_t * dst_written,const char * allow_symbols)2209 extern void URL_EncodeEx
2210 (const void* src_buf,
2211 size_t src_size,
2212 size_t* src_read,
2213 void* dst_buf,
2214 size_t dst_size,
2215 size_t* dst_written,
2216 const char* allow_symbols)
2217 {
2218 unsigned char* src = (unsigned char*) src_buf;
2219 unsigned char* dst = (unsigned char*) dst_buf;
2220
2221 *src_read = 0;
2222 *dst_written = 0;
2223 if (!src_size || !dst_size || !dst || !src)
2224 return;
2225
2226 for ( ; *src_read != src_size && *dst_written != dst_size;
2227 (*src_read)++, (*dst_written)++, src++, dst++) {
2228 const char* subst = allow_symbols ? strchr(allow_symbols, *src) : 0;
2229 if (!subst)
2230 subst = s_EncodeTable[*src];
2231 if (*subst != '%') {
2232 *dst = *subst;
2233 } else if (*dst_written < dst_size - 2) {
2234 *dst = '%';
2235 *(++dst) = *(++subst);
2236 *(++dst) = *(++subst);
2237 *dst_written += 2;
2238 } else
2239 return;
2240 }
2241 assert(src == (unsigned char*) src_buf + *src_read );
2242 assert(dst == (unsigned char*) dst_buf + *dst_written);
2243 }
2244
2245
URL_Encode(const void * src_buf,size_t src_size,size_t * src_read,void * dst_buf,size_t dst_size,size_t * dst_written)2246 extern void URL_Encode
2247 (const void* src_buf,
2248 size_t src_size,
2249 size_t* src_read,
2250 void* dst_buf,
2251 size_t dst_size,
2252 size_t* dst_written)
2253 {
2254 URL_EncodeEx
2255 (src_buf, src_size, src_read, dst_buf, dst_size, dst_written, 0);
2256 }
2257
2258
2259
2260 /****************************************************************************
2261 * NCBI-specific MIME content type and sub-types
2262 */
2263
2264
2265 static const char* s_MIME_Type[eMIME_T_Unknown+1] = {
2266 "x-ncbi-data",
2267 "text",
2268 "application",
2269 "unknown"
2270 };
2271
2272 static const char* s_MIME_SubType[eMIME_Unknown+1] = {
2273 "x-dispatch",
2274 "x-asn-text",
2275 "x-asn-binary",
2276 "x-fasta",
2277 "x-www-form",
2278 "html",
2279 "plain",
2280 "xml",
2281 "xml+soap",
2282 "octet-stream",
2283 "x-unknown"
2284 };
2285
2286 static const char* s_MIME_Encoding[eENCOD_Unknown+1] = {
2287 "",
2288 "urlencoded",
2289 "encoded"
2290 };
2291
2292
MIME_ComposeContentTypeEx(EMIME_Type type,EMIME_SubType subtype,EMIME_Encoding encoding,char * buf,size_t buflen)2293 extern char* MIME_ComposeContentTypeEx
2294 (EMIME_Type type,
2295 EMIME_SubType subtype,
2296 EMIME_Encoding encoding,
2297 char* buf,
2298 size_t buflen)
2299 {
2300 static const char s_ContentType[] = "Content-Type: ";
2301 const char* x_type;
2302 const char* x_subtype;
2303 const char* x_encoding;
2304 char x_buf[MAX_CONTENT_TYPE_LEN];
2305
2306 assert(buf && buflen);
2307
2308 if (type == eMIME_T_Undefined || subtype == eMIME_Undefined)
2309 return 0;
2310 if (type >= eMIME_T_Unknown)
2311 type = eMIME_T_Unknown;
2312 if (subtype >= eMIME_Unknown)
2313 subtype = eMIME_Unknown;
2314 if (encoding >= eENCOD_Unknown)
2315 encoding = eENCOD_Unknown;
2316
2317 x_type = s_MIME_Type [type];
2318 x_subtype = s_MIME_SubType [subtype];
2319 x_encoding = s_MIME_Encoding[encoding];
2320
2321 if ( *x_encoding ) {
2322 assert(sizeof(s_ContentType) + strlen(x_type) + strlen(x_subtype)
2323 + strlen(x_encoding) + 4 < MAX_CONTENT_TYPE_LEN);
2324 sprintf(x_buf, "%s%s/%s-%s\r\n",
2325 s_ContentType, x_type, x_subtype, x_encoding);
2326 } else {
2327 assert(sizeof(s_ContentType) + strlen(x_type) + strlen(x_subtype)
2328 + 3 < MAX_CONTENT_TYPE_LEN);
2329 sprintf(x_buf, "%s%s/%s\r\n", s_ContentType, x_type, x_subtype);
2330 }
2331 assert(strlen(x_buf) < sizeof(x_buf));
2332 assert(strlen(x_buf) < buflen);
2333 strncpy0(buf, x_buf, buflen - 1);
2334 return buf;
2335 }
2336
2337
MIME_ParseContentTypeEx(const char * str,EMIME_Type * type,EMIME_SubType * subtype,EMIME_Encoding * encoding)2338 extern int/*bool*/ MIME_ParseContentTypeEx
2339 (const char* str,
2340 EMIME_Type* type,
2341 EMIME_SubType* subtype,
2342 EMIME_Encoding* encoding)
2343 {
2344 char* x_buf;
2345 size_t x_size;
2346 char* x_type;
2347 char* x_subtype;
2348 int i;
2349
2350 if ( type )
2351 *type = eMIME_T_Undefined;
2352 if ( subtype )
2353 *subtype = eMIME_Undefined;
2354 if ( encoding )
2355 *encoding = eENCOD_None;
2356
2357 x_size = str && *str ? strlen(str) + 1 : 0;
2358 if (!x_size)
2359 return 0/*false*/;
2360
2361 if (!(x_buf = (char*) malloc(x_size << 1)))
2362 return 0/*false*/;
2363 x_type = x_buf + x_size;
2364
2365 strlwr(strcpy(x_buf, str));
2366
2367 if ((sscanf(x_buf, " content-type: %s ", x_type) != 1 &&
2368 sscanf(x_buf, " %s ", x_type) != 1) ||
2369 (x_subtype = strchr(x_type, '/')) == 0) {
2370 free(x_buf);
2371 return 0/*false*/;
2372 }
2373 *x_subtype++ = '\0';
2374 x_size = strlen(x_subtype);
2375
2376 if ( type ) {
2377 for (i = 0; i < (int) eMIME_T_Unknown; i++) {
2378 if (strcmp(x_type, s_MIME_Type[i]) == 0)
2379 break;
2380 }
2381 *type = (EMIME_Type) i;
2382 }
2383
2384 for (i = 1; i <= (int) eENCOD_Unknown; i++) {
2385 size_t len = strlen(s_MIME_Encoding[i]);
2386 if (len < x_size) {
2387 char* x_encoding = x_subtype + x_size - len;
2388 if (x_encoding[-1] == '-'
2389 && strcmp(x_encoding, s_MIME_Encoding[i]) == 0) {
2390 if ( encoding ) {
2391 *encoding = (i == (int) eENCOD_Unknown
2392 ? eENCOD_None : (EMIME_Encoding) i);
2393 }
2394 x_encoding[-1] = '\0';
2395 break;
2396 }
2397 }
2398 }
2399
2400 if ( subtype ) {
2401 for (i = 0; i < (int) eMIME_Unknown; i++) {
2402 if (strcmp(x_subtype, s_MIME_SubType[i]) == 0)
2403 break;
2404 }
2405 *subtype = (EMIME_SubType) i;
2406 }
2407
2408 free(x_buf);
2409 return 1/*true*/;
2410 }
2411
2412
SERV_InitFirewallPorts(void)2413 void SERV_InitFirewallPorts(void)
2414 {
2415 memset(s_FWPorts, 0, sizeof(s_FWPorts));
2416 }
2417
2418
SERV_AddFirewallPort(unsigned short port)2419 int/*bool*/ SERV_AddFirewallPort(unsigned short port)
2420 {
2421 unsigned int n, m;
2422 if (!port--)
2423 return 0/*false*/;
2424 n = port / (sizeof(s_FWPorts[0]) << 3);
2425 m = port % (sizeof(s_FWPorts[0]) << 3);
2426 if ((size_t) n < SizeOf(s_FWPorts)) {
2427 s_FWPorts[n] |= (TNCBI_BigCount) 1 << m;
2428 return 1/*true*/;
2429 }
2430 return 0/*false*/;
2431 }
2432
2433
SERV_IsFirewallPort(unsigned short port)2434 int/*bool*/ SERV_IsFirewallPort(unsigned short port)
2435 {
2436 unsigned int n, m;
2437 if (!port--)
2438 return 0/*false*/;
2439 n = port / (sizeof(s_FWPorts[0]) << 3);
2440 m = port % (sizeof(s_FWPorts[0]) << 3);
2441 if ((size_t) n < SizeOf(s_FWPorts) &&
2442 s_FWPorts[n] & ((TNCBI_BigCount) 1 << m)) {
2443 return 1/*true*/;
2444 }
2445 return 0/*false*/;
2446 }
2447
2448
SERV_PrintFirewallPorts(char * buf,size_t bufsize,EFWMode mode)2449 void SERV_PrintFirewallPorts(char* buf, size_t bufsize, EFWMode mode)
2450 {
2451 size_t len, n;
2452 unsigned int m;
2453
2454 assert(buf && bufsize > 1);
2455 switch (mode) {
2456 case eFWMode_Legacy:
2457 *buf = '\0';
2458 return;
2459 case eFWMode_Firewall:
2460 memcpy(buf, "0", 2);
2461 return;
2462 default:
2463 break;
2464 }
2465 len = 0;
2466 for (n = m = 0; n < SizeOf(s_FWPorts); ++n, m += sizeof(s_FWPorts[0])<<3) {
2467 unsigned short p;
2468 TNCBI_BigCount mask = s_FWPorts[n];
2469 for (p = m + 1; mask; ++p, mask >>= 1) {
2470 if (mask & 1) {
2471 char port[10];
2472 int k = sprintf(port, &" %hu"[!len], p);
2473 if (len + k < bufsize) {
2474 memcpy(buf + len, port, k);
2475 len += k;
2476 }
2477 }
2478 }
2479 }
2480 buf[len] = '\0';
2481 }
2482