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