1 /* $Id: ncbi_service.c,v 6.152 2016/12/27 19:44:11 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  *   Top-level API to resolve NCBI service name to the server meta-address.
30  *
31  */
32 
33 #include "ncbi_ansi_ext.h"
34 #include "ncbi_dispd.h"
35 #include "ncbi_lbsmd.h"
36 #include "ncbi_local.h"
37 #ifdef NCBI_CXX_TOOLKIT
38 #  include "ncbi_lbosp.h"
39 #endif
40 #include "ncbi_priv.h"
41 #include <ctype.h>
42 #include <stdlib.h>
43 #include <time.h>
44 
45 #define NCBI_USE_ERRCODE_X   Connect_Service
46 
47 
48 /*
49  * FIXME FIXME FIXME FIXME FIXME ---->
50  *
51  * NOTE: For fSERV_ReverseDns lookups the following rules apply to "skip"
52  *       contents:  a service would not be selected if there is a same-named
53  *       entry with matching host[:port] is found in the "skip" list, or
54  *       there is a nameless...
55  * NOTE:  Lookup by mask cancels fSERV_ReverseDns mode, if both are used.
56  */
57 
58 
59 #define CONN_SERVICE_NAME  DEF_CONN_REG_SECTION "_" REG_CONN_SERVICE_NAME
60 
61 
62 static ESwitch s_Fast = eOff;
63 
64 
SERV_DoFastOpens(ESwitch on)65 ESwitch SERV_DoFastOpens(ESwitch on)
66 {
67     ESwitch retval = s_Fast;
68     if (on != eDefault)
69         s_Fast = on;
70     return retval;
71 }
72 
73 
x_ServiceName(const char * service,int ismask,unsigned int depth)74 static char* x_ServiceName(const char* service,
75                            int/*bool*/ ismask, unsigned int depth)
76 {
77     char   buf[128];
78     char   srv[128];
79     size_t len;
80     char*  s;
81 
82     if (depth > 7) {
83         assert(service  &&  *service);
84         CORE_LOGF_X(7, eLOG_Error,
85                     ("[%s]  Maximal service name recursion depth reached: %u",
86                      service, depth));
87         return 0/*failure*/;
88     }
89     len = 0;
90     assert(sizeof(buf) > sizeof(CONN_SERVICE_NAME));
91     if (!service  ||  (!ismask  &&  (!*service  ||  strpbrk(service, "?*")))
92         ||  (len = strlen(service)) >= sizeof(buf)-sizeof(CONN_SERVICE_NAME)) {
93         CORE_LOGF_X(8, eLOG_Error,
94                     ("%s%s%s%s service name",
95                      !service  ||  !*service ? "" : "[",
96                      !service ? "" : service,
97                      !service  ||  !*service ? "" : "]  ",
98                      !service ? "NULL" : !*service ? "Empty" :
99                      len < sizeof(buf)-sizeof(CONN_SERVICE_NAME) ? "Invalid" :
100                      "Too long"));
101         return 0/*failure*/;
102     }
103     if (!s_Fast  &&  !ismask) {
104         s = (char*) memcpy(buf, service, len) + len;
105         *s++ = '_';
106         memcpy(s, CONN_SERVICE_NAME, sizeof(CONN_SERVICE_NAME));
107         /* Looking for "service_CONN_SERVICE_NAME" in the environment */
108         if (!(s = getenv(strupr(buf)))  ||  !*s) {
109             /* Looking for "CONN_SERVICE_NAME" in registry section [service] */
110             buf[len++] = '\0';
111             CORE_REG_GET(buf, buf + len, srv, sizeof(srv), 0);
112             s = srv;
113         }
114         if (*s  &&  strcasecmp(s, service) != 0)
115             return x_ServiceName(s, ismask, depth + 1);
116     }
117     return strdup(service);
118 }
119 
120 
s_ServiceName(const char * service,int ismask)121 static char* s_ServiceName(const char* service, int/*bool*/ ismask)
122 {
123     char* retval;
124     CORE_LOCK_READ;
125     retval = x_ServiceName(service, ismask, 0);
126     CORE_UNLOCK;
127     return retval;
128 }
129 
130 
SERV_ServiceName(const char * service)131 char* SERV_ServiceName(const char* service)
132 {
133     return s_ServiceName(service, 0);
134 }
135 
136 
s_AddSkipInfo(SERV_ITER iter,const char * name,SSERV_InfoCPtr info)137 static int/*bool*/ s_AddSkipInfo(SERV_ITER      iter,
138                                  const char*    name,
139                                  SSERV_InfoCPtr info)
140 {
141     size_t n;
142     assert(name);
143     for (n = 0;  n < iter->n_skip;  n++) {
144         if (strcasecmp(name, SERV_NameOfInfo(iter->skip[n])) == 0
145             &&  (SERV_EqualInfo(info, iter->skip[n])  ||
146                  (iter->skip[n]->type == fSERV_Firewall  &&
147                   iter->skip[n]->u.firewall.type == info->u.firewall.type))) {
148             /* Replace older version */
149             if (iter->last == iter->skip[n])
150                 iter->last  = info;
151             free((void*) iter->skip[n]);
152             iter->skip[n] = info;
153             return 1;
154         }
155     }
156     if (iter->n_skip == iter->a_skip) {
157         SSERV_InfoCPtr* temp;
158         n = iter->a_skip + 10;
159         temp = (SSERV_InfoCPtr*)
160             (iter->skip
161              ? realloc((void*) iter->skip, n * sizeof(*temp))
162              : malloc (                    n * sizeof(*temp)));
163         if (!temp)
164             return 0;
165         iter->skip = temp;
166         iter->a_skip = n;
167     }
168     iter->skip[iter->n_skip++] = info;
169     return 1;
170 }
171 
172 
173 #ifdef __GNUC__
174 inline
175 #endif /*__GNUC__*/
s_IsMapperConfigured(const char * service,const char * key)176 static int/*bool*/ s_IsMapperConfigured(const char* service, const char* key)
177 {
178     char val[32];
179     if (s_Fast)
180         return 0;
181     ConnNetInfo_GetValue(service, key, val, sizeof(val), 0);
182     return ConnNetInfo_Boolean(val);
183 }
184 
185 
x_Open(const char * service,unsigned ismask,TSERV_Type types,unsigned int preferred_host,unsigned short preferred_port,double preference,const SConnNetInfo * net_info,SSERV_InfoCPtr skip[],size_t n_skip,unsigned external,const char * arg,const char * val,SSERV_Info ** info,HOST_INFO * host_info)186 static SERV_ITER x_Open(const char*         service,
187                         unsigned/*bool*/    ismask,
188                         TSERV_Type          types,
189                         unsigned int        preferred_host,
190                         unsigned short      preferred_port,
191                         double              preference,
192                         const SConnNetInfo* net_info,
193                         SSERV_InfoCPtr      skip[],
194                         size_t              n_skip,
195                         unsigned/*bool*/    external,
196                         const char*         arg,
197                         const char*         val,
198                         SSERV_Info**        info,
199                         HOST_INFO*          host_info)
200 {
201     int/*bool*/
202         do_lbsmd = -1/*unassigned*/,
203 #ifdef NCBI_CXX_TOOLKIT
204         do_lbos  = -1/*unassigned*/,
205 #endif /*NCBI_CXX_TOOLKIT*/
206         do_dispd = -1/*unassigned*/;
207     const SSERV_VTable* op;
208     SERV_ITER iter;
209     const char* s;
210 
211     if (!(s = s_ServiceName(service, ismask)))
212         return 0;
213     if (!(iter = (SERV_ITER) calloc(1, sizeof(*iter)))) {
214         free((void*) s);
215         return 0;
216     }
217     assert(ismask  ||  *s);
218 
219     iter->name              = s;
220     iter->host              = (preferred_host == SERV_LOCALHOST
221                                ? SOCK_GetLocalHostAddress(eDefault)
222                                : preferred_host);
223     iter->port              = preferred_port;
224     iter->pref              = (preference < 0.0
225                                ? -1.0
226                                :  0.01 * (preference > 100.0
227                                           ? 100.0
228                                           : preference));
229     iter->types             = types;
230     if (ismask)
231         iter->ismask        = 1;
232     if (types & fSERV_IncludeDown)
233         iter->ok_down       = 1;
234     if (types & fSERV_IncludeReserved)
235         iter->ok_reserved   = 1;
236     if (types & fSERV_IncludeSuppressed)
237         iter->ok_suppressed = 1;
238     if (types & fSERV_ReverseDns)
239         iter->reverse_dns   = 1;
240     if (types & fSERV_Stateless)
241         iter->stateless     = 1;
242     iter->external          = external ? 1 : 0;
243     if (arg  &&  *arg) {
244         iter->arg           = arg;
245         iter->arglen        = strlen(arg);
246         if (val) {
247             iter->val       = val;
248             iter->vallen    = strlen(val);
249         }
250     }
251     iter->time              = (TNCBI_Time) time(0);
252 
253     if (n_skip) {
254         size_t i;
255         for (i = 0;  i < n_skip;  i++) {
256             const char* name = (iter->ismask  ||  skip[i]->type == fSERV_Dns
257                                 ? SERV_NameOfInfo(skip[i]) : "");
258             SSERV_Info* temp = SERV_CopyInfoEx(skip[i],
259                                                !iter->reverse_dns  ||  *name ?
260                                                name : s);
261             if (temp) {
262                 temp->time = NCBI_TIME_INFINITE;
263                 if (!s_AddSkipInfo(iter, name, temp)) {
264                     free(temp);
265                     temp = 0;
266                 }
267             }
268             if (!temp) {
269                 SERV_Close(iter);
270                 return 0;
271             }
272         }
273     }
274     assert(n_skip == iter->n_skip);
275     iter->o_skip = iter->n_skip;
276 
277     if (net_info) {
278         if (net_info->external)
279             iter->external = 1;
280         if (net_info->firewall)
281             iter->types |= fSERV_Firewall;
282         if (net_info->stateless)
283             iter->stateless = 1;
284         if (net_info->lb_disable)
285             do_lbsmd = 0/*false*/;
286     } else
287         do_dispd = 0/*false*/;
288     /* Ugly optimization not to access the registry more than necessary */
289     if ((!s_IsMapperConfigured(service, REG_CONN_LOCAL_ENABLE)               ||
290          !(op = SERV_LOCAL_Open(iter, info, host_info)))
291 
292         &&
293         (!do_lbsmd                                                           ||
294          !(do_lbsmd = !s_IsMapperConfigured
295            (service, REG_CONN_LBSMD_DISABLE))                                ||
296          !(op = SERV_LBSMD_Open(iter, info, host_info,
297                                 (!do_dispd                                   ||
298                                  !(do_dispd = !s_IsMapperConfigured
299                                    (service, REG_CONN_DISPD_DISABLE)))
300 #ifdef NCBI_CXX_TOOLKIT
301                                 &&
302                                 !(do_lbos = s_IsMapperConfigured
303                                   (service, REG_CONN_LBOS_ENABLE))
304 #endif /*NCBI_CXX_TOOLKIT*/
305                                 )))
306 
307 #ifdef NCBI_CXX_TOOLKIT
308         &&
309         (!do_lbos                                                            ||
310          (do_lbos < 0  &&  !(do_lbos = s_IsMapperConfigured
311                              (service, REG_CONN_LBOS_ENABLE)))               ||
312          !(op = SERV_LBOS_Open(iter, net_info, info)))
313 #endif /*NCBI_CXX_TOOLKIT*/
314 
315         &&
316         (!do_dispd                                                           ||
317          (do_dispd < 0  &&  !(do_dispd = !s_IsMapperConfigured
318                               (service, REG_CONN_DISPD_DISABLE)))            ||
319          !(op = SERV_DISPD_Open(iter, net_info, info, host_info)))) {
320         if (!do_lbsmd  &&  !do_dispd) {
321             CORE_LOGF_X(1, eLOG_Error,
322                         ("[%s]  No service mappers available", service));
323         }
324         SERV_Close(iter);
325         return 0;
326     }
327 
328     assert(op != 0);
329     iter->op = op;
330     return iter;
331 }
332 
333 
s_SkipSkip(SERV_ITER iter)334 static void s_SkipSkip(SERV_ITER iter)
335 {
336     size_t n;
337     if (iter->time  &&  (iter->ismask | iter->ok_down | iter->ok_suppressed))
338         return;
339     n = 0;
340     while (n < iter->n_skip) {
341         SSERV_InfoCPtr temp = iter->skip[n];
342         if (temp->time != NCBI_TIME_INFINITE
343             &&  (!iter->time/*iterator reset*/
344                  ||  ((temp->type != fSERV_Dns  ||  temp->host)
345                       &&  temp->time < iter->time))) {
346             if (--iter->n_skip > n) {
347                 SSERV_InfoCPtr* ptr = iter->skip + n;
348                 memmove((void*) ptr, (void*)(ptr + 1),
349                         (iter->n_skip - n) * sizeof(*ptr));
350             }
351             if (iter->last == temp)
352                 iter->last  = 0;
353             free((void*) temp);
354         } else
355             n++;
356     }
357 }
358 
359 
s_GetNextInfo(SERV_ITER iter,HOST_INFO * host_info,int internal)360 static SSERV_Info* s_GetNextInfo(SERV_ITER   iter,
361                                  HOST_INFO*  host_info,
362                                  int/*bool*/ internal)
363 {
364     SSERV_Info* info = 0;
365     assert(iter  &&  iter->op);
366     if (iter->op->GetNextInfo) {
367         if (!internal) {
368             iter->time = (TNCBI_Time) time(0);
369             s_SkipSkip(iter);
370         }
371         /* Obtain a fresh entry from the actual mapper */
372         while ((info = iter->op->GetNextInfo(iter, host_info)) != 0) {
373             /* This should never actually be used for LBSMD dispatcher,
374              * as all exclusion logic is already done in it internally. */
375             int/*bool*/ go =
376                 !info->host  ||  iter->pref >= 0.0  ||
377                 !iter->host  ||  (iter->host == info->host  &&
378                                   (!iter->port  ||  iter->port == info->port));
379             if (go  &&  internal)
380                 break;
381             if (!s_AddSkipInfo(iter, SERV_NameOfInfo(info), info)) {
382                 free(info);
383                 info = 0;
384             }
385             if (go  ||  !info)
386                 break;
387         }
388     }
389     if (!internal)
390         iter->last = info;
391     return info;
392 }
393 
394 
s_Open(const char * service,unsigned ismask,TSERV_Type types,unsigned int preferred_host,unsigned short preferred_port,double preference,const SConnNetInfo * net_info,SSERV_InfoCPtr skip[],size_t n_skip,int external,const char * arg,const char * val,SSERV_Info ** info,HOST_INFO * host_info)395 static SERV_ITER s_Open(const char*         service,
396                         unsigned/*bool*/    ismask,
397                         TSERV_Type          types,
398                         unsigned int        preferred_host,
399                         unsigned short      preferred_port,
400                         double              preference,
401                         const SConnNetInfo* net_info,
402                         SSERV_InfoCPtr      skip[],
403                         size_t              n_skip,
404                         int /*bool*/        external,
405                         const char*         arg,
406                         const char*         val,
407                         SSERV_Info**        info,
408                         HOST_INFO*          host_info)
409 {
410     SSERV_Info* x_info;
411     SERV_ITER iter = x_Open(service, ismask, types,
412                             preferred_host, preferred_port, preference,
413                             net_info, skip, n_skip,
414                             external, arg, val,
415                             &x_info, host_info);
416     assert(!iter  ||  iter->op);
417     if (!iter)
418         x_info = 0;
419     else if (!x_info)
420         x_info = info ? s_GetNextInfo(iter, host_info, 1/*internal*/) : 0;
421     else if (x_info == (SSERV_Info*)(-1L)) {
422         SERV_Close(iter);
423         x_info = 0;
424         iter = 0;
425     }
426     if (info)
427         *info = x_info;
428     else if (x_info)
429         free(x_info);
430     return iter;
431 }
432 
433 
SERV_OpenSimple(const char * service)434 extern SERV_ITER SERV_OpenSimple(const char* service)
435 {
436     SConnNetInfo* net_info = ConnNetInfo_Create(service);
437     SERV_ITER iter = s_Open(service, 0/*not mask*/, fSERV_Any,
438                             SERV_ANYHOST,
439                             0/*preferred_port*/, 0.0/*preference*/,
440                             net_info, 0/*skip*/, 0/*n_skip*/,
441                             0/*not external*/, 0/*arg*/, 0/*val*/,
442                             0/*info*/, 0/*host_info*/);
443     ConnNetInfo_Destroy(net_info);
444     return iter;
445 }
446 
447 
SERV_Open(const char * service,TSERV_Type types,unsigned int preferred_host,const SConnNetInfo * net_info)448 extern SERV_ITER SERV_Open(const char*         service,
449                            TSERV_Type          types,
450                            unsigned int        preferred_host,
451                            const SConnNetInfo* net_info)
452 {
453     return s_Open(service, 0/*not mask*/, types,
454                   preferred_host, 0/*preferred_port*/, 0.0/*preference*/,
455                   net_info, 0/*skip*/, 0/*n_skip*/,
456                   0/*not external*/, 0/*arg*/, 0/*val*/,
457                   0/*info*/, 0/*host_info*/);
458 }
459 
460 
SERV_OpenEx(const char * service,TSERV_Type types,unsigned int preferred_host,const SConnNetInfo * net_info,SSERV_InfoCPtr skip[],size_t n_skip)461 extern SERV_ITER SERV_OpenEx(const char*         service,
462                              TSERV_Type          types,
463                              unsigned int        preferred_host,
464                              const SConnNetInfo* net_info,
465                              SSERV_InfoCPtr      skip[],
466                              size_t              n_skip)
467 {
468     return s_Open(service, 0/*not mask*/, types,
469                   preferred_host, 0/*preferred_port*/, 0.0/*preference*/,
470                   net_info, skip, n_skip,
471                   0/*not external*/, 0/*arg*/, 0/*val*/,
472                   0/*info*/, 0/*host_info*/);
473 }
474 
475 
SERV_OpenP(const char * service,TSERV_Type types,unsigned int preferred_host,unsigned short preferred_port,double preference,const SConnNetInfo * net_info,SSERV_InfoCPtr skip[],size_t n_skip,int external,const char * arg,const char * val)476 SERV_ITER SERV_OpenP(const char*         service,
477                      TSERV_Type          types,
478                      unsigned int        preferred_host,
479                      unsigned short      preferred_port,
480                      double              preference,
481                      const SConnNetInfo* net_info,
482                      SSERV_InfoCPtr      skip[],
483                      size_t              n_skip,
484                      int/*bool*/         external,
485                      const char*         arg,
486                      const char*         val)
487 {
488     return s_Open(service,
489                   service  &&  (!*service  ||  strpbrk(service, "?*")), types,
490                   preferred_host, preferred_port, preference,
491                   net_info, skip, n_skip,
492                   external, arg, val,
493                   0/*info*/, 0/*host_info*/);
494 }
495 
496 
SERV_GetInfoSimple(const char * service)497 extern SSERV_Info* SERV_GetInfoSimple(const char* service)
498 {
499     SConnNetInfo* net_info = ConnNetInfo_Create(service);
500     SSERV_Info* info = SERV_GetInfoP(service, fSERV_Any,
501                                      SERV_ANYHOST/*preferred_host*/,
502                                      0/*preferred_port*/, 0.0/*preference*/,
503                                      net_info, 0/*skip*/, 0/*n_skip*/,
504                                      0/*not external*/, 0/*arg*/, 0/*val*/,
505                                      0/*host_info*/);
506     ConnNetInfo_Destroy(net_info);
507     return info;
508 }
509 
510 
SERV_GetInfo(const char * service,TSERV_Type types,unsigned int preferred_host,const SConnNetInfo * net_info)511 extern SSERV_Info* SERV_GetInfo(const char*         service,
512                                 TSERV_Type          types,
513                                 unsigned int        preferred_host,
514                                 const SConnNetInfo* net_info)
515 {
516     return SERV_GetInfoP(service, types,
517                          preferred_host,
518                          0/*preferred_port*/, 0.0/*preference*/,
519                          net_info, 0/*skip*/, 0/*n_skip*/,
520                          0/*not external*/, 0/*arg*/, 0/*val*/,
521                          0/*host_info*/);
522 }
523 
524 
SERV_GetInfoEx(const char * service,TSERV_Type types,unsigned int preferred_host,const SConnNetInfo * net_info,SSERV_InfoCPtr skip[],size_t n_skip,HOST_INFO * host_info)525 extern SSERV_Info* SERV_GetInfoEx(const char*         service,
526                                   TSERV_Type          types,
527                                   unsigned int        preferred_host,
528                                   const SConnNetInfo* net_info,
529                                   SSERV_InfoCPtr      skip[],
530                                   size_t              n_skip,
531                                   HOST_INFO*          host_info)
532 {
533     return SERV_GetInfoP(service, types,
534                          preferred_host,
535                          0/*preferred_port*/, 0.0/*preference*/,
536                          net_info, skip, n_skip,
537                          0/*not external*/, 0/*arg*/, 0/*val*/,
538                          host_info);
539 }
540 
541 
SERV_GetInfoP(const char * service,TSERV_Type types,unsigned int preferred_host,unsigned short preferred_port,double preference,const SConnNetInfo * net_info,SSERV_InfoCPtr skip[],size_t n_skip,int external,const char * arg,const char * val,HOST_INFO * host_info)542 SSERV_Info* SERV_GetInfoP(const char*         service,
543                           TSERV_Type          types,
544                           unsigned int        preferred_host,
545                           unsigned short      preferred_port,
546                           double              preference,
547                           const SConnNetInfo* net_info,
548                           SSERV_InfoCPtr      skip[],
549                           size_t              n_skip,
550                           int/*bool*/         external,
551                           const char*         arg,
552                           const char*         val,
553                           HOST_INFO*          host_info)
554 {
555     SSERV_Info* info;
556     SERV_ITER iter = s_Open(service, 0/*not mask*/, types,
557                             preferred_host, preferred_port, preference,
558                             net_info, skip, n_skip,
559                             external, arg, val,
560                             &info, host_info);
561     assert(!info  ||  iter);
562     SERV_Close(iter);
563     return info;
564 }
565 
566 
SERV_GetNextInfoEx(SERV_ITER iter,HOST_INFO * host_info)567 extern SSERV_InfoCPtr SERV_GetNextInfoEx(SERV_ITER  iter,
568                                          HOST_INFO* host_info)
569 {
570     assert(!iter  ||  iter->op);
571     return iter ? s_GetNextInfo(iter, host_info, 0) : 0;
572 }
573 
574 
SERV_GetNextInfo(SERV_ITER iter)575 extern SSERV_InfoCPtr SERV_GetNextInfo(SERV_ITER iter)
576 {
577     assert(!iter  ||  iter->op);
578     return iter ? s_GetNextInfo(iter, 0,         0) : 0;
579 }
580 
581 
SERV_MapperName(SERV_ITER iter)582 const char* SERV_MapperName(SERV_ITER iter)
583 {
584     assert(!iter  ||  iter->op);
585     return iter ? iter->op->mapper : 0;
586 }
587 
588 
SERV_CurrentName(SERV_ITER iter)589 const char* SERV_CurrentName(SERV_ITER iter)
590 {
591     const char* name = SERV_NameOfInfo(iter->last);
592     return name  &&  *name ? name : iter->name;
593 }
594 
595 
SERV_PenalizeEx(SERV_ITER iter,double fine,TNCBI_Time time)596 int/*bool*/ SERV_PenalizeEx(SERV_ITER iter, double fine, TNCBI_Time time)
597 {
598     assert(!iter  ||  iter->op);
599     if (!iter  ||  !iter->op->Feedback  ||  !iter->last)
600         return 0/*false*/;
601     return iter->op->Feedback(iter, fine, time ? time : 1/*NB: always != 0*/);
602 }
603 
604 
SERV_Penalize(SERV_ITER iter,double fine)605 extern int/*bool*/ SERV_Penalize(SERV_ITER iter, double fine)
606 {
607     return SERV_PenalizeEx(iter, fine, 0);
608 }
609 
610 
SERV_Rerate(SERV_ITER iter,double rate)611 extern int/*bool*/ SERV_Rerate(SERV_ITER iter, double rate)
612 {
613     assert(!iter  ||  iter->op);
614     if (!iter  ||  !iter->op->Feedback  ||  !iter->last)
615         return 0/*false*/;
616     return iter->op->Feedback(iter, rate, 0/*i.e.rate*/);
617 }
618 
619 
SERV_Reset(SERV_ITER iter)620 extern void SERV_Reset(SERV_ITER iter)
621 {
622     if (!iter)
623         return;
624     iter->last  = 0;
625     iter->time  = 0;
626     s_SkipSkip(iter);
627     if (iter->op  &&  iter->op->Reset)
628         iter->op->Reset(iter);
629 }
630 
631 
SERV_Close(SERV_ITER iter)632 extern void SERV_Close(SERV_ITER iter)
633 {
634     size_t i;
635     if (!iter)
636         return;
637     SERV_Reset(iter);
638     for (i = 0;  i < iter->n_skip;  i++)
639         free((void*) iter->skip[i]);
640     iter->n_skip = 0;
641     if (iter->op) {
642         if (iter->op->Close)
643             iter->op->Close(iter);
644         iter->op = 0;
645     }
646     if (iter->skip)
647         free((void*) iter->skip);
648     free((void*) iter->name);
649     free(iter);
650 }
651 
652 
SERV_Update(SERV_ITER iter,const char * text,int code)653 int/*bool*/ SERV_Update(SERV_ITER iter, const char* text, int code)
654 {
655     static const char used_server_info[] = "Used-Server-Info-";
656     int retval = 0/*not updated yet*/;
657 
658     assert(!iter  ||  iter->op);
659     if (iter  &&  text) {
660         const char *c, *b;
661         iter->time = (TNCBI_Time) time(0);
662         for (b = text;  (c = strchr(b, '\n')) != 0;  b = c + 1) {
663             size_t len = (size_t)(c - b);
664             SSERV_Info* info;
665             unsigned int d1;
666             char* p, *t;
667             int d2;
668 
669             if (!(t = (char*) malloc(len + 1)))
670                 continue;
671             memcpy(t, b, len);
672             if (t[len - 1] == '\r')
673                 t[len - 1]  = '\0';
674             else
675                 t[len    ]  = '\0';
676             p = t;
677             if (iter->op->Update  &&  iter->op->Update(iter, p, code))
678                 retval = 1/*updated*/;
679             if (!strncasecmp(p, used_server_info, sizeof(used_server_info) - 1)
680                 &&  isdigit((unsigned char) p[sizeof(used_server_info) - 1])) {
681                 p += sizeof(used_server_info) - 1;
682                 if (sscanf(p, "%u: %n", &d1, &d2) >= 1
683                     &&  (info = SERV_ReadInfoEx(p + d2, "", 0)) != 0) {
684                     if (!s_AddSkipInfo(iter, "", info))
685                         free(info);
686                     else
687                         retval = 1/*updated*/;
688                 }
689             }
690             free(t);
691         }
692     }
693     return retval;
694 }
695 
696 
s_SetDefaultReferer(SConnNetInfo * net_info,SERV_ITER iter)697 static void s_SetDefaultReferer(SConnNetInfo* net_info, SERV_ITER iter)
698 {
699     char* str, *referer = 0;
700 
701     if (strcasecmp(iter->op->mapper, "DISPD") == 0)
702         referer = ConnNetInfo_URL(net_info);
703     else if ((str = strdup(iter->op->mapper)) != 0) {
704         const char* host = net_info->client_host;
705         const char* args = net_info->args;
706         const char* name = iter->name;
707 
708         if (!*net_info->client_host
709             &&  !SOCK_gethostbyaddr(0, net_info->client_host,
710                                     sizeof(net_info->client_host))) {
711             SOCK_gethostname(net_info->client_host,
712                              sizeof(net_info->client_host));
713         }
714         if (!(referer = (char*) malloc(3 + 1 + 1 + 1 + 2*strlen(strlwr(str)) +
715                                        strlen(host) + (args[0]
716                                                        ? strlen(args)
717                                                        : 8 + strlen(name))))) {
718             return;
719         }
720         strcat(strcat(strcat(strcat(strcpy
721                                     (referer, str), "://"), host), "/"), str);
722         if (args[0])
723             strcat(strcat(referer, "?"),         args);
724         else
725             strcat(strcat(referer, "?service="), name);
726         free(str);
727     }
728     assert(!net_info->http_referer);
729     net_info->http_referer = referer;
730 }
731 
732 
SERV_Print(SERV_ITER iter,SConnNetInfo * net_info,int but_last)733 char* SERV_Print(SERV_ITER iter, SConnNetInfo* net_info, int/*bool*/ but_last)
734 {
735     static const char kAcceptedServerTypes[] = "Accepted-Server-Types:";
736     static const char kClientRevision[] = "Client-Revision: %u.%u\r\n";
737     static const char kUsedServerInfo[] = "Used-Server-Info: ";
738     static const char kNcbiExternal[] = NCBI_EXTERNAL ": Y\r\n";
739     static const char kNcbiFWPorts[] = "NCBI-Firewall-Ports: ";
740     static const char kPreference[] = "Preference: ";
741     static const char kSkipInfo[] = "Skip-Info-%u: ";
742     static const char kAffinity[] = "Affinity: ";
743     char buffer[128], *str;
744     TBSERV_TypeOnly t;
745     size_t buflen, i;
746     BUF buf = 0;
747 
748     /* Put client version number */
749     buflen = sprintf(buffer, kClientRevision,
750                      SERV_CLIENT_REVISION_MAJOR, SERV_CLIENT_REVISION_MINOR);
751     assert(buflen < sizeof(buffer));
752     if (!BUF_Write(&buf, buffer, buflen)) {
753         BUF_Destroy(buf);
754         return 0;
755     }
756     if (iter) {
757         assert(iter->op);
758         if (net_info  &&  !net_info->http_referer  &&  iter->op->mapper)
759             s_SetDefaultReferer(net_info, iter);
760         /* Accepted server types */
761         buflen = sizeof(kAcceptedServerTypes) - 1;
762         memcpy(buffer, kAcceptedServerTypes, buflen);
763         for (t = 1;  t;  t <<= 1) {
764             if (iter->types & t) {
765                 const char* name = SERV_TypeStr((ESERV_Type) t);
766                 size_t namelen = strlen(name);
767                 if (!namelen  ||  buflen + 1 + namelen + 2 >= sizeof(buffer))
768                     break;
769                 buffer[buflen++] = ' ';
770                 memcpy(buffer + buflen, name, namelen);
771                 buflen += namelen;
772             } else if (iter->types < t)
773                 break;
774         }
775         if (buffer[buflen - 1] != ':') {
776             strcpy(&buffer[buflen], "\r\n");
777             assert(strlen(buffer) == buflen+2  &&  buflen+2 < sizeof(buffer));
778             if (!BUF_Write(&buf, buffer, buflen + 2)) {
779                 BUF_Destroy(buf);
780                 return 0;
781             }
782         }
783         if (iter->external) {
784             /* External */
785             if (!BUF_Write(&buf, kNcbiExternal, sizeof(kNcbiExternal)-1)) {
786                 BUF_Destroy(buf);
787                 return 0;
788             }
789         }
790         if (iter->types & fSERV_Firewall) {
791             /* Firewall */
792             EFWMode mode
793                 = net_info ? (EFWMode) net_info->firewall : eFWMode_Legacy;
794             SERV_PrintFirewallPorts(buffer, sizeof(buffer), mode);
795             if (*buffer
796                 &&  (!BUF_Write(&buf, kNcbiFWPorts, sizeof(kNcbiFWPorts)-1)  ||
797                      !BUF_Write(&buf, buffer, strlen(buffer))                ||
798                      !BUF_Write(&buf, "\r\n", 2))) {
799                 BUF_Destroy(buf);
800                 return 0;
801             }
802         }
803         if (iter->pref  &&  (iter->host | iter->port)) {
804             /* Preference */
805             buflen = SOCK_HostPortToString(iter->host, iter->port,
806                                            buffer, sizeof(buffer));
807             buffer[buflen++] = ' ';
808             buflen = (size_t)(strcpy(NCBI_simple_ftoa(buffer + buflen,
809                                                       iter->pref * 100.0, 2),
810                                      "\r\n") - buffer) + 2/*"\r\n"*/;
811             if (!BUF_Write(&buf, kPreference, sizeof(kPreference) - 1)  ||
812                 !BUF_Write(&buf, buffer, buflen)) {
813                 BUF_Destroy(buf);
814                 return 0;
815             }
816         }
817         if (iter->arglen) {
818             /* Affinity */
819             if (!BUF_Write(&buf, kAffinity, sizeof(kAffinity) - 1)           ||
820                 !BUF_Write(&buf, iter->arg, iter->arglen)                    ||
821                 (iter->val  &&  (!BUF_Write(&buf, "=", 1)                    ||
822                                  !BUF_Write(&buf, iter->val, iter->vallen))) ||
823                 !BUF_Write(&buf, "\r\n", 2)) {
824                 BUF_Destroy(buf);
825                 return 0;
826             }
827         }
828         /* Drop any outdated skip entries */
829         iter->time = (TNCBI_Time) time(0);
830         s_SkipSkip(iter);
831         /* Put all the rest into rejection list */
832         for (i = 0;  i < iter->n_skip;  ++i) {
833             /* NB: all skip infos are now kept with names (perhaps, empty) */
834             const char* name    = SERV_NameOfInfo(iter->skip[i]);
835             size_t      namelen = name  &&  *name ? strlen(name) : 0;
836             if (!(str = SERV_WriteInfo(iter->skip[i])))
837                 break;
838             if (but_last  &&  iter->last == iter->skip[i]) {
839                 buflen = sizeof(kUsedServerInfo) - 1;
840                 memcpy(buffer, kUsedServerInfo, buflen);
841             } else
842                 buflen = sprintf(buffer, kSkipInfo, (unsigned) i + 1);
843             assert(buflen < sizeof(buffer) - 1);
844             if (!BUF_Write(&buf, buffer, buflen)                ||
845                 (namelen  &&  !BUF_Write(&buf, name, namelen))  ||
846                 (namelen  &&  !BUF_Write(&buf, " ", 1))         ||
847                 !BUF_Write(&buf, str, strlen(str))              ||
848                 !BUF_Write(&buf, "\r\n", 2)) {
849                 free(str);
850                 break;
851             }
852             free(str);
853         }
854         if (i < iter->n_skip) {
855             BUF_Destroy(buf);
856             return 0;
857         }
858     }
859     /* Ok then, we have filled the entire header, <CR><LF> terminated */
860     if ((buflen = BUF_Size(buf)) != 0) {
861         if ((str = (char*) malloc(buflen + 1)) != 0) {
862             if (BUF_Read(buf, str, buflen) != buflen) {
863                 free(str);
864                 str = 0;
865             } else
866                 str[buflen] = '\0';
867         }
868     } else
869         str = 0;
870     BUF_Destroy(buf);
871     return str;
872 }
873 
874 
SERV_ServerPort(const char * name,unsigned int host)875 extern unsigned short SERV_ServerPort(const char*  name,
876                                       unsigned int host)
877 {
878     SSERV_Info*    info;
879     unsigned short port;
880 
881     /* FIXME:  SERV_LOCALHOST may not need to be resolved here,
882      *         but taken from LBSMD table (or resolved later in DISPD/LOCAL
883      *         if needed).
884      */
885     if (!host  ||  host == SERV_LOCALHOST)
886         host = SOCK_GetLocalHostAddress(eDefault);
887     if (!(info = SERV_GetInfoP(name, fSERV_Standalone | fSERV_Promiscuous,
888                                host, 0/*pref. port*/, -1.0/*latch host*/,
889                                0/*net_info*/, 0/*skip*/, 0/*n_skip*/,
890                                0/*not external*/, 0/*arg*/, 0/*val*/,
891                                0/*host_info*/))) {
892         return 0;
893     }
894     assert(info->host == host);
895     port = info->port;
896     free((void*) info);
897     assert(port);
898     return port;
899 }
900 
901 
902 #if 0
903 int/*bool*/ SERV_MatchesHost(const SSERV_Info* info, unsigned int host)
904 {
905     if (host == SERV_ANYHOST)
906         return 1/*true*/;
907     if (host != SERV_LOCALHOST)
908         return info->host == host ? 1/*true*/ : 0/*false*/;
909     if (!info->host  ||  info->host == SOCK_GetLocalHostAddress(eDefault))
910         return 1/*true*/;
911     return 0/*false*/;
912 }
913 #endif
914