1 /* $Id: ncbi_service_connector.c,v 6.152 2016/12/30 17:29:14 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:  Anton Lavrentiev
27  *
28  * File Description:
29  *   Implementation of CONNECTOR to a named service
30  *
31  */
32 
33 #include "ncbi_ansi_ext.h"
34 #include "ncbi_comm.h"
35 #include "ncbi_priv.h"
36 #include "ncbi_servicep.h"
37 #include "ncbi_socketp.h"
38 #include <connect/ncbi_service_connector.h>
39 #include <connect/ncbi_socket_connector.h>
40 #include <ctype.h>
41 #include <stdlib.h>
42 
43 #define NCBI_USE_ERRCODE_X   Connect_Service
44 
45 
46 typedef struct SServiceConnectorTag {
47     const char*    type;                /* Verbal connector type             */
48     const char*    descr;               /* Verbal connector description      */
49     SConnNetInfo*  net_info;            /* Connection information            */
50     const char*    user_header;         /* User header currently set         */
51     SERV_ITER      iter;                /* Dispatcher information            */
52     SMetaConnector meta;                /* Low level comm.conn and its VT    */
53     unsigned int   host;                /* Parsed connection info... (n.b.o) */
54     unsigned short port;                /*                       ... (h.b.o) */
55     unsigned short retry;               /* Open retry count since last okay  */
56     TSERV_TypeOnly types;               /* Server types w/o any specials     */
57     unsigned       reset:1;             /* Non-zero if iter was just reset   */
58     SSERVICE_Extra extra;               /* Extra params as passed to ctor    */
59     ticket_t       ticket;              /* Network byte order (none if zero) */
60     EIO_Status     status;              /* Status of last op                 */
61     const char     service[1];          /* Untranslated (orig.) service name */
62 } SServiceConnector;
63 
64 
65 /***********************************************************************
66  *  INTERNAL -- "s_VT_*" functions for the "virt. table" of connector methods
67  ***********************************************************************/
68 
69 #ifdef __cplusplus
70 extern "C" {
71 #endif /*__cplusplus*/
72     static const char* s_VT_GetType(CONNECTOR       connector);
73     static char*       s_VT_Descr  (CONNECTOR       connector);
74     static EIO_Status  s_VT_Open   (CONNECTOR       connector,
75                                     const STimeout* timeout);
76     static EIO_Status  s_VT_Status (CONNECTOR       connector,
77                                     EIO_Event       direction);
78     static EIO_Status  s_VT_Close  (CONNECTOR       connector,
79                                     const STimeout* timeout);
80     static void        s_Setup     (CONNECTOR       connector);
81     static void        s_Destroy   (CONNECTOR       connector);
82 #ifdef __cplusplus
83 } /* extern "C" */
84 #endif /*__cplusplus*/
85 
86 
s_OpenDispatcher(SServiceConnector * uuu)87 static int/*bool*/ s_OpenDispatcher(SServiceConnector* uuu)
88 {
89     TSERV_Type types = uuu->types;
90     if (uuu->net_info->stateless)
91         types |= fSERV_Stateless;
92     if (!(uuu->iter = SERV_Open(uuu->service, types,
93                                 SERV_LOCALHOST, uuu->net_info))) {
94         CORE_LOGF_X(5, eLOG_Error,
95                     ("[%s]  Service not found", uuu->service));
96         return 0/*false*/;
97     }
98     uuu->reset = 1/*true*/;
99     return 1/*true*/;
100 }
101 
102 
s_CloseDispatcher(SServiceConnector * uuu)103 static void s_CloseDispatcher(SServiceConnector* uuu)
104 {
105     SERV_Close(uuu->iter);
106     uuu->iter = 0;
107 }
108 
109 
110 /* Reset functions, which are implemented only in transport
111  * connectors, but not in this connector.
112  */
s_Reset(SMetaConnector * meta,CONNECTOR connector)113 static void s_Reset(SMetaConnector *meta, CONNECTOR connector)
114 {
115     CONN_SET_METHOD(meta, descr,  s_VT_Descr,  connector);
116     CONN_SET_METHOD(meta, wait,   0,           0);
117     CONN_SET_METHOD(meta, write,  0,           0);
118     CONN_SET_METHOD(meta, flush,  0,           0);
119     CONN_SET_METHOD(meta, read,   0,           0);
120     CONN_SET_METHOD(meta, status, s_VT_Status, connector);
121 }
122 
123 
s_ParseHeader(const char * header,void * user_data,int server_error,int user_callback_enabled)124 static EHTTP_HeaderParse s_ParseHeader(const char* header,
125                                        void*       user_data,
126                                        int         server_error,
127                                        int/*bool*/ user_callback_enabled)
128 {
129     static const char   kStateless[] = "TRY_STATELESS";
130     static const size_t kSLen = sizeof(kStateless) - 1;
131     SServiceConnector*  uuu = (SServiceConnector*) user_data;
132     EHTTP_HeaderParse   header_parse;
133 
134     SERV_Update(uuu->iter, header, server_error);
135     if (user_callback_enabled  &&  uuu->extra.parse_header) {
136         header_parse
137             = uuu->extra.parse_header(header, uuu->extra.data, server_error);
138         if (server_error  ||  !header_parse)
139             return header_parse;
140     } else {
141         if (server_error)
142             return eHTTP_HeaderSuccess;
143         header_parse = eHTTP_HeaderError;
144     }
145 
146     while (header  &&  *header) {
147         if (strncasecmp(header, HTTP_CONNECTION_INFO,
148                         sizeof(HTTP_CONNECTION_INFO) - 1) == 0) {
149             if (uuu->host)
150                 break/*failed - duplicate connection info*/;
151             header += sizeof(HTTP_CONNECTION_INFO) - 1;
152             while (*header  &&  isspace((unsigned char)(*header)))
153                 header++;
154             if (strncasecmp(header, kStateless, kSLen) == 0  &&
155                 (!header[kSLen]  ||  isspace((unsigned char) header[kSLen]))) {
156                 /* Special keyword for switching into stateless mode */
157                 uuu->host = (unsigned int)(-1);
158 #if defined(_DEBUG)  &&  !defined(NDEBUG)
159                 if (uuu->net_info->debug_printout) {
160                     CORE_LOGF_X(2, eLOG_Note,
161                                 ("[%s]  Fallback to stateless", uuu->service));
162                 }
163 #endif /*_DEBUG && !NDEBUG*/
164             } else {
165                 unsigned int  i1, i2, i3, i4, tkt, n, m;
166                 unsigned char o1, o2, o3, o4;
167                 char ipaddr[40];
168 
169                 if (sscanf(header, "%u.%u.%u.%u%n", &i1, &i2, &i3, &i4, &n) < 4
170                     ||  sscanf(header + n, "%hu%x%n", &uuu->port, &tkt, &m) < 2
171                     || (header[m += n] && !isspace((unsigned char)header[m]))){
172                     break/*failed - unreadable connection info*/;
173                 }
174                 o1 = i1; o2 = i2; o3 = i3; o4 = i4;
175                 sprintf(ipaddr, "%u.%u.%u.%u", o1, o2, o3, o4);
176                 if (strncmp(header, ipaddr, n) != 0
177                     ||  !(uuu->host = SOCK_gethostbyname(ipaddr))
178                     ||  !uuu->port) {
179                     break/*failed - bad host:port in connection info*/;
180                 }
181                 uuu->ticket = SOCK_HostToNetLong(tkt);
182             }
183         }
184         if ((header = strchr(header, '\n')) != 0)
185             header++;
186     }
187 
188     if (header  &&  *header)
189         uuu->host = 0;
190     else if (!header_parse)
191         header_parse = eHTTP_HeaderSuccess;
192     return header_parse;
193 }
194 
195 
196 #ifdef __cplusplus
197 extern "C" {
198 #endif /*__cplusplus*/
199 static EHTTP_HeaderParse
s_ParseHeaderUCB(const char * header,void * data,int server_error)200 s_ParseHeaderUCB  (const char* header, void* data, int server_error)
201 {
202     return s_ParseHeader(header, data, server_error, 1/*enable user CB*/);
203 }
204 #ifdef __cplusplus
205 }
206 #endif /*__cplusplus*/
207 
208 
209 #ifdef __cplusplus
210 extern "C" {
211 #endif /*__cplusplus*/
212 static EHTTP_HeaderParse
s_ParseHeaderNoUCB(const char * header,void * data,int server_error)213 s_ParseHeaderNoUCB(const char* header, void* data, int server_error)
214 {
215     return s_ParseHeader(header, data, server_error, 0/*disable user CB*/);
216 }
217 #ifdef __cplusplus
218 }
219 #endif /*__cplusplus*/
220 
221 
222 /*ARGSUSED*/
s_IsContentTypeDefined(const char * service,const SConnNetInfo * net_info,EMIME_Type mime_t,EMIME_SubType mime_s,EMIME_Encoding mime_e)223 static int/*bool*/ s_IsContentTypeDefined(const char*         service,
224                                           const SConnNetInfo* net_info,
225                                           EMIME_Type          mime_t,
226                                           EMIME_SubType       mime_s,
227                                           EMIME_Encoding      mime_e)
228 {
229     const char* s;
230 
231     assert(net_info);
232     for (s = net_info->http_user_header;  s;  s = strchr(s, '\n')) {
233         if (s != net_info->http_user_header)
234             s++;
235         if (!*s)
236             break;
237         if (strncasecmp(s, "content-type: ", 14) == 0) {
238 #if defined(_DEBUG)  &&  !defined(NDEBUG)
239             EMIME_Type     m_t;
240             EMIME_SubType  m_s;
241             EMIME_Encoding m_e;
242             char           c_t[MAX_CONTENT_TYPE_LEN];
243             if (net_info->debug_printout         &&
244                 mime_t != eMIME_T_Undefined      &&
245                 mime_t != eMIME_T_Unknown        &&
246                 (!MIME_ParseContentTypeEx(s, &m_t, &m_s, &m_e)
247                  ||   mime_t != m_t
248                  ||  (mime_s != eMIME_Undefined  &&
249                       mime_s != eMIME_Unknown    &&
250                       m_s    != eMIME_Unknown    &&  mime_s != m_s)
251                  ||  (mime_e != eENCOD_None      &&
252                       m_e    != eENCOD_None      &&  mime_e != m_e))) {
253                 const char* c;
254                 size_t len;
255                 char* t;
256                 for (s += 15;  *s;  s++) {
257                     if (!isspace((unsigned char)(*s)))
258                         break;
259                 }
260                 if (!(c = strchr(s, '\n')))
261                     c = s + strlen(s);
262                 if (c > s  &&  c[-1] == '\r')
263                     c--;
264                 len = (size_t)(c - s);
265                 if ((t = (char*) malloc(len + 1)) != 0) {
266                     memcpy(t, s, len);
267                     t[len] = '\0';
268                 }
269                 if (!MIME_ComposeContentTypeEx(mime_t, mime_s, mime_e,
270                                                c_t, sizeof(c_t))) {
271                     *c_t = '\0';
272                 }
273                 CORE_LOGF_X(3, eLOG_Warning,
274                             ("[%s]  Content-Type mismatch: "
275                              "%s%s%s%s%s%s%s", service,
276                              t  &&  *t           ? "specified=<"  : "",
277                              t  &&  *t           ? t              : "",
278                              t  &&  *t           ? ">"            : "",
279                              t  &&  *t  &&  *c_t ? ", "           : "",
280                              *c_t                ? "configured=<" : "",
281                              *c_t                ? c_t            : "",
282                              *c_t                ? ">"            : ""));
283                 if (t)
284                     free(t);
285             }
286 #endif /*_DEBUG && !NDEBUG*/
287             return 1/*true*/;
288         }
289     }
290     return 0/*false*/;
291 }
292 
293 
s_AdjustNetParams(const char * service,SConnNetInfo * net_info,EReqMethod req_method,const char * cgi_path,const char * cgi_args,const char * args,const char * static_header,EMIME_Type mime_t,EMIME_SubType mime_s,EMIME_Encoding mime_e,const char * extend_header)294 static const char* s_AdjustNetParams(const char*    service,
295                                      SConnNetInfo*  net_info,
296                                      EReqMethod     req_method,
297                                      const char*    cgi_path,
298                                      const char*    cgi_args,
299                                      const char*    args,
300                                      const char*    static_header,
301                                      EMIME_Type     mime_t,
302                                      EMIME_SubType  mime_s,
303                                      EMIME_Encoding mime_e,
304                                      const char*    extend_header)
305 {
306     const char *retval = 0;
307 
308     net_info->req_method = req_method;
309 
310     if (cgi_path)
311         strncpy0(net_info->path, cgi_path, sizeof(net_info->path) - 1);
312 
313     if (args)
314         strncpy0(net_info->args, args, sizeof(net_info->args) - 1);
315     ConnNetInfo_DeleteAllArgs(net_info, cgi_args);
316 
317     if (ConnNetInfo_PrependArg(net_info, cgi_args, 0)) {
318         size_t sh_len = static_header ? strlen(static_header) : 0;
319         size_t eh_len = extend_header ? strlen(extend_header) : 0;
320         char   c_t[MAX_CONTENT_TYPE_LEN];
321         size_t len;
322 
323         if (s_IsContentTypeDefined(service, net_info, mime_t, mime_s, mime_e)
324             ||  !MIME_ComposeContentTypeEx(mime_t, mime_s, mime_e,
325                                            c_t, sizeof(c_t))) {
326             *c_t = '\0';
327             len = 0;
328         } else
329             len = strlen(c_t);
330         if ((len += sh_len + eh_len) != 0) {
331             char* temp = (char*) malloc(++len/*w/EOL*/);
332             if (temp) {
333                 retval = temp;
334                 if (static_header) {
335                     memcpy(temp, static_header, sh_len);
336                     temp += sh_len;
337                 }
338                 if (extend_header) {
339                     memcpy(temp, extend_header, eh_len);
340                     temp += eh_len;
341                 }
342                 strcpy(temp, c_t);
343                 assert(*retval);
344             }
345         } else
346             retval = "";
347     }
348 
349     return retval;
350 }
351 
352 
s_GetNextInfo(SServiceConnector * uuu,int http)353 static SSERV_InfoCPtr s_GetNextInfo(SServiceConnector* uuu, int/*bool*/ http)
354 {
355     for (;;) {
356         SSERV_InfoCPtr info = uuu->extra.get_next_info
357             ? uuu->extra.get_next_info(uuu->extra.data, uuu->iter)
358             : SERV_GetNextInfo(uuu->iter);
359         if (info) {
360             if (http) {
361                 /* Skip any 'stateful_capable' or unconnectable entries here,
362                  * which might have been left behind by either
363                  *   a/ a failed stateful dispatching that fallen back to
364                  *      stateless HTTP mode, or
365                  *   b/ a too relaxed server type selection.
366                  */
367                 if ((info->mode & fSERV_Stateful)  ||  info->type == fSERV_Dns)
368                     continue;
369             }
370             uuu->reset = 0/*false*/;
371             return info;
372         }
373         if (uuu->reset)
374             break;
375         SERV_Reset(uuu->iter);
376         uuu->reset = 1/*true*/;
377     }
378     return 0;
379 }
380 
381 
x_HostPort(const char * host,unsigned short nport)382 static char* x_HostPort(const char* host, unsigned short nport)
383 {
384     char*  hostport, port[16];
385     size_t hostlen = strlen(host);
386     size_t portlen = (size_t) sprintf(port, ":%hu", nport) + 1;
387     hostport = (char*) malloc(hostlen + portlen);
388     if (hostport) {
389         memcpy(hostport,           host, hostlen);
390         memcpy(hostport + hostlen, port, portlen);
391     }
392     return hostport;
393 }
394 
395 
396 /* Until r294766, this code used to send a ticket along with building the
397  * tunnel, but for buggy proxies, which ignore HTTP body as connection data
398  * (and thus violate the standard), that shortcut could not be utilized;
399  * so the longer multi-step sequence was introduced below, instead.
400  * Cf. ncbi_conn_stream.cpp: s_SocketConnectorBuilder().
401  */
s_SocketConnectorBuilder(SConnNetInfo * net_info,const char * hostport,EIO_Status * status,const void * data,size_t size,TSOCK_Flags flags)402 static CONNECTOR s_SocketConnectorBuilder(SConnNetInfo* net_info,
403                                           const char*   hostport,
404                                           EIO_Status*   status,
405                                           const void*   data,
406                                           size_t        size,
407                                           TSOCK_Flags   flags)
408 {
409     int/*bool*/ proxy = 0/*false*/;
410     SOCK        sock = 0, s;
411     SSOCK_Init  init;
412     CONNECTOR   c;
413 
414     flags |= (net_info->debug_printout == eDebugPrintout_Data
415               ? fSOCK_LogOn : fSOCK_LogDefault);
416     if (net_info->http_proxy_host[0]  &&  net_info->http_proxy_port) {
417         /* NB: ideally, should have pushed data:size here if proxy not buggy */
418         *status = HTTP_CreateTunnel(net_info, fHTTP_NoAutoRetry, &sock);
419         assert(!sock ^ !(*status != eIO_Success));
420         if (*status == eIO_Success
421             &&  (size  ||  (flags & ~(fSOCK_LogOn | fSOCK_LogDefault)))) {
422             /* push initial data through the proxy, as-is (i.e. clear-text) */
423             TSOCK_Flags tempf = flags;
424             if (size  &&  (flags & fSOCK_Secure)) {
425                 tempf &=  fSOCK_LogOn | fSOCK_LogDefault;
426                 tempf &= ~fSOCK_Secure;
427             }
428             memset(&init, 0, sizeof(init));
429             init.data = data;
430             init.size = size;
431             init.cred = 0;
432             *status  = SOCK_CreateOnTopInternal(sock, 0, &s,
433                                                 &init, tempf);
434             assert(!s ^ !(*status != eIO_Success));
435             SOCK_Destroy(sock);
436             sock = s;
437             if (*status == eIO_Success  &&  tempf != flags) {
438                 init.data = 0;
439                 init.size = 0;
440                 init.cred = net_info->credentials;
441                 *status  = SOCK_CreateOnTopInternal(sock, 0, &s,
442                                                     &init, flags);
443                 assert(!s ^ !(*status != eIO_Success));
444                 SOCK_Destroy(sock);
445                 sock = s;
446             }
447         }
448         proxy = 1/*true*/;
449     }
450     if (!sock  &&  (!proxy  ||  net_info->http_proxy_leak)) {
451         TSOCK_Flags tempf = flags;
452         if (size  &&  (flags & fSOCK_Secure)) {
453             tempf &=  fSOCK_LogOn | fSOCK_LogDefault;
454             tempf &= ~fSOCK_Secure;
455         }
456         if (!proxy  &&  net_info->debug_printout) {
457             net_info->scheme = eURL_Unspec;
458             net_info->req_method = eReqMethod_Any;
459             net_info->firewall = 0;
460             net_info->stateless = 0;
461             net_info->lb_disable = 0;
462             net_info->user[0] = '\0';
463             net_info->pass[0] = '\0';
464             net_info->path[0] = '\0';
465             net_info->args[0] = '\0';
466             net_info->http_proxy_host[0] = '\0';
467             net_info->http_proxy_port    =   0;
468             net_info->http_proxy_user[0] = '\0';
469             net_info->http_proxy_pass[0] = '\0';
470             ConnNetInfo_SetUserHeader(net_info, 0);
471             if (net_info->http_referer) {
472                 free((void*) net_info->http_referer);
473                 net_info->http_referer = 0;
474             }
475             ConnNetInfo_Log(net_info, eLOG_Note, CORE_GetLOG());
476         }
477         memset(&init, 0, sizeof(init));
478         init.data = data;
479         init.size = size;
480         init.cred = 0;
481         *status = SOCK_CreateInternal(net_info->host, net_info->port,
482                                       net_info->timeout, &sock,
483                                       &init, tempf);
484         assert(!sock ^ !(*status != eIO_Success));
485         if (*status == eIO_Success  &&  tempf != flags) {
486             init.data = 0;
487             init.size = 0;
488             init.cred = net_info->credentials;
489             *status  = SOCK_CreateOnTopInternal(sock, 0, &s,
490                                                 &init, flags);
491             assert(!s ^ !(*status != eIO_Success));
492             SOCK_Destroy(sock);
493             sock = s;
494         }
495     }
496     if (!(c = SOCK_CreateConnectorOnTopEx(sock, 1/*own*/, hostport))) {
497         if (*status == eIO_Success)
498             *status  = eIO_Unknown;
499         SOCK_Abort(sock);
500         SOCK_Close(sock);
501     }
502     return c;
503 }
504 
505 
506 /* Although all additional HTTP tags that comprise the dispatching have their
507  * default values, which in most cases are fine with us, we will use these tags
508  * explicitly to distinguish the calls originated within the service connector
509  * from other calls (e.g. by Web browsers), and let the dispatcher decide
510  * whether to use more expensive dispatching (involving loopback connections)
511  * in the latter case.
512  */
513 
514 
515 #ifdef __cplusplus
516 extern "C" {
517     static int s_Adjust(SConnNetInfo*, void*, unsigned int);
518 }
519 #endif /*__cplusplus*/
520 
521 /*ARGSUSED*/
522 /* NB: This callback is only for services called via direct HTTP */
s_Adjust(SConnNetInfo * net_info,void * data,unsigned int n)523 static int/*bool*/ s_Adjust(SConnNetInfo* net_info,
524                             void*         data,
525                             unsigned int  n)
526 {
527     SServiceConnector* uuu = (SServiceConnector*) data;
528     const char*        user_header;
529     char*              iter_header;
530     SSERV_InfoCPtr     info;
531 
532     assert(n  ||  uuu->extra.adjust);
533     assert(!net_info->firewall  ||  net_info->stateless);
534 
535     if (!n)
536         return uuu->extra.adjust(net_info, uuu->extra.data, 0);
537 
538     if (uuu->retry >= uuu->net_info->max_try)
539         return 0/*failure - too many errors*/;
540     uuu->retry++;
541 
542     if (!(info = s_GetNextInfo(uuu, 1/*http*/)))
543         return 0/*failure - not adjusted*/;
544 
545     iter_header = SERV_Print(uuu->iter, 0, 0);
546     switch (info->type) {
547     case fSERV_Ncbid:
548         user_header = "Connection-Mode: STATELESS\r\n"; /*default*/
549         user_header = s_AdjustNetParams(uuu->service, net_info,
550                                         eReqMethod_Post,
551                                         NCBID_WEBPATH,
552                                         SERV_NCBID_ARGS(&info->u.ncbid),
553                                         uuu->net_info->args,
554                                         user_header, info->mime_t,
555                                         info->mime_s, info->mime_e,
556                                         iter_header);
557         break;
558     case fSERV_Http:
559     case fSERV_HttpGet:
560     case fSERV_HttpPost:
561         user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/
562         user_header = s_AdjustNetParams(uuu->service, net_info,
563                                         info->type == fSERV_HttpPost
564                                         ?  eReqMethod_Post
565                                         : (info->type == fSERV_HttpGet
566                                            ? eReqMethod_Get
567                                            : eReqMethod_Any),
568                                         SERV_HTTP_PATH(&info->u.http),
569                                         SERV_HTTP_ARGS(&info->u.http),
570                                         uuu->net_info->args,
571                                         user_header, info->mime_t,
572                                         info->mime_s, info->mime_e,
573                                         iter_header);
574         break;
575     case fSERV_Firewall:
576     case fSERV_Standalone:
577         user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/
578         user_header = s_AdjustNetParams(uuu->service, net_info,
579                                         eReqMethod_Any,
580                                         uuu->net_info->path, 0,
581                                         uuu->net_info->args,
582                                         user_header, info->mime_t,
583                                         info->mime_s, info->mime_e,
584                                         iter_header);
585         break;
586     default:
587         user_header = 0;
588         assert(0);
589         break;
590     }
591     if (iter_header)
592         free(iter_header);
593     if (!user_header)
594         return 0/*false - not adjusted*/;
595 
596     if (uuu->user_header) {
597         assert(*uuu->user_header);
598         ConnNetInfo_DeleteUserHeader(net_info, uuu->user_header);
599         free((void*) uuu->user_header);
600     }
601     if (*user_header) {
602         uuu->user_header = user_header;
603         if (!ConnNetInfo_OverrideUserHeader(net_info, user_header))
604             return 0/*failure - not adjusted*/;
605     } else /*NB: special case ""*/
606         uuu->user_header = 0;
607 
608     if (info->type == fSERV_Ncbid  ||  (info->type & fSERV_Http)) {
609         SOCK_ntoa(info->host, net_info->host, sizeof(net_info->host));
610         net_info->port = info->port;
611     } else {
612         strcpy(net_info->host, uuu->net_info->host);
613         net_info->port = uuu->net_info->port;
614     }
615     return uuu->extra.adjust
616         &&  !uuu->extra.adjust(net_info, uuu->extra.data, uuu->retry)
617         ? 0/*failure - not adjusted*/
618         : 1/*success - adjusted*/;
619 }
620 
621 
622 #ifdef __GNUC__
623 inline
624 #endif /*__GNUC__*/
x_DestroyConnector(CONNECTOR c)625 static void x_DestroyConnector(CONNECTOR c)
626 {
627     assert(!c->meta  &&  !c->next);
628     if (c->destroy)
629         c->destroy(c);
630 }
631 
632 
s_Open(SServiceConnector * uuu,const STimeout * timeout,SSERV_InfoCPtr info,SConnNetInfo * net_info,EIO_Status * status)633 static CONNECTOR s_Open(SServiceConnector* uuu,
634                         const STimeout*    timeout,
635                         SSERV_InfoCPtr     info,
636                         SConnNetInfo*      net_info,
637                         EIO_Status*        status)
638 {
639     int/*bool*/ but_last = 0/*false*/;
640     const char* user_header; /* either static "" or non-empty dynamic string */
641     char*       iter_header;
642     EReqMethod  req_method;
643 
644     if (info  &&  info->type != fSERV_Firewall) {
645         /* Not a firewall/relay connection here */
646         /* We know the connection point, let's try to use it! */
647         if (info->type != fSERV_Standalone  ||  !net_info->stateless) {
648             SOCK_ntoa(info->host, net_info->host, sizeof(net_info->host));
649             net_info->port = info->port;
650         }
651 
652         switch (info->type) {
653         case fSERV_Ncbid:
654             /* Connection directly to NCBID, add NCBID-specific tags */
655             if (info->mode & fSERV_Secure)
656                 net_info->scheme = eURL_Https;
657             if (net_info->stateless) {
658                 /* Connection request with data */
659                 user_header = "Connection-Mode: STATELESS\r\n"; /*default*/
660                 req_method  = eReqMethod_Post;
661             } else {
662                 /* We will be waiting for conn-info back */
663                 user_header = "Connection-Mode: STATEFUL\r\n";
664                 req_method  = eReqMethod_Get;
665             }
666             user_header = s_AdjustNetParams(uuu->service, net_info, req_method,
667                                             NCBID_WEBPATH,
668                                             SERV_NCBID_ARGS(&info->u.ncbid),
669                                             0, user_header, info->mime_t,
670                                             info->mime_s, info->mime_e, 0);
671             break;
672         case fSERV_Http:
673         case fSERV_HttpGet:
674         case fSERV_HttpPost:
675             /* Connection directly to CGI */
676             req_method  = info->type == fSERV_HttpGet
677                 ? eReqMethod_Get : (info->type == fSERV_HttpPost
678                                     ? eReqMethod_Post : eReqMethod_Any);
679             user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/
680             user_header = s_AdjustNetParams(uuu->service, net_info, req_method,
681                                             SERV_HTTP_PATH(&info->u.http),
682                                             SERV_HTTP_ARGS(&info->u.http),
683                                             0, user_header, info->mime_t,
684                                             info->mime_s, info->mime_e, 0);
685             break;
686         case fSERV_Standalone:
687             if (!net_info->stateless) {
688                 assert(!uuu->descr);
689                 uuu->descr = x_HostPort(net_info->host, net_info->port);
690                 return s_SocketConnectorBuilder(net_info, uuu->descr, status,
691                                                 0, 0, info->mode & fSERV_Secure
692                                                 ? fSOCK_Secure : 0);
693             }
694             /* Otherwise, it will be a pass-thru connection via dispatcher */
695             user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/
696             user_header = s_AdjustNetParams(uuu->service, net_info,
697                                             eReqMethod_Any, 0, 0,
698                                             0, user_header, info->mime_t,
699                                             info->mime_s, info->mime_e, 0);
700             but_last = 1/*true*/;
701             break;
702         default:
703             user_header = 0;
704             assert(0);
705             break;
706         }
707     } else {
708         EMIME_Type     mime_t;
709         EMIME_SubType  mime_s;
710         EMIME_Encoding mime_e;
711         if (!net_info->scheme)
712             net_info->scheme = eURL_Https;
713         if (net_info->stateless
714             ||  (info  &&  (info->u.firewall.type & fSERV_Http))) {
715             if (info) {
716                 req_method = (info->u.firewall.type == fSERV_HttpGet
717                               ? eReqMethod_Get
718                               : (info->u.firewall.type == fSERV_HttpPost
719                                  ? eReqMethod_Post
720                                  : eReqMethod_Any));
721                 net_info->stateless = 1/*true*/;
722             } else
723                 req_method = eReqMethod_Any;
724         } else
725             req_method = eReqMethod_Get;
726         if (info) {
727             mime_t = info->mime_t;
728             mime_s = info->mime_s;
729             mime_e = info->mime_e;
730         } else {
731             mime_t = eMIME_T_Undefined;
732             mime_s = eMIME_Undefined;
733             mime_e = eENCOD_None;
734         }
735         /* Firewall/relay connection thru dispatcher, special tags */
736         user_header = (net_info->stateless
737                        ? "Client-Mode: STATELESS_ONLY\r\n" /*default*/
738                        : "Client-Mode: STATEFUL_CAPABLE\r\n");
739         user_header = s_AdjustNetParams(uuu->service, net_info, req_method,
740                                         0, 0, 0, user_header,
741                                         mime_t, mime_s, mime_e, 0);
742         if (info)
743             but_last = 1/*true*/;
744     }
745     if (!user_header) {
746         *status = eIO_Unknown;
747         return 0;
748     }
749 
750     if ((iter_header = SERV_Print(uuu->iter, net_info, but_last)) != 0) {
751         size_t uh_len;
752         if ((uh_len = strlen(user_header)) > 0) {
753             char*  ih;
754             size_t ih_len = strlen(iter_header);
755             if ((ih = (char*) realloc(iter_header, ++uh_len + ih_len)) != 0) {
756                 memcpy(ih + ih_len, user_header, uh_len);
757                 iter_header = ih;
758             }
759             free((void*) user_header);
760         }
761         user_header = iter_header;
762     } else if (!*user_header)
763         user_header = 0; /* special case of assignment of literal "" */
764 
765     if (uuu->user_header) {
766         ConnNetInfo_DeleteUserHeader(net_info, uuu->user_header);
767         free((void*) uuu->user_header);
768     }
769     uuu->user_header = user_header;
770     if (user_header && !ConnNetInfo_OverrideUserHeader(net_info, user_header)){
771         *status = eIO_Unknown;
772         return 0;
773     }
774 
775     ConnNetInfo_ExtendUserHeader
776         (net_info, "User-Agent: NCBIServiceConnector/"
777          NCBI_DISP_VERSION
778 #ifdef NCBI_CXX_TOOLKIT
779          " (CXX Toolkit)"
780 #else
781          " (C Toolkit)"
782 #endif /*NCBI_CXX_TOOLKIT*/
783          );
784 
785     *status = eIO_Success;
786     if (!net_info->stateless  &&  (!info                         ||
787                                    info->type == fSERV_Firewall  ||
788                                    info->type == fSERV_Ncbid)) {
789         /* Auxiliary HTTP connector first */
790         EIO_Status temp = eIO_Success;
791         CONNECTOR c;
792         CONN conn;
793 
794         /* Clear connection info */
795         uuu->host = 0;
796         uuu->port = 0;
797         uuu->ticket = 0;
798         net_info->max_try = 1;
799         c = HTTP_CreateConnectorEx(net_info, fHTTP_Flushable,
800                                    s_ParseHeaderNoUCB, uuu/*user_data*/,
801                                    0/*adjust*/, 0/*cleanup*/);
802         /* Wait for connection info back from dispatcher */
803         if (c  &&  (temp = CONN_Create(c, &conn)) == eIO_Success) {
804             CONN_SetTimeout(conn, eIO_Open,      timeout);
805             CONN_SetTimeout(conn, eIO_ReadWrite, timeout);
806             CONN_SetTimeout(conn, eIO_Close,     timeout);
807             /* Send all the HTTP data... */
808             if ((temp = CONN_Flush(conn)) != eIO_Success)
809                 *status = temp;
810             /* ...then trigger the header callback */
811             if ((temp = CONN_Close(conn)) != eIO_Success  &&
812                  temp                     != eIO_Closed   &&
813                 *status < temp) {
814                 *status = temp;
815             }
816         } else {
817             /* can only happen if we're out of memory */
818             const char* error;
819             if (c) {
820                 error = IO_StatusStr(temp);
821                 x_DestroyConnector(c);
822                 *status = temp;
823             } else
824                 error = 0;
825             CORE_LOGF_X(4, eLOG_Error,
826                         ("[%s]  Unable to create auxiliary HTTP %s%s%s",
827                          uuu->service, c ? "connection" : "connector",
828                          error  &&  *error ? ": " : "", error ? error : ""));
829             assert(!uuu->host);
830         }
831         if (uuu->host == (unsigned int)(-1)) {
832             assert(!info  ||  info->type == fSERV_Firewall);
833             assert(!net_info->stateless);
834             net_info->stateless = 1/*true*/;
835             /* Fallback to try to use stateless mode instead */
836             return s_Open(uuu, timeout, info, net_info, status);
837         }
838         if (!uuu->host  ||  !uuu->port) {
839             /* no connection info found */
840             if (*status == eIO_Success)
841                 *status  = eIO_Unknown;
842             if (!net_info->scheme)
843                 net_info->scheme = eURL_Http;
844             net_info->args[0] = '\0';
845             assert(!uuu->descr);
846             uuu->descr = ConnNetInfo_URL(net_info);
847             return 0;
848         }
849         if (net_info->firewall == eFWMode_Fallback
850             &&  !SERV_IsFirewallPort(uuu->port)) {
851             CORE_LOGF_X(9, eLOG_Warning,
852                         ("[%s]  Firewall port :%hu is not in the fallback set",
853                          uuu->service, uuu->port));
854         }
855         ConnNetInfo_DeleteUserHeader(net_info, uuu->user_header);
856         SOCK_ntoa(uuu->host, net_info->host, sizeof(net_info->host));
857         net_info->port = uuu->port;
858         assert(!uuu->descr);
859         uuu->descr = x_HostPort(net_info->host, net_info->port);
860         if (net_info->http_proxy_host[0]  &&  net_info->http_proxy_port)
861             net_info->scheme = uuu->net_info->scheme;
862         return s_SocketConnectorBuilder(net_info, uuu->descr, status,
863                                         &uuu->ticket,
864                                         uuu->ticket ? sizeof(uuu->ticket) : 0,
865                                         info  &&  (info->mode & fSERV_Secure)
866                                         ? fSOCK_Secure : 0);
867     }
868     ConnNetInfo_DeleteUserHeader(net_info, "Host:");
869     if (info  &&  (info->mode & fSERV_Secure))
870         net_info->scheme = eURL_Https;
871     else if (!net_info->scheme)
872         net_info->scheme = eURL_Http;
873     assert(!uuu->descr);
874     uuu->descr = ConnNetInfo_URL(net_info);
875     return !uuu->extra.adjust
876         ||  uuu->extra.adjust(net_info, uuu->extra.data, (unsigned int)(-1))
877         ? HTTP_CreateConnectorEx(net_info,
878                                  (uuu->extra.flags
879                                   & (fHTTP_Flushable       |
880                                      fHTTP_NoAutoRetry     |
881                                      (uuu->extra.adjust
882                                       ? fHTTP_AdjustOnRedirect
883                                       : 0)))
884                                  | fHTTP_AutoReconnect,
885                                  s_ParseHeaderUCB, uuu/*user_data*/,
886                                  s_Adjust, 0/*cleanup*/)
887         : 0/*failure*/;
888 }
889 
890 
s_Cleanup(SServiceConnector * uuu)891 static void s_Cleanup(SServiceConnector* uuu)
892 {
893     if (uuu->type) {
894         free((void*) uuu->type);
895         uuu->type = 0;
896     }
897     if (uuu->descr) {
898         free((void*) uuu->descr);
899         uuu->descr = 0;
900     }
901     if (uuu->user_header) {
902         free((void*) uuu->user_header);
903         uuu->user_header = 0;
904     }
905 }
906 
907 
s_Close(CONNECTOR connector,const STimeout * timeout,int cleanup)908 static EIO_Status s_Close(CONNECTOR       connector,
909                           const STimeout* timeout,
910                           int/*bool*/     cleanup)
911 {
912     SServiceConnector* uuu = (SServiceConnector*) connector->handle;
913     EIO_Status status;
914 
915     if (cleanup) {
916         status = uuu->meta.close
917             ? uuu->meta.close(uuu->meta.c_close, timeout)
918             : eIO_Success;
919         if (uuu->extra.reset)
920             uuu->extra.reset(uuu->extra.data);
921         s_CloseDispatcher(uuu);
922         s_Cleanup(uuu);
923     } else
924         status = eIO_Success/*unused*/;
925 
926     if (uuu->meta.list) {
927         SMetaConnector* meta = connector->meta;
928         METACONN_Remove(meta, uuu->meta.list);
929         uuu->meta.list = 0;
930         s_Reset(meta, connector);
931     }
932 
933     return status;
934 }
935 
936 
s_VT_GetType(CONNECTOR connector)937 static const char* s_VT_GetType(CONNECTOR connector)
938 {
939     SServiceConnector* uuu = (SServiceConnector*) connector->handle;
940     return uuu->type ? uuu->type : uuu->service;
941 }
942 
943 
s_VT_Descr(CONNECTOR connector)944 static char* s_VT_Descr(CONNECTOR connector)
945 {
946     SServiceConnector* uuu = (SServiceConnector*) connector->handle;
947     return uuu->descr  &&  *uuu->descr ? strdup(uuu->descr) : 0;
948 }
949 
950 
s_VT_Open(CONNECTOR connector,const STimeout * timeout)951 static EIO_Status s_VT_Open(CONNECTOR connector, const STimeout* timeout)
952 {
953     SServiceConnector* uuu = (SServiceConnector*) connector->handle;
954     SMetaConnector* meta = connector->meta;
955     EIO_Status status = eIO_Closed;
956 
957     for (uuu->retry = 0;  uuu->retry < uuu->net_info->max_try;  uuu->retry++) {
958         SConnNetInfo* net_info;
959         SSERV_InfoCPtr info;
960         int stateless;
961         CONNECTOR c;
962 
963         assert(!uuu->meta.list  &&  status != eIO_Success);
964 
965         if (!uuu->iter  &&  !s_OpenDispatcher(uuu))
966             break;
967 
968         if (!(info = s_GetNextInfo(uuu, 0/*any*/))
969             &&  (!uuu->net_info->firewall
970                  ||  strcasecmp(SERV_MapperName(uuu->iter), "local") == 0)) {
971             break;
972         }
973         if (uuu->type) {
974             free((void*) uuu->type);
975             uuu->type = 0;
976         }
977         if (uuu->descr) {
978             free((void*) uuu->descr);
979             uuu->descr = 0;
980         }
981         if (!(net_info = ConnNetInfo_Clone(uuu->net_info))) {
982             status = eIO_Unknown;
983             break;
984         }
985 
986         c = s_Open(uuu, timeout, info, net_info, &status);
987         stateless = net_info->stateless;
988 
989         ConnNetInfo_Destroy(net_info);
990 
991         if (!c) {
992             if (status == eIO_Success)
993                 status  = eIO_Closed;
994             continue;
995         }
996 
997         /* Setup the new connector on a temporary meta-connector... */
998         memset(&uuu->meta, 0, sizeof(uuu->meta));
999         if ((status = METACONN_Insert(&uuu->meta, c)) != eIO_Success) {
1000             x_DestroyConnector(c);
1001             continue;
1002         }
1003         /* ...then link it in using current connection's meta */
1004         assert(c->meta == &uuu->meta);
1005         c->next = meta->list;
1006         meta->list = c;
1007 
1008         if (!uuu->descr  &&  uuu->meta.descr)
1009             CONN_SET_METHOD(meta, descr,  uuu->meta.descr, uuu->meta.c_descr);
1010         CONN_SET_METHOD    (meta, wait,   uuu->meta.wait,  uuu->meta.c_wait);
1011         CONN_SET_METHOD    (meta, write,  uuu->meta.write, uuu->meta.c_write);
1012         CONN_SET_METHOD    (meta, flush,  uuu->meta.flush, uuu->meta.c_flush);
1013         CONN_SET_METHOD    (meta, read,   uuu->meta.read,  uuu->meta.c_read);
1014         CONN_SET_METHOD    (meta, status, uuu->meta.status,uuu->meta.c_status);
1015 
1016         if (uuu->meta.get_type) {
1017             const char* temp;
1018             if ((temp = uuu->meta.get_type(uuu->meta.c_get_type)) != 0) {
1019                 size_t slen = strlen(uuu->service);
1020                 size_t tlen = strlen(temp);
1021                 char*  type = (char*) malloc(slen + tlen + 2);
1022                 if (type) {
1023                     memcpy(type,        uuu->service, slen);
1024                     type[slen++]      = '/';
1025                     memcpy(type + slen, temp,         tlen);
1026                     type[slen + tlen] = '\0';
1027                     uuu->type = type;
1028                 }
1029             }
1030         }
1031 
1032         if (!uuu->meta.open) {
1033             s_Close(connector, timeout, 0/*retain*/);
1034             status = eIO_NotSupported;
1035             continue;
1036         }
1037 
1038         status = uuu->meta.open(uuu->meta.c_open, timeout);
1039         if (status == eIO_Success)
1040             break;
1041 
1042         if (!stateless  &&  (!info  ||  info->type == fSERV_Firewall)) {
1043             static const char kFWDLink[] = CONN_FWD_LINK;
1044             CORE_LOGF_X(6, eLOG_Error,
1045                         ("[%s]  %s connection failure (%s) usually"
1046                          " indicates possible firewall configuration"
1047                          " problems; please consult <%s>", uuu->service,
1048                          !info ? "Firewall" : "Stateful relay",
1049                          IO_StatusStr(status), kFWDLink));
1050         }
1051 
1052         s_Close(connector, timeout, 0/*retain*/);
1053     }
1054 
1055     uuu->status = status;
1056     return status;
1057 }
1058 
1059 
1060 /*ARGSUSED*/
s_VT_Status(CONNECTOR connector,EIO_Event unused)1061 static EIO_Status s_VT_Status(CONNECTOR connector, EIO_Event unused)
1062 {
1063     return ((SServiceConnector*) connector->handle)->status;
1064 }
1065 
1066 
s_VT_Close(CONNECTOR connector,const STimeout * timeout)1067 static EIO_Status s_VT_Close(CONNECTOR connector, const STimeout* timeout)
1068 {
1069     return s_Close(connector, timeout, 1/*cleanup*/);
1070 }
1071 
1072 
s_Setup(CONNECTOR connector)1073 static void s_Setup(CONNECTOR connector)
1074 {
1075     SServiceConnector* uuu = (SServiceConnector*) connector->handle;
1076     SMetaConnector* meta = connector->meta;
1077 
1078     /* initialize virtual table */
1079     CONN_SET_METHOD(meta, get_type, s_VT_GetType, connector);
1080     CONN_SET_METHOD(meta, open,     s_VT_Open,    connector);
1081     CONN_SET_METHOD(meta, close,    s_VT_Close,   connector);
1082     CONN_SET_DEFAULT_TIMEOUT(meta, uuu->net_info->timeout);
1083     /* reset everything else */
1084     s_Reset(meta, connector);
1085 }
1086 
1087 
s_Destroy(CONNECTOR connector)1088 static void s_Destroy(CONNECTOR connector)
1089 {
1090     SServiceConnector* uuu = (SServiceConnector*) connector->handle;
1091     connector->handle = 0;
1092 
1093     if (uuu->extra.cleanup)
1094         uuu->extra.cleanup(uuu->extra.data);
1095     s_CloseDispatcher(uuu);
1096     s_Cleanup(uuu);
1097     ConnNetInfo_Destroy(uuu->net_info);
1098     free(uuu);
1099     free(connector);
1100 }
1101 
1102 
1103 /***********************************************************************
1104  *  EXTERNAL -- the connector's "constructor"
1105  ***********************************************************************/
1106 
SERVICE_CreateConnectorEx(const char * service,TSERV_Type types,const SConnNetInfo * net_info,const SSERVICE_Extra * extra)1107 extern CONNECTOR SERVICE_CreateConnectorEx
1108 (const char*           service,
1109  TSERV_Type            types,
1110  const SConnNetInfo*   net_info,
1111  const SSERVICE_Extra* extra)
1112 {
1113     char*              x_service;
1114     CONNECTOR          ccc;
1115     size_t             len;
1116     SServiceConnector* xxx;
1117 
1118     if (!service  ||  !*service  ||  !(x_service = SERV_ServiceName(service)))
1119         return 0;
1120 
1121     if (!(ccc = (SConnector*) malloc(sizeof(SConnector)))) {
1122         free(x_service);
1123         return 0;
1124     }
1125     len = strlen(service);
1126     if (!(xxx = (SServiceConnector*) calloc(1, sizeof(*xxx) + len))) {
1127         free(x_service);
1128         free(ccc);
1129         return 0;
1130     }
1131 
1132     /* initialize connector structures */
1133     ccc->handle   = xxx;
1134     ccc->next     = 0;
1135     ccc->meta     = 0;
1136     ccc->setup    = s_Setup;
1137     ccc->destroy  = s_Destroy;
1138 
1139     xxx->types    = types;
1140     xxx->net_info = (net_info
1141                      ? ConnNetInfo_Clone(net_info)
1142                      : ConnNetInfo_Create(service));
1143 
1144     if (!ConnNetInfo_SetupStandardArgs(xxx->net_info, x_service)) {
1145         free(x_service);
1146         s_Destroy(ccc);
1147         return 0;
1148     }
1149     /* NB: zero'ed block, no need to copy trailing '\0' */
1150     memcpy((char*) xxx->service, service, len);
1151     free(x_service);
1152 
1153     /* now get ready for first probe dispatching */
1154     if ( types & fSERV_Stateless )
1155         xxx->net_info->stateless = 1/*true*/;
1156     if ((types & fSERV_Firewall)  &&  !xxx->net_info->firewall)
1157         xxx->net_info->firewall = eFWMode_Adaptive;
1158     if (xxx->net_info->max_try < 1)
1159         xxx->net_info->max_try = 1;
1160     if (!s_OpenDispatcher(xxx)) {
1161         s_Destroy(ccc);
1162         return 0;
1163     }
1164     assert(xxx->iter);
1165 
1166     /* finally, store all callback extras */
1167     if (extra)
1168         memcpy(&xxx->extra, extra, sizeof(xxx->extra));
1169 
1170     /* done */
1171     return ccc;
1172 }
1173