1 /*
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  * Authors:  Dmitriy Elisov
27  * Credits:  Denis Vakatov
28  * @file
29  * File Description:
30  *   A client for service discovery API based on LBOS.
31  *   LBOS is a client for ZooKeeper cloud-based DB.
32  *   LBOS allows to announce, deannounce and resolve services.
33  */
34 
35 
36 #include "ncbi_ansi_ext.h"
37 #include <connect/ncbi_http_connector.h>
38 #include "ncbi_lbosp.h"
39 #include "ncbi_priv.h"
40 #include <stdlib.h> /* free, realloc, calloc, malloc */
41 #include <ctype.h> /* isdigit */
42 #include "parson.h"
43 #define            kHostportStringLength     (16+1+5)/**<
44                                                      strlen("255.255.255.255")+
45                                                      strlen(":") +
46                                                      strlen("65535")         */
47 #define            kMaxLineSize              1024  /**< used to build strings*/
48 
49 
50 #define            NCBI_USE_ERRCODE_X        Connect_LBSM /**< Used in
51                                                                CORE_LOG*_X  */
52 
53 #ifdef NCBI_COMPILER_MSVC
54 #   define LBOS_STRTOK strtok_s
55 #else
56 #   define LBOS_STRTOK strtok_r
57 #endif
58 /*/////////////////////////////////////////////////////////////////////////////
59 //                         STATIC VARIABLES                                  //
60 /////////////////////////////////////////////////////////////////////////////*/
61 
62 #ifdef NCBI_OS_MSWIN
63     static const char* kLbosresolverFile      = "C:\\Apps\\Admin_Installs\\etc"
64                                                 "\\ncbi\\lbosresolver";
65 #else
66     static const char* kLbosresolverFile      = "/etc/ncbi/lbosresolver";
67 #endif
68 
69 static const char* kLBOSQuery                 = "/lbos/v3/services/"
70                                                 "?format=json&show=all&q=";
71 
72 /*
73  * LBOS registry section for announcement
74  */
75 static const char* kLBOSAnnouncementSection    = "LBOS_ANNOUNCEMENT";
76 static const char* kLBOSServiceVariable        = "SERVICE";
77 static const char* kLBOSVersionVariable        = "VERSION";
78 static const char* kLBOSServerHostVariable     = "HOST";
79 static const char* kLBOSPortVariable           = "PORT";
80 static const char* kLBOSHealthcheckUrlVariable = "HEALTHCHECK";
81 static const char* kLBOSMetaVariable           = "meta";
82 
83 
84 static SConnNetInfo* s_EmptyNetInfo         = NULL; /* Do not change..       */
85 static       char*   s_LBOS_Lbosresolver    = NULL; /*          ..after init */
86 static const int     kInitialCandidatesCount = 1;   /* For initial memory
87                                                        allocation            */
88 static       int     s_LBOS_TurnedOn         = 1;   /* If LBOS cannot
89                                                        resolve even itself,
90                                                        we turn it off for
91                                                        performance           */
92 static       int     s_LBOS_Init             = 0;   /* If client has already
93                                                        been initialized      */
94 
95 static
96 struct SLBOS_AnnounceHandle_Tag*
97                    s_LBOS_AnnouncedServers      = NULL;
98 static
99 unsigned int       s_LBOS_AnnouncedServersNum   = 0;
100 static
101 unsigned int       s_LBOS_AnnouncedServersAlloc = 0;
102 /*/////////////////////////////////////////////////////////////////////////////
103 //                     STATIC FUNCTION DECLARATIONS                          //
104 /////////////////////////////////////////////////////////////////////////////*/
105 static SSERV_Info**   s_LBOS_ResolveIPPort (const char*   lbos_address,
106                                             const char*   serviceName,
107                                             SConnNetInfo* net_info);
108 static char *         s_LBOS_UrlReadAll    (SConnNetInfo* net_info,
109                                             const char*   url,
110                                             int*          status_code,
111                                             char**        status_message);
112 static void           s_LBOS_FillCandidates(SLBOS_Data*   data,
113                                             const char*   service);
114 static SLBOS_Data*    s_LBOS_ConstructData (size_t        candidatesCapacity);
115 static void           s_LBOS_DestroyData   (SLBOS_Data*   data);
116 static SSERV_Info*    s_LBOS_GetNextInfo   (SERV_ITER     iter,
117                                             HOST_INFO*    host_info);
118 static void           s_LBOS_Initialize    (void);
119 static int/*bool*/    s_LBOS_Feedback      (SERV_ITER     a,
120                                             double        b,
121                                             int           c);
122 static void           s_LBOS_Close         (SERV_ITER     iter);
123 static void           s_LBOS_Reset         (SERV_ITER     iter);
124 static int/*bool*/    s_LBOS_Update        (SERV_ITER     iter,
125                                             const char*   text,
126                                             int           code);
127 #if defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN
128 static const char*    s_LBOS_ReadLbosresolver(void);
129 #endif /* defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN */
130 static
131 EHTTP_HeaderParse     s_LBOS_ParseHeader   (const char*   header,
132                                             void*         /* SLBOS_UserData* */
133                                                           response,
134                                             int           server_error);
135 /*LBOS is intended to answer in 0.5 sec, or is considered dead*/
136 static const STimeout   kLBOSTimeout       =  {10, 500000};
137 static char*            s_LBOS_Instance    =  NULL;
138 static char*            s_LBOS_DTABLocal   =  NULL;
139 
140 static
141 unsigned short s_LBOS_Announce(const char*             service,
142                                const char*             version,
143                                const char*             host,
144                                unsigned short          port,
145                                const char*             healthcheck_url,
146                                const char*             meta,
147                                char**                  lbos_answer,
148                                char**                  http_status_message);
149 
150 
151 /*/////////////////////////////////////////////////////////////////////////////
152 //                     UNIT TESTING VIRTUAL FUNCTION TABLE                   //
153 /////////////////////////////////////////////////////////////////////////////*/
154 static SLBOS_Functions s_LBOS_funcs = {
155                 s_LBOS_ResolveIPPort
156                 ,CONN_Read
157                 ,s_LBOS_FillCandidates
158                 ,s_LBOS_DestroyData
159                 ,s_LBOS_GetNextInfo
160                 ,s_LBOS_Initialize
161                 ,s_LBOS_UrlReadAll
162                 ,s_LBOS_ParseHeader
163                 ,SOCK_GetLocalHostAddress
164                 ,s_LBOS_Announce
165 };
166 
167 
168 /** Get pointer to s_LBOS_MyPort */
169 ///////////////////////////////////////////////////////////////////////////////
170 //                  INTERNAL STORAGE OF ANNOUNCED SERVERS                    //
171 ///////////////////////////////////////////////////////////////////////////////
172 /** Add server to list of announced servers.
173  * @attention
174  *  Intended to be used inside critical section, with caller having
175  *  locked already, so does not lock.  */
s_LBOS_FindAnnouncedServer(const char * service,const char * version,unsigned short port,const char * host)176 static int  s_LBOS_FindAnnouncedServer(const char*            service,
177                                        const char*            version,
178                                        unsigned short         port,
179                                        const char*            host)
180 {
181     /* For convenience, we use some aliases. Not references, because this
182      * is not C++ */
183     struct SLBOS_AnnounceHandle_Tag** arr;
184     unsigned int* count;
185     int retval;
186     unsigned int i = 0;
187 
188     arr = &s_LBOS_AnnouncedServers;
189     if (*arr == NULL)
190         return -1;
191     count = &s_LBOS_AnnouncedServersNum;
192     retval = -1;
193 
194     /* Just iterate and compare */
195     for (i = 0;  i < *count;  i++) {
196         if (strcmp(service, (*arr)[i].service) == 0
197             &&
198             strcmp(version, (*arr)[i].version) == 0
199             &&
200             strcmp(host, (*arr)[i].host) == 0
201             &&
202             (*arr)[i].port == port)
203         {
204             retval = i;
205         }
206     }
207 
208     return retval;
209 }
210 
211 
212 /** Remove server from list of announced servers.
213  * @attention
214  *  Intended to be used inside critical section, with caller having
215  *  locked already, so does not lock.  */
216 static
s_LBOS_RemoveAnnouncedServer(const char * service,const char * version,unsigned short port,const char * host)217 int/*bool*/ s_LBOS_RemoveAnnouncedServer(const char*          service,
218                                          const char*          version,
219                                          unsigned short       port,
220                                          const char*          host)
221 {
222     int return_code;
223     struct SLBOS_AnnounceHandle_Tag** arr;
224     unsigned int* count;
225     int pos;
226 
227 
228     /* If server was announced using local IP, it should be deleted
229      * with IP "0.0.0.0"                                    */
230     if (g_LBOS_StringIsNullOrEmpty(host)) {
231         host = "0.0.0.0"; /* we safely reassign, because caller should care
232                              about memory leaks */
233     }
234     return_code = 1;
235     /* For convenience, we use some aliases. Not references, because this
236      * is not C++ */
237     arr = &s_LBOS_AnnouncedServers;
238     if (*arr == NULL) {
239         return_code = 0;
240         goto unlock_and_return;
241     }
242     count = &s_LBOS_AnnouncedServersNum;
243 
244     /* Find node to delete*/
245     pos = s_LBOS_FindAnnouncedServer(service, version, port, host);
246     if (pos == -1) {/* no such service was announced */
247         return_code = 0;
248         goto unlock_and_return;
249     }
250 
251     /* Erase node data */
252     free((*arr)[pos].version);
253     free((*arr)[pos].service);
254     free((*arr)[pos].host);
255 
256     /* Move all nodes to fill erased position */
257     memmove(*arr + pos,
258             *arr + pos + 1,
259             sizeof(**arr) * (*count - pos -1 ));
260     (*count)--;
261 unlock_and_return:
262     assert(return_code == 0 || return_code == 1);
263     return return_code;
264 }
265 
266 
267 /** Add server to list of announced servers.
268  * @attention
269  *  Intended to be used inside critical section, with caller having
270  *  locked already, so does not lock.  */
271 static
s_LBOS_AddAnnouncedServer(const char * service,const char * version,unsigned short port,const char * healthcheck_url)272 int/*bool*/ s_LBOS_AddAnnouncedServer(const char*            service,
273                                      const char*             version,
274                                      unsigned short          port,
275                                      const char*             healthcheck_url)
276 {
277     int return_code;
278     struct SLBOS_AnnounceHandle_Tag handle;
279     SConnNetInfo * healthcheck_info;
280     /* For convenience, we use some aliases. Not references, because this
281      * is not C++ */
282     struct SLBOS_AnnounceHandle_Tag** arr = &s_LBOS_AnnouncedServers;
283     unsigned int* count = &s_LBOS_AnnouncedServersNum;
284     unsigned int* alloc = &s_LBOS_AnnouncedServersAlloc;
285 
286     /* First we create object without using static variables, and then lock
287      * critical section and copy object to static array. This will allow to
288      * to use most of multithreading */
289     return_code = 1;
290     /* extract host from healthcheck url */
291     healthcheck_info = ConnNetInfo_Clone(s_EmptyNetInfo);
292     healthcheck_info->host[0] = '\0'; /* to be sure that it will be
293                                            * overridden                      */
294     ConnNetInfo_ParseURL(healthcheck_info, healthcheck_url);
295 
296     /* Create new element of list*/
297     handle.host = strdup(healthcheck_info->host);
298     handle.port = port;
299     handle.version = strdup(version);
300     handle.service = strdup(service);
301 
302     /* We search for the same server being already announced                 */
303     s_LBOS_RemoveAnnouncedServer(service, version, port,
304                                  healthcheck_info->host);
305     ConnNetInfo_Destroy(healthcheck_info);
306 
307     /* Allocate more space, if needed */
308     if (*arr == NULL || *count == *alloc)
309     {
310         int new_size = *alloc*2 + 1;
311         struct SLBOS_AnnounceHandle_Tag* realloc_result =
312             (struct SLBOS_AnnounceHandle_Tag*)realloc(*arr,
313                 new_size * sizeof(struct SLBOS_AnnounceHandle_Tag));
314         if (realloc_result != NULL) {
315             *arr = realloc_result;
316             *alloc = new_size;
317         } else {
318             free(handle.version);
319             free(handle.service);
320             free(handle.host);
321             return_code = 0;
322             goto clear_and_exit;
323         }
324     }
325     assert(arr != NULL);
326     /* Copy handle to the end of array*/
327     (*count)++;
328     (*arr)[*count - 1] = handle;
329 
330 clear_and_exit:
331     return return_code;
332 }
333 
334 
335 /*/////////////////////////////////////////////////////////////////////////////
336 //                  SERVICE MAPPING VIRTUAL FUNCTION TABLE                   //
337 /////////////////////////////////////////////////////////////////////////////*/
338 static const SSERV_VTable s_lbos_op =  {
339         s_LBOS_GetNextInfo, /**< Use open()'ed iterator to get next found
340                              *   server*/
341         s_LBOS_Feedback,    /**< Not used                                    */
342         s_LBOS_Update,      /**< Not used                                    */
343         s_LBOS_Reset,       /**< Free iterator's 'data'                      */
344         s_LBOS_Close,       /**< Close iterator and free its 'data'          */
345         "lbos"              /**< name of mapper                              */
346 };
347 
348 
349 /*/////////////////////////////////////////////////////////////////////////////
350 //                    GLOBAL CONVENIENCE FUNCTIONS                           //
351 /////////////////////////////////////////////////////////////////////////////*/
352 /** Check C-string if it is not NULL and contains at least one symbol        */
g_LBOS_StringIsNullOrEmpty(const char * str)353 int/*bool*/ g_LBOS_StringIsNullOrEmpty(const char* str)
354 {
355     if ((str == NULL) || (strlen(str) == 0)) {
356         return 1;
357     }
358     return 0;
359 }
360 
361 
362 /** Case-insensitive string lookup.
363  * @note
364  *  Supposes that the string can be changed! No const modifier for dest
365  * @note
366  *  If dest or lookup is NULL or empty, returns NULL */
g_LBOS_strcasestr(const char * dest,const char * lookup)367 const char* g_LBOS_strcasestr(const char* dest, const char* lookup)
368 {
369     char* dest_lwr;
370     char* lookup_lwr;
371     const char* result;
372 
373     if (g_LBOS_StringIsNullOrEmpty(dest) || g_LBOS_StringIsNullOrEmpty(lookup))
374     {
375         return NULL;
376     }
377     dest_lwr = strlwr(strdup(dest));
378     lookup_lwr = strlwr(strdup(lookup));
379 
380     result = strstr(dest_lwr, lookup_lwr);
381     /* Set the same offset, but in original string */
382     if (result != NULL) {
383         result = dest + (result - dest_lwr);
384     }
385 
386     free(dest_lwr);
387     free(lookup_lwr);
388 
389     return result;
390 }
391 
392 
393 /**  Concatenate two C-strings and assign result to the left
394  *  @param dest[in, out]
395  *   Address of string to which you want to append another string.
396  *   This address will be realloc()'ed and returned, so dest should point to
397  *   heap and can be a result of function like strdup("my string").
398  *   If it is NULL, a new string will be created.
399  *  @param to_append[in]
400  *   String that will be copied to the end of dest.
401  *   It will not be changed. If it is NULL or empty, and dest is not NULL,
402  *   dest will be returned. If dest is NULL, empty string will be returned.
403  *  @param dest_length[in]
404  *   Can be NULL. Optimizes performance on sequence of concatenations like
405  *   g_LBOS_StringConcat(g_LBOS_StringConcat(str,to_app1,&len), to_app2,&len).
406  *   Value of data_length on first call should be 0.
407  *  @return
408  *   reallocated dest on success, NULL if error happened and dest was
409  *   not changed         */
g_LBOS_StringConcat(char * dest,const char * to_append,size_t * dest_length)410 char*   g_LBOS_StringConcat(char*       dest,
411                             const char* to_append,
412                             size_t*     dest_length)
413 {
414     char* realloc_result;
415     size_t dest_length_local = 0; /* not to handle if dest_length is NULL */
416     size_t append_len;
417 
418     if (dest_length != NULL) {
419         dest_length_local = *dest_length;
420     }
421     if (dest == NULL) {
422         dest_length_local = 0;
423     } else if (dest_length_local == 0) {
424         dest_length_local = strlen(dest);
425     }
426     append_len = 0;
427     if (!g_LBOS_StringIsNullOrEmpty(to_append)) {
428         /* All done, in this case */
429         append_len = strlen(to_append);
430     }
431     realloc_result = (char*)realloc(dest, dest_length_local + append_len + 1);
432     if (realloc_result == NULL) {
433         CORE_LOG_X(eLBOS_MemAlloc, eLOG_Critical,
434                     "g_LBOS_StringConcat: No RAM. Returning NULL.");
435         free(dest);
436         return NULL;
437     }
438     dest = realloc_result;
439     memcpy(dest + dest_length_local, to_append, append_len);
440     dest[dest_length_local + append_len] = '\0';
441     dest_length_local += append_len;
442     if (dest_length != NULL) {
443         *dest_length = dest_length_local;
444     }
445     return dest;
446 }
447 
448 
g_LBOS_StringNConcat(char * dest,const char * to_append,size_t * dest_length,size_t count)449 char*   g_LBOS_StringNConcat(char*       dest,
450                              const char* to_append,
451                              size_t*     dest_length,
452                              size_t      count)
453 {
454     char* buf = (char*)malloc((count + 1) * sizeof(char));
455     char* result;
456 
457     if (buf == NULL) {
458         CORE_LOG_X(eLBOS_MemAlloc, eLOG_Critical,
459                  "g_LBOS_StringConcat: No RAM. Returning NULL.");
460         free(buf);
461         free(dest);
462         return NULL;
463     }
464     memcpy(buf, to_append, count);
465     buf[count] = '\0';
466     result = g_LBOS_StringConcat(dest, buf, dest_length);
467     free(buf);
468     return result;
469 }
470 
471 
472 /* Wrapper of CORE_REG_GET that eliminates the need to know the length of the
473  * parameter being read*/
g_LBOS_RegGet(const char * section,const char * name,const char * def_value)474 char*   g_LBOS_RegGet(const char* section,
475                       const char* name,
476                       const char* def_value)
477 {
478     size_t    totalBufSize     = kMaxLineSize;
479     char*     realloc_result   = NULL;
480     char*     buf              = (char*)malloc(totalBufSize * sizeof(char));
481 
482     if (buf == NULL) {
483         CORE_LOG_X(eLBOS_MemAlloc, eLOG_Critical,
484                    "g_LBOS_RegGet: No RAM. Returning NULL.");
485         return buf;
486     }
487     for (;;) {
488         CORE_REG_GET(section, name, buf, totalBufSize, def_value);
489         /* If we had enough space allocated */
490         if (strlen(buf) < totalBufSize-1) {
491             break;
492         }
493         /* If we (possibly) did not have enough space allocated
494            then add space to buffer  */
495         realloc_result = (char*)realloc(buf, sizeof(char)*(totalBufSize * 2));
496         if (realloc_result == NULL) {
497             CORE_LOG_X(eLBOS_MemAlloc, eLOG_Warning,
498                        "g_LBOS_RegGet: Buffer overflow while reading from "
499                        "registry. Returning string at its maximum size");
500             return buf;
501         } else {
502             buf = realloc_result;
503             totalBufSize *= 2;
504         }
505     }
506     return buf;
507 }
508 
509 
510 /** Checks iterator, fact that iterator belongs to this mapper, iterator data.
511  * Only debug function.                                                      */
g_LBOS_CheckIterator(SERV_ITER iter,ELBOSIteratorCheckType should_have_data)512 int/*bool*/ g_LBOS_CheckIterator(SERV_ITER              iter,
513                                  ELBOSIteratorCheckType should_have_data)
514 {
515     assert(iter != NULL);  /* we can do nothing if this happens */
516     if (should_have_data == ELBOSIteratorCheckType_MustHaveData) {
517         if (iter->data == NULL) {
518             return 0;
519         }
520         assert(((SLBOS_Data*)iter->data)->a_cand >=
521             ((SLBOS_Data*)iter->data)->n_cand);
522         assert(((SLBOS_Data*)iter->data)->pos_cand <=
523             ((SLBOS_Data*)iter->data)->n_cand);
524     }
525     if (should_have_data == ELBOSIteratorCheckType_DataMustBeNULL
526         && iter->data != NULL) {
527         return 0;
528     }
529     if (strcmp(iter->op->mapper, s_lbos_op.mapper) != 0) {
530         return 0;
531     }
532     return 1;
533 }
534 
535 
536 /**  This function is needed to get LBOS hostname in different situations.
537  *  @param priority_find_method[in]
538  *   The first method to try to find LBOS. If it fails, default order of
539  *   methods will be used to find LBOS.
540  *   Default order is:
541  *   1) registry value of [CONN]->lbos;
542  *   2) 127.0.0.1:8080;
543  *   3) LBOS for current /etc/ncbi/{role, domain}.
544  *   To not specify default method, use eLBOSFindMethod_None.
545  *  @param lbos_addr[in]
546  *   If priority_find_method is eLBOS_FindMethod_CustomHost, then LBOS is
547  *   first looked for at hostname:port specified in this variable.           */
g_LBOS_GetLBOSAddressEx(ELBOSFindMethod priority_find_method,const char * lbos_addr)548 char* g_LBOS_GetLBOSAddressEx (ELBOSFindMethod priority_find_method,
549                                 const char* lbos_addr)
550 {
551 #if defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN
552     const char* lbosaddress = NULL; /* for const strings */
553 #endif /* defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN */
554     char* lbosaddress_temp = NULL;  /* for non-const strings */
555     char* address = NULL;
556     /* List of methods used, in their order */
557     ELBOSFindMethod find_method_order[] = {
558                priority_find_method /* eLBOSFindMethod_None, if not specified*/
559                , eLBOS_FindMethod_Registry
560                , eLBOS_FindMethod_Lbosresolve
561     };
562     size_t find_method_iter;
563     size_t find_method_count =
564         sizeof(find_method_order) / sizeof(ELBOSFindMethod);
565     CORE_LOG(eLOG_Trace, "Getting LBOS addresses...");
566     /* Iterate through methods of finding LBOS address */
567     for (find_method_iter = 0;
568          find_method_iter < find_method_count;
569          ++find_method_iter)
570     {
571         /* If LBOS address has been filled - we're done */
572         if (address != NULL)
573             break;
574         switch (find_method_order[find_method_iter]) {
575         case eLBOSFindMethod_None :
576             break;
577         case eLBOS_FindMethod_CustomHost :
578             if (g_LBOS_StringIsNullOrEmpty(lbos_addr)) {
579                 CORE_LOG_X(1, eLOG_Warning, "Use of custom LBOS address was "
580                            "asked for, but no custom address was supplied. "
581                            "Using default LBOS.");
582                 break;
583             }
584             address = strdup(lbos_addr);
585             if (address == NULL) {
586                 CORE_LOG_X(1, eLOG_Warning, "Did not manage to copy custom "
587                            "LBOS address. Probably insufficient RAM.");
588             }
589             break;
590         case eLBOS_FindMethod_Lbosresolve :
591 #if defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN
592             lbosaddress = s_LBOS_ReadLbosresolver();
593             if (g_LBOS_StringIsNullOrEmpty(lbosaddress)) {
594                 CORE_LOG_X(1, eLOG_Warning, "Trying to find LBOS using "
595                            "/etc/ncbi/lbosresolve failed");
596             } else {
597                 address = strdup(lbosaddress);
598             }
599 #endif /* defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN */
600             break;
601         case eLBOS_FindMethod_Registry:
602             lbosaddress_temp = g_LBOS_RegGet("CONN", "lbos", NULL);
603             if (g_LBOS_StringIsNullOrEmpty(lbosaddress_temp)) {
604                 CORE_LOG_X(1, eLOG_Warning, "Trying to find LBOS in "
605                                             "registry [CONN]lbos failed. "
606                                             "Using address in "
607                                             "/etc/ncbi/lbosresolver");
608                 free(lbosaddress_temp); /* just in case */
609                 lbosaddress_temp = NULL;
610                 break;
611             }
612             address = lbosaddress_temp;
613             break;
614         }
615     }
616     return address;
617 }
618 
619 
620 /* Get possible addresses of LBOS in default order */
g_LBOS_GetLBOSAddress(void)621 char* g_LBOS_GetLBOSAddress(void)
622 {
623     return g_LBOS_GetLBOSAddressEx(eLBOSFindMethod_None, NULL);
624 }
625 
626 
627 /*/////////////////////////////////////////////////////////////////////////////
628 //                    STATIC CONVENIENCE FUNCTIONS                           //
629 /////////////////////////////////////////////////////////////////////////////*/
630 #if 0 /* deprecated, remove on or after July 9 2016 */
631 /** Get role of current host. Returned string is read-only, may reside in
632  * 'data' memory area                                                        */
633 static const char* s_LBOS_ReadRole()
634 {
635     /*
636      * If no role previously read, fill it. Of course, not on MSWIN
637      */
638 #ifdef NCBI_OS_LINUX
639     if (s_LBOS_CurrentRole == NULL) {
640         size_t len;
641         char str[kMaxLineSize];
642         char* read_result; /* during function will become equal either NULL
643                               or str, do not free() */
644         FILE* role_file;
645         char *role; /* will be used to set s_LBOS_CurrentRole, do not free() */
646         if ((role_file = fopen(kRoleFile, "r")) == NULL) {
647             /* No role recognized */
648             CORE_LOGF(eLOG_Warning, ("s_LBOS_ReadRole: "
649                                      "could not open role file %s",
650                                      kRoleFile));
651             return NULL;
652         }
653         read_result = fgets(str, sizeof(str), role_file);
654         fclose(role_file);
655         if (read_result == NULL) {
656             CORE_LOG(eLOG_Warning, "s_LBOS_ReadRole: "
657                                      "memory allocation failed");
658             return NULL;
659         }
660         len = strlen(str);
661         assert(len);
662         /*We remove unnecessary '/n' and probably '/r'   */
663         if (str[len - 1] == '\n') {
664             if (--len && str[len - 1] == '\r')
665                 --len;
666             str[len] = '\0';
667         }
668         if (strncasecmp(str, "try", 3) == 0)
669             role = "try";
670         else {
671             if (strncasecmp(str, "qa", 2) == 0)
672                 role = "qa";
673             else {
674                 if (strncasecmp(str, "dev", 3) == 0)
675                     role = "dev";
676                 else {
677                     if (strncasecmp(str, "prod", 4) == 0)
678                         role = "prod";
679                     else {
680                         /* No role recognized */
681                         CORE_LOGF(eLOG_Warning,
682                             ("s_LBOS_ComposeLBOSAddress"
683                                 ": could not recognize role [%s] in %s",
684                                 str, kRoleFile));
685                         return NULL;
686                     }
687                 }
688             }
689         }
690         CORE_LOCK_WRITE;
691         /* Check one more time that no other thread managed to fill
692             * static variable ahead of this thread. If this happened,
693             * release memory */
694         if (s_LBOS_CurrentRole == NULL)
695             s_LBOS_CurrentRole = strdup(role);
696         CORE_UNLOCK;
697     }
698 #endif /* #ifdef NCBI_OS_LINUX */
699     return s_LBOS_CurrentRole;
700 }
701 
702 #endif /* 0 */
703 
704 #if defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN
705 /**  Read contents of lbosresolver
706  *
707  *  @warning
708  *   Do not modify or clear the returned string! Returned string is
709  *   read-only
710  */
s_LBOS_ReadLbosresolver(void)711 static const char* s_LBOS_ReadLbosresolver(void)
712 {
713     if (s_LBOS_Lbosresolver != NULL) {
714         return s_LBOS_Lbosresolver;
715     }
716 
717     FILE* lbosresolver_file;
718     size_t len;
719     char str[kMaxLineSize];
720     char* read_result; /* during function will become equal either NULL
721                         or str, do not free() */
722     if ((lbosresolver_file = fopen(kLbosresolverFile, "r")) == NULL) {
723         CORE_LOGF(eLOG_Warning, ("LBOS mapper: "
724                                  "could not open file %s",
725                                  kLbosresolverFile));
726         return NULL;
727     }
728     read_result = fgets(str, sizeof(str), lbosresolver_file);
729     fclose(lbosresolver_file);
730     if (read_result == NULL) {
731         CORE_LOG(eLOG_Warning, "s_LBOS_ReadLBOSResolve: "
732                                "memory allocation failed");
733         return NULL;
734     }
735     len = strlen(str);
736     assert(len);
737     if (g_LBOS_StringIsNullOrEmpty(str)) {
738         /* No domain recognized */
739         CORE_LOGF(eLOG_Warning,
740                   ("LBOS mapper: file %s is empty, no LBOS address available",
741                    kLbosresolverFile));
742         free(read_result);
743         return NULL;
744     }
745     /*We remove unnecessary '/n' and probably '/r'   */
746     if (str[len - 1] == '\n') {
747         if (--len && str[len - 1] == '\r') {
748             --len;
749         }
750         str[len] = '\0';
751     }
752     CORE_LOCK_WRITE;
753     /* Check one more time that no other thread managed to fill
754         * static variable ahead of this thread. If this happened,
755         * release memory */
756     if (s_LBOS_Lbosresolver == NULL)
757         /* We skip "http://" and "/lbos" */
758         str[strlen(str) - strlen("/lbos")] = '\0';
759         s_LBOS_Lbosresolver = strdup(str + 7);
760     CORE_UNLOCK;
761 
762     return s_LBOS_Lbosresolver;
763 }
764 #endif  /* defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN */
765 
766 
767 /**  Take original string and return URL-encoded string.
768  *  @attention
769  *   Original string is untouched. Caller is responsible for freeing
770  *   allocated space.
771  */
s_LBOS_URLEncode(const char * to_encode)772 static char* s_LBOS_URLEncode (const char* to_encode)
773 {
774     /* If all symbols are escape, our string will take triple space */
775     size_t encoded_string_buf_size = strlen(to_encode)*3 + 1;
776     char* encoded_string = (char*)calloc(encoded_string_buf_size,
777                                          sizeof(char));
778     size_t src_read, dst_written; /* strange things needed by URL_Encode */
779     URL_Encode(to_encode, strlen(to_encode), &src_read,
780                encoded_string, encoded_string_buf_size, &dst_written);
781     return encoded_string;
782 }
783 
784 
785 /**  Function to convert legacy service names for LBOS.
786  *   Takes service name and if it does not start with '/', prepends "/Legacy"
787  *   to it.
788  *  @attention
789  *   Original string is untouched. Caller is responsible for freeing
790  *   allocated space.
791  *  @warning
792  *   to_modify MUST be a valid non-empty C-string
793  */
s_LBOS_ModifyServiceName(const char * to_modify)794 char* s_LBOS_ModifyServiceName (const char* to_modify)
795 {
796     /* If all symbols are escape, our string will take triple space */
797     static const char* prefix = "/Legacy/";
798     if (to_modify[0] == '/')
799         return strdup(to_modify);
800     /* We deal with legacy service name. First, get "/Legacy/" prefix, then
801      * change to_modify to lower register, concatenate them and return */
802     char* modified_str = strdup(prefix);
803     char* service_lwr = strlwr(strdup(to_modify));
804     modified_str = g_LBOS_StringConcat(modified_str, service_lwr, NULL);
805     free(service_lwr);
806     return modified_str;
807 }
808 
809 
810 /** @brief Just connect and return connection
811  *
812  *  Internal function to create connection, which simplifies process
813  *  by ignoring many settings and making them default
814  */
s_LBOS_ConnectURL(SConnNetInfo * net_info,const char * url,SLBOS_UserData * user_data)815 static CONN s_LBOS_ConnectURL(SConnNetInfo* net_info, const char* url,
816                               SLBOS_UserData* user_data)
817 {
818     THTTP_Flags         flags        = fHTTP_AutoReconnect | fHTTP_Flushable;
819     CONN                conn;
820     CONNECTOR           connector;
821 
822     CORE_LOGF(eLOG_Note, ("Parsing URL \"%s\"", url));
823     if (!ConnNetInfo_ParseURL(net_info, url)) {
824         CORE_LOG(eLOG_Warning, "Cannot parse URL");
825         return NULL;
826     }
827     CORE_LOGF(eLOG_Note, ("Creating HTTP%s connector",
828             &"S"[net_info->scheme != eURL_Https]));
829     if (!(connector = HTTP_CreateConnectorEx(net_info, flags,
830                       g_LBOS_UnitTesting_GetLBOSFuncs()->ParseHeader,
831                       user_data/*data*/, 0,
832                       0/*cleanup*/)))
833     {
834         CORE_LOG(eLOG_Warning, "Cannot create HTTP connector");
835         return NULL;
836     }
837     CORE_LOG(eLOG_Note, "Creating connection");
838     if (CONN_Create(connector, &conn) != eIO_Success) {
839         CORE_LOG(eLOG_Warning, "Cannot create connection");
840         return NULL;
841     }
842     /*
843      * We define very little timeout so that we can quickly iterate
844      * through LBOSes (they should answer in no more than 0.5 second)
845      */
846     CONN_SetTimeout(conn, eIO_Open,      &kLBOSTimeout);
847     CONN_SetTimeout(conn, eIO_ReadWrite, &kLBOSTimeout);
848     return conn;
849 }
850 
851 
852 /** If you are sure that all answer will fit in one char*, use this function
853  * to read all input. Must be free()'d by the caller.
854  */
s_LBOS_UrlReadAll(SConnNetInfo * net_info,const char * url,int * status_code,char ** status_message)855 static char * s_LBOS_UrlReadAll(SConnNetInfo*   net_info,
856                                 const char*     url,
857                                 int*            status_code,
858                                 char**          status_message)
859 {
860     SLBOS_UserData user_data; /* used to store HTTP response code and
861                                  HTTP header, HTTP header only for unit
862                                  testing */
863     /* Status of reading*/
864     EIO_Status    status = eIO_Timeout;
865     /* how much how been read to the moment*/
866     size_t        totalRead        = 0;
867     /* how much we suppose one line takes*/
868     size_t        oneLineSize      = kMaxLineSize;
869     /* how much bytes was read in one turn*/
870     size_t        bytesRead        = 0;
871     /* Connection to LBOS. We do not know if LBOS really exists at url */
872     CONN          conn;
873     char*         buf;
874     size_t        totalBufSize;
875     char*         realloc_result;
876 
877     /* Not to handle case when 'status_code' is NULL, we use internal variable,
878      * and only try to set 'status_code' in the end of this function (and if it
879      * is NULL, we do not set it) */
880     user_data.http_response_code = 0;
881     /* The same for status_message */
882     user_data.http_status_mesage = NULL;
883     net_info->max_try = 1; /* we do not need to try more than once */
884     /* Set HTTP header for unit testing. Pointer to already existing string. */
885     user_data.header = net_info->http_user_header;
886     /* Set content length to the maximum possible value */
887     user_data.content_length = (size_t)-1;
888     conn = s_LBOS_ConnectURL(net_info, url, &user_data);
889     if (conn == NULL) {
890         return NULL;
891     }
892     CONN_SetUserData(conn, &user_data);
893     buf              = (char*)calloc(oneLineSize, sizeof(char));
894     /* Total length of buffer. We already have oneLineSize because of calloc */
895     totalBufSize     = oneLineSize;
896     if (buf == NULL)
897     {
898         CORE_LOG(eLOG_Critical, "s_LBOS_UrlReadAll: No RAM. "
899                                 "Returning NULL.");
900         CONN_Close(conn);
901         return NULL;
902     }
903     do {
904         /* If there is no LBOS, we will know about it here */
905         status = g_LBOS_UnitTesting_GetLBOSFuncs()->
906                           Read(conn, buf + strlen(buf),
907                                totalBufSize - totalRead - 1 /* for \0 */,
908                                &bytesRead, eIO_ReadPlain);
909         if (user_data.http_response_code != 200) {
910             CORE_LOGF(eLOG_Critical, ("s_LBOS_UrlReadAll: LBOS returned "
911                                       "status code %d",
912                                       user_data.http_response_code));
913         }
914         if (status_code != NULL) {
915             *status_code = user_data.http_response_code;
916         }
917         if (status_message != NULL && user_data.http_status_mesage != NULL) {
918             *status_message = strdup(user_data.http_status_mesage);
919         }
920         free(user_data.http_status_mesage); /* not needed anymore*/
921         user_data.http_status_mesage = NULL;
922         /* If could not connect, fail now */
923         if (user_data.http_response_code == 0) {
924             free(buf);
925             CONN_Close(conn);
926             return NULL;
927         }
928         totalRead += bytesRead;
929 
930         buf[totalRead] = 0; /* force end of string */
931 
932         /* IF we still have to read - then add space to buffer, if needed  */
933         if ( status == eIO_Success && totalBufSize < totalRead * 2 )
934         {
935             realloc_result = (char*)realloc(buf,
936                                             sizeof(char) * (totalBufSize * 2));
937             if (realloc_result == NULL) {
938                 CORE_LOG(eLOG_Warning, "s_LBOS_UrlReadAll: Buffer "
939                                         "overflow. Returning string at its "
940                                         "maximum size");
941                 return buf;
942             } else {
943                 buf = realloc_result;
944                 totalBufSize *= 2;
945             }
946         }
947     } while (status == eIO_Success);
948     /*In the end we shrink buffer to the minimal needed size*/
949     if ( !(realloc_result = (char*) realloc(buf,
950                                            sizeof(char) * (strlen(buf) + 1))) )
951     {
952         CORE_LOG(eLOG_Warning, "s_LBOS_UrlReadAll: Buffer shrink error, using "
953                                "original stirng");
954     }  else  {
955         buf = realloc_result;
956     }
957     CONN_Close(conn);
958     return buf;
959 }
960 
961 
962 /**   Resolve service name to one of the hosts implementing service.
963  *   Uses LBZK at specified IP and port.
964  */
s_LBOS_ResolveIPPort(const char * lbos_address,const char * service_name,SConnNetInfo * net_info)965 static SSERV_Info** s_LBOS_ResolveIPPort(const char* lbos_address,
966                                          const char* service_name,
967                                          SConnNetInfo* net_info)
968 {
969     SSERV_Info** infos;
970     size_t infos_count;
971     size_t infos_capacity;
972     char* servicename_url_encoded = NULL;
973     size_t url_length;
974     char * url;
975     char * lbos_answer; /*to write down LBOS's answer*/
976     const char* user_dtab = NULL;
977     size_t length;
978     size_t user_dtab_length;
979     char* new_dtab = NULL;
980     char* user_dtab_end;
981     /* Allocate space for answer (will be expanded later, if needed) */
982     infos = (SSERV_Info**)calloc(2, sizeof(SSERV_Info*));
983     if (infos == NULL) {
984         CORE_LOG(eLOG_Critical, "s_LBOS_ResolveIPPort: No RAM. "
985                                 "Returning NULL.");
986         return NULL;
987     }
988     infos_count = 0;
989     infos_capacity = 1;
990     /* Update HTTP Header with local DTabs from registry and revert after we
991      * finished reading from URL */
992     /* First, we look if there is DTab-Local already in header*/
993     char* old_header = net_info->http_user_header ?
994             strdup(net_info->http_user_header) : NULL;
995     user_dtab = g_LBOS_strcasestr(net_info->http_user_header, "DTab-local:");
996     /* If there is an already defined local DTab, we mix it with one from
997      * registry */
998     if (user_dtab != NULL)
999     {
1000         /* Move start after name of tag*/
1001         user_dtab += strlen("DTab-Local:");
1002         if (user_dtab[1] == ' ') {
1003             user_dtab++;
1004         }
1005         /* Find end of line */
1006         user_dtab_end = strchr(user_dtab, '\n');
1007         if (user_dtab_end[-1] == '\r')  {
1008             user_dtab_end--;
1009         }
1010         /* Create new string that includes first DTabs from registry and then
1011          * DTabs from HTTP requests */
1012         length = 0;
1013         user_dtab_length = user_dtab_end - user_dtab;
1014         new_dtab = NULL;
1015         new_dtab = g_LBOS_StringNConcat(g_LBOS_StringConcat(
1016             g_LBOS_StringConcat(g_LBOS_StringConcat(
1017             /*dest*/   /*to append*/       /*length*/   /*count*/
1018             new_dtab,   "DTab-local: ",     &length),
1019                         s_LBOS_DTABLocal,   &length),
1020                         ";",                &length),
1021                         user_dtab,          &length,    user_dtab_length);
1022         ConnNetInfo_OverrideUserHeader(net_info, new_dtab);
1023         free(new_dtab);
1024     } else {
1025         /* Set default Dtab from registry */
1026         size_t length = 0;
1027         char* new_dtab = NULL;
1028         new_dtab = g_LBOS_StringConcat(g_LBOS_StringConcat(
1029             /*dest*/   /*to append*/       /*length*/   /*count*/
1030             new_dtab,   "DTab-local: ",     &length),
1031                         s_LBOS_DTABLocal,   &length);
1032         ConnNetInfo_OverrideUserHeader(net_info, new_dtab);
1033         free(new_dtab);
1034     }
1035     servicename_url_encoded = s_LBOS_ModifyServiceName(service_name);
1036    /*encode service name to url encoding (change ' ' to %20, '/' to %2f, etc.)*/
1037     url_length = strlen("http://")  + strlen(lbos_address) +
1038                  strlen(kLBOSQuery) + strlen(servicename_url_encoded);
1039     url = (char*)malloc(sizeof(char) * url_length + 1); /** to make up
1040                                                    LBOS query URI to connect*/
1041     if (url == NULL)
1042     {
1043         CORE_LOG(eLOG_Critical, "s_LBOS_ResolveIPPort: No RAM. "
1044                                 "Returning NULL.");
1045         free(infos);
1046         free(old_header);
1047         free(servicename_url_encoded);
1048         return NULL;
1049     }
1050     sprintf(url, "%s%s%s%s", "http://", lbos_address, kLBOSQuery,
1051             servicename_url_encoded);
1052     lbos_answer = s_LBOS_UrlReadAll(net_info, url, NULL, NULL);
1053     /* Revert header */
1054     ConnNetInfo_OverrideUserHeader(net_info, old_header);
1055     free(old_header);
1056     free(url);
1057     free(servicename_url_encoded);
1058     /* If no connection */
1059     if (lbos_answer == NULL) {
1060         free(infos);
1061         return NULL;
1062     }
1063     x_JSON_Value  *root_value;
1064     x_JSON_Object *root_obj;
1065     x_JSON_Object *services;
1066     x_JSON_Array  *serviceEndpoints;
1067     x_JSON_Object *serviceEndpoint;
1068     unsigned int j = 0;
1069     root_value = x_json_parse_string(lbos_answer);
1070     if (x_json_value_get_type(root_value) != JSONObject) {
1071         goto clean_and_exit;
1072     }
1073     root_obj = x_json_value_get_object(root_value);
1074     services = x_json_object_get_object(root_obj, "services");
1075     /* Get endpoints for the first service name.
1076      * Note: Multiple service resolution is not supported intentionally
1077      * (yet). */
1078     serviceEndpoints =
1079         x_json_object_get_array(services, x_json_object_get_name(services, 0));
1080     /* Iterate through endpoints */
1081     for (j = 0;  j < x_json_array_get_count(serviceEndpoints);  j++) {
1082         const char *host, *rate, *extra, *type;
1083         char* server_description;
1084         const char* descr_format = "%s %s:%u %s Regular R=%s L=no T=25";
1085         int port;
1086         serviceEndpoint = x_json_array_get_object(serviceEndpoints, j);
1087         host = x_json_object_dotget_string(serviceEndpoint,
1088                                          "serviceEndpoint.host");
1089         if (host == NULL) {
1090             continue;
1091         }
1092         port = (int)x_json_object_dotget_number(serviceEndpoint,
1093                                               "serviceEndpoint.port");
1094         /* -------------rate------------- */
1095         rate = x_json_object_dotget_string(serviceEndpoint,
1096                                          "meta.rate");
1097         rate = !g_LBOS_StringIsNullOrEmpty(rate) ? rate : "1";
1098         /* -------------type------------- */
1099         type = x_json_object_dotget_string(serviceEndpoint,
1100                                          "meta.type");
1101         type = !g_LBOS_StringIsNullOrEmpty(type) ? type : "STANDALONE";
1102         /* -------------extra------------- */
1103         extra = x_json_object_dotget_string(serviceEndpoint,
1104                                          "meta.extra");
1105         extra = !g_LBOS_StringIsNullOrEmpty(extra) ? extra : "";
1106 
1107         /* Examples:
1108            HTTP - accn2gi
1109            DNS  - alndbasn_lb
1110            STANDALONE - aligndb_dbldd
1111            HTTP_POST - taxservice3test
1112            NCBID - taxservice/mapviewaugust2011*/
1113         size_t length; /* used to count size to allocate for server_description,
1114                           and then for g_LBOS_StringConcat*/
1115         /* Occasionally, we are not able to allocate memory */
1116         length = strlen(descr_format) + strlen(type) + strlen(host) +
1117                  5 /*length of port*/ + strlen(extra) + strlen(rate);
1118         server_description = malloc(sizeof(char) * length);
1119         sprintf(server_description, descr_format, type, host,
1120                 port, extra, rate);
1121         SSERV_Info * info = SERV_ReadInfoEx(server_description,service_name, 0);
1122         free(server_description);
1123         if (info == NULL) {
1124             continue;
1125         }
1126         if (infos_capacity <= infos_count + 1) {
1127             SSERV_Info** realloc_result =
1128                 (SSERV_Info**)realloc(infos, sizeof(SSERV_Info*) *
1129                                              (infos_capacity*2 + 1));
1130             if (realloc_result == NULL) {
1131                 /* If error with realloc, return as much as could
1132                  * allocate for */
1133                 infos_count--; /* Will just rewrite last info with NULL
1134                                 * to mark end of array*/
1135                 free(info);
1136                 break;
1137             } else { /* If realloc successful */
1138                 infos = realloc_result;
1139                 infos_capacity = infos_capacity*2 + 1;
1140             }
1141         }
1142         infos[infos_count++] = info;
1143     }
1144 
1145 clean_and_exit:
1146     x_json_value_free(root_value);
1147     free(lbos_answer);
1148     /* Shuffle list with Durstenfeld's shuffle algorithm
1149      * (also credits go to Fisher and Yates, and Knuth) */
1150     if (infos_count > 1) {
1151         size_t i;
1152         for (i = 0; i < infos_count - 1; i++) {
1153             size_t j = i + ( rand() % (infos_count - i) );
1154             if (i == j) continue; /* not swapping the item with itself */
1155             SSERV_Info* t = infos[j];
1156             infos[j] = infos[i];
1157             infos[i] = t;
1158         }
1159     }
1160     /* Set last element this NULL, finalizing the array ...*/
1161     infos[infos_count] = NULL;
1162 
1163     return infos;
1164 }
1165 
1166 
1167 /** Given an empty iterator which has service name in its "name" field
1168  * we fill it with all servers which were found by asking LBOS
1169  * @param[in,out] iter
1170  *  We need name from it, then set "data" field with all servers. All
1171  *  previous "data" will be overwritten, causing possible memory leak.
1172  */
s_LBOS_FillCandidates(SLBOS_Data * data,const char * service)1173 static void s_LBOS_FillCandidates(SLBOS_Data* data, const char* service)
1174 {
1175     unsigned int    i;
1176     SSERV_Info**    hostports_array = 0;
1177     char*           lbos_address    = NULL; /* We copy LBOS address to  */
1178 
1179     /* We suppose that number of addresses is constant (and so
1180        is position of NULL), so no mutex is necessary */
1181     if (s_LBOS_Instance == NULL) return;
1182     lbos_address = s_LBOS_Instance;
1183     CORE_LOGF_X(1, eLOG_Trace, ("Trying to find servers of \"%s\" with "
1184                 "LBOS at %s", service, lbos_address));
1185     hostports_array =
1186         g_LBOS_UnitTesting_GetLBOSFuncs()->ResolveIPPort(lbos_address, service,
1187                                                          data->net_info);
1188     if (hostports_array == NULL) {
1189         CORE_LOGF_X(1, eLOG_Trace, ("Ho servers of \"%s\" found by LBOS",
1190                     service));
1191         return;
1192     }
1193     for (i = 0;  hostports_array[i] != NULL;  i++) continue;
1194     CORE_LOGF_X(1, eLOG_Trace, ("Found %u servers of \"%s\" with "
1195                 "LBOS at %s", i, service, lbos_address));
1196     /* If we received answer from LBOS, we fill candidates */
1197     if (hostports_array != NULL) {
1198         SLBOS_Candidate* realloc_result;
1199         /* To allocate space once and forever, let's quickly find the number of
1200          * received addresses...
1201          */
1202         for (i = 0;  hostports_array[i] != NULL;  ++i) continue;
1203         /* ...and allocate space */
1204         realloc_result = (SLBOS_Candidate*)realloc(data->cand,
1205                                            sizeof(SLBOS_Candidate) * (i + 1));
1206         if (realloc_result == NULL) {
1207             CORE_LOGF_X(1, eLOG_Warning,
1208                         ("s_LBOS_FillCandidates: Could not "
1209                         "allocate space for all candidates, "
1210                         "will use as much as was allocated "
1211                         "initially: %du",
1212                         (unsigned int)data->a_cand));
1213         } else {
1214             data->cand = realloc_result;
1215             data->a_cand = i + 1;
1216         }
1217         for (i = 0;  hostports_array[i] != NULL && i < data->a_cand;  i++) {
1218             data->cand[i].info = hostports_array[i];
1219             data->n_cand++;
1220         }
1221         free(hostports_array);
1222     }
1223     /*If we did not find answer from LBOS, we just finish*/
1224 }
1225 
1226 
1227 
s_LBOS_CheckAnnounceArgs(const char * service,const char * version,const char * host,unsigned short port,const char * healthcheck_url,char ** lbos_answer)1228 static int s_LBOS_CheckAnnounceArgs(const char* service,
1229                                     const char* version,
1230                                     const char* host,
1231                                     unsigned short port,
1232                                     const char* healthcheck_url,
1233                                     char** lbos_answer)
1234 {
1235     unsigned short i;
1236     if (g_LBOS_StringIsNullOrEmpty(healthcheck_url)) {
1237         CORE_LOG(eLOG_Critical, "Error with announcement, "
1238                                 "no healthcheck_url specified.");
1239         return 0;
1240     }
1241     if ((strstr(healthcheck_url, "http://") != healthcheck_url)
1242         &&
1243         (strstr(healthcheck_url, "https://") != healthcheck_url)) {
1244         CORE_LOG(eLOG_Critical, "Error with announcement, missing http:// or "
1245                                 "https:// in the beginning of healthcheck "
1246                                 "URL.");
1247         return 0;
1248     }
1249     /* Greedy check for host - host should consist of alphanumerics and dots,
1250      * or be NULL */
1251     if (!g_LBOS_StringIsNullOrEmpty(host)) {
1252         for (i = 0;  i < strlen(host);  i++) {
1253             if (!isalnum(host[i]) && (host[i] != '.')) {
1254                 CORE_LOG(eLOG_Critical, "Error with announcement, "
1255                                         "ip has incorrect format "
1256                                         "(only digits and dots are allowed). "
1257                                         "Please provide resolved IP to "
1258                                         "avoid this error");
1259                 return 0;
1260             }
1261         }
1262     }
1263     if (port < 1 || port > 65535) {
1264         CORE_LOG(eLOG_Critical, "Error with announcement, incorrect port.");
1265         return 0;
1266     }
1267     if (g_LBOS_StringIsNullOrEmpty(version)) {
1268         CORE_LOG(eLOG_Critical, "Error with announcement, "
1269                                 "no version specified.");
1270         return 0;
1271     }
1272     if (g_LBOS_StringIsNullOrEmpty(service)) {
1273         CORE_LOG(eLOG_Critical, "Error with announcement, "
1274                                 "no service name specified.");
1275         return 0;
1276     }
1277     if (lbos_answer == NULL) {
1278         CORE_LOG(eLOG_Critical, "Error with announcement, "
1279                                 "no variable provided to save LBOS answer.");
1280         return 0;
1281     }
1282     return 1;
1283 }
1284 
1285 
s_LBOS_CheckDeannounceArgs(const char * service,const char * version,const char * host,unsigned short port)1286 static int s_LBOS_CheckDeannounceArgs(const char*    service,
1287                                       const char*    version,
1288                                       const char*    host,
1289                                       unsigned short port)
1290 {
1291     if (!g_LBOS_StringIsNullOrEmpty(host) && strstr(host, ":") != NULL) {
1292         CORE_LOG(eLOG_Critical, "Invalid argument passed for de-announcement, "
1293                                 "please check that \"host\" parameter does "
1294                                 "not contain protocol or port");
1295         return 0;
1296     }
1297     if (port < 1 || port > 65535) {
1298         CORE_LOG(eLOG_Critical, "Invalid argument passed for de-announcement, "
1299                                 "incorrect port.");
1300         return 0;
1301     }
1302     if (g_LBOS_StringIsNullOrEmpty(version)) {
1303         CORE_LOG(eLOG_Critical, "Invalid argument passed for de-announcement, "
1304                                 "no version specified.");
1305         return 0;
1306     }
1307     if (g_LBOS_StringIsNullOrEmpty(service)) {
1308         CORE_LOG(eLOG_Critical, "Invalid argument passed for de-announcement, "
1309                                 "no service name specified.");
1310         return 0;
1311     }
1312     return 1;
1313 }
1314 
1315 
1316 /** Iterator request through LBOSes and return the best answer (ideally, one
1317  * that has status code 200)
1318  * This function is TODO
1319  */
s_LBOS_PerformRequest(const char * request,char ** lbos_answer,char ** http_status_message,TReqMethod req_method,const char * service)1320 static unsigned short s_LBOS_PerformRequest(const char* request,
1321                                             char**      lbos_answer,
1322                                             char**      http_status_message,
1323                                             TReqMethod  req_method,
1324                                             const char* service)
1325 {
1326     SConnNetInfo*  net_info;
1327     char*          buf;
1328     char*          lbos_address;
1329     int            status_code;
1330     char*          query;
1331     char*          status_message  = NULL;
1332     size_t         length           = 0;
1333     /* We try all LBOSes until we get 200. If we receive error status code
1334      * instead of 200, we save it. So, if no LBOS returns 200, we
1335      * return error code. If we receive nothing (LBOS is not present), then
1336      * we save nothing.
1337      */
1338     net_info             = ConnNetInfo_Clone(s_EmptyNetInfo);
1339     net_info->req_method = req_method;
1340     buf                  = NULL;
1341     status_code          = 0;
1342     lbos_address         = s_LBOS_Instance;
1343     query                = g_LBOS_StringConcat(g_LBOS_StringConcat(
1344                                 strdup("http://"), lbos_address, &length),
1345                                 request, &length);
1346     length               = strlen(query);
1347     buf                  = s_LBOS_UrlReadAll(net_info, query, &status_code,
1348                                              &status_message);
1349     free(query);
1350     if (lbos_answer != NULL && !g_LBOS_StringIsNullOrEmpty(buf)) {
1351         *lbos_answer = strdup(buf);
1352     }
1353     free(buf);
1354     if (http_status_message != NULL && status_message != NULL) {
1355         *http_status_message = strdup(status_message);
1356     }
1357     free(status_message);
1358 
1359     if (status_code == 0) {
1360         status_code = eLBOS_LbosNotFound;
1361     }
1362     /* Cleanup */
1363     ConnNetInfo_Destroy(net_info);
1364     return status_code;
1365 }
1366 
1367 /**  Find "0.0.0.0" in healthcheck_url and replace it with IP of current host
1368  *  @attention
1369  *   Original string is untouched. Caller is responsible for freeing
1370  *   allocated space.
1371  */
s_LBOS_Replace0000WithIP(const char * healthcheck_url)1372 static char* s_LBOS_Replace0000WithIP(const char* healthcheck_url)
1373 {
1374     size_t          chars_to_copy;
1375     const char*     query;
1376     char            hostname[kMaxLineSize];
1377     size_t          length;
1378     /* new url with replaced "0.0.0.0" (if needed) */
1379     char*           my_healthcheck_url;
1380     unsigned int    local_host_ip;
1381     const char*     replace_pos; /* to check if there is 0.0.0.0 */
1382     if (healthcheck_url == NULL)
1383         return NULL;
1384     /* By 'const' said that we will not touch healthcheck_url, but actually
1385      * we need to be able to change it, so we copy it */
1386     /* If we need to insert local IP instead of 0.0.0.0, we will */
1387     if ((replace_pos = strstr(healthcheck_url, "0.0.0.0")) == NULL) {
1388         my_healthcheck_url = strdup(healthcheck_url);
1389         return(my_healthcheck_url);
1390     }
1391     my_healthcheck_url = (char*)calloc(kMaxLineSize, sizeof(char));
1392     if (my_healthcheck_url == NULL) {
1393         CORE_LOG(eLOG_Warning, "Failed memory allocation. Most likely, "
1394                                "not enough RAM.");
1395         return NULL;
1396     }
1397     chars_to_copy   = replace_pos - healthcheck_url;
1398     query           = replace_pos + strlen("0.0.0.0");
1399     local_host_ip = g_LBOS_UnitTesting_GetLBOSFuncs()->LocalHostAddr(eDefault);
1400     if (local_host_ip == 0) {
1401         CORE_LOG(eLOG_Warning,
1402                  "Error with announcement, cannot find local IP.");
1403         free(my_healthcheck_url);
1404         return NULL;
1405     }
1406     SOCK_HostPortToString(local_host_ip, 0, hostname, kMaxLineSize - 1);
1407     if (strlen(hostname) == 0) {
1408         CORE_LOG(eLOG_Warning,
1409                  "Error with announcement, cannot find local IP.");
1410         free(my_healthcheck_url);
1411         return NULL;
1412     }
1413     length = strlen(my_healthcheck_url);
1414     my_healthcheck_url =
1415         g_LBOS_StringConcat(g_LBOS_StringConcat(g_LBOS_StringNConcat(
1416         my_healthcheck_url, healthcheck_url,   &length,   chars_to_copy),
1417                             strlwr(hostname),  &length),
1418                             query,             &length);
1419 
1420     return my_healthcheck_url;
1421 }
1422 
1423 
s_TurnOn()1424 static int s_TurnOn()
1425 {
1426     if (s_LBOS_Init == 0) {
1427         s_LBOS_funcs.Initialize();
1428     }
1429     if (s_LBOS_TurnedOn == 0) {
1430         return 0;
1431     }
1432     return 1;
1433 }
1434 
1435 /*/////////////////////////////////////////////////////////////////////////////
1436 //                             UNIT TESTING                                  //
1437 /////////////////////////////////////////////////////////////////////////////*/
1438 /** Check whether LBOS client is turned ON or OFF
1439  * @return
1440  *  address of static variable s_LBOS_TurnedOn
1441  * @see
1442  *  SERV_LBOS_Open()                                                         */
g_LBOS_UnitTesting_PowerStatus(void)1443 int* g_LBOS_UnitTesting_PowerStatus(void)
1444 {
1445     return &s_LBOS_TurnedOn;
1446 }
1447 
1448 
g_LBOS_UnitTesting_GetLBOSFuncs(void)1449 SLBOS_Functions* g_LBOS_UnitTesting_GetLBOSFuncs(void)
1450 {
1451     return &s_LBOS_funcs;
1452 }
1453 
1454 /**  Check whether LBOS client has been initialized already
1455  *  @return
1456  *   Address of static variable s_LBOS_Init
1457  *  @see
1458  *   SERV_LBOS_Open()                                                        */
g_LBOS_UnitTesting_InitStatus(void)1459 int* g_LBOS_UnitTesting_InitStatus(void)
1460 {
1461     return &s_LBOS_Init;
1462 }
1463 
1464 
1465 /**  List of addresses of LBOS that is maintained in actual state.
1466  *  @return
1467  *   address of static variable s_LBOS_InstancesList.
1468  *  @see
1469  *   SERV_LBOS_Open(), s_LBOS_FillCandidates()                               */
g_LBOS_UnitTesting_Instance(void)1470 char** g_LBOS_UnitTesting_Instance(void)
1471 {
1472     return &s_LBOS_Instance;
1473 }
1474 
1475 
1476 /**  Pointer to s_LBOS_CurrentRole
1477  *  @return
1478  *   address of static variable s_LBOS_CurrentRole.
1479  *  @see                                                                     */
g_LBOS_UnitTesting_Lbosresolver(void)1480 char** g_LBOS_UnitTesting_Lbosresolver(void)
1481 {
1482     return &s_LBOS_Lbosresolver;
1483 }
1484 
1485 
g_LBOS_UnitTesting_SetLBOSFindMethod(SERV_ITER iter,ELBOSFindMethod method)1486 int/*bool*/ g_LBOS_UnitTesting_SetLBOSFindMethod (SERV_ITER       iter,
1487                                                   ELBOSFindMethod method)
1488 {
1489     SLBOS_Data* data;
1490     assert(g_LBOS_CheckIterator(iter, ELBOSIteratorCheckType_MustHaveData));
1491     data = (SLBOS_Data*) iter->data;
1492     data->find_method = method;
1493     return 1;
1494 }
1495 
1496 
g_LBOS_UnitTesting_SetLBOSResolverFile(const char * resolverfile)1497 int/*bool*/ g_LBOS_UnitTesting_SetLBOSResolverFile(const char* resolverfile)
1498 {
1499     if (resolverfile != NULL) {
1500         kLbosresolverFile = resolverfile;
1501         return 1;
1502     }
1503     return 0;
1504 }
1505 
1506 
1507 /**  Set custom address for LBOS. Can be either hostname:port or IP:port.
1508  *   Intended mostly for testing.
1509  *  @param[in] iter
1510  *   Where to set address for LBOS. Change is made only for this iterator */
g_LBOS_UnitTesting_SetLBOSaddress(SERV_ITER iter,char * address)1511 int/*bool*/ g_LBOS_UnitTesting_SetLBOSaddress (SERV_ITER iter, char* address) {
1512     SLBOS_Data* data;
1513     assert(g_LBOS_CheckIterator(iter, ELBOSIteratorCheckType_MustHaveData));
1514     data = (SLBOS_Data*) iter->data;
1515     data->lbos_addr = address;
1516     return 1;
1517 }
1518 
1519 
g_LBOS_UnitTesting_GetAnnouncedServers(void)1520 struct SLBOS_AnnounceHandle_Tag** g_LBOS_UnitTesting_GetAnnouncedServers(void)
1521 {
1522     return &s_LBOS_AnnouncedServers;
1523 }
1524 
1525 
g_LBOS_UnitTesting_GetAnnouncedServersNum(void)1526 int g_LBOS_UnitTesting_GetAnnouncedServersNum(void)
1527 {
1528     return s_LBOS_AnnouncedServersNum;
1529 }
1530 
1531 
g_LBOS_UnitTesting_FindAnnouncedServer(const char * service,const char * version,unsigned short port,const char * host)1532 int g_LBOS_UnitTesting_FindAnnouncedServer(const char*             service,
1533                                            const char*             version,
1534                                            unsigned short          port,
1535                                            const char*             host)
1536 {
1537     return s_LBOS_FindAnnouncedServer(service, version, port, host);
1538 }
1539 
1540 
1541 /*/////////////////////////////////////////////////////////////////////////////
1542 //                      DATA CONSTRUCTOR/DESTRUCTOR                          //
1543 /////////////////////////////////////////////////////////////////////////////*/
1544 /** Constructor for SLBOS_Data. Returns pointer to new empty initialized
1545  * SLBOS_Data.                                                              */
s_LBOS_ConstructData(size_t candidatesCapacity)1546 static SLBOS_Data* s_LBOS_ConstructData(size_t candidatesCapacity)
1547 {
1548     SLBOS_Data* data;
1549 
1550     if (!(data = (SLBOS_Data*) calloc(1, sizeof(SLBOS_Data))))
1551     {
1552         CORE_LOG_X(1, eLOG_Error, "Could not allocate memory for LBOS mapper");
1553         return NULL;
1554     }
1555     /*
1556      * We consider that there will never be more than 20 candidates, which
1557      * is not many for us
1558      */
1559     data->a_cand        = candidatesCapacity;
1560     data->pos_cand      = 0;
1561     data->n_cand        = 0;
1562     data->lbos_addr     = NULL;
1563     data->find_method   = eLBOSFindMethod_None;
1564     data->cand          = (SLBOS_Candidate*)calloc(candidatesCapacity,
1565                                                    sizeof(SLBOS_Candidate));
1566     return data;
1567 }
1568 
1569 
1570 /** Destructor for SLBOS_Data.
1571  * @param[in] data_to_delete
1572  *  Please note that you have to set pointer to NULL yourself.               */
s_LBOS_DestroyData(SLBOS_Data * data)1573 static void s_LBOS_DestroyData(SLBOS_Data* data)
1574 {
1575     if (data == NULL) {
1576         return;
1577     }
1578     if (data->cand != NULL) {
1579         size_t i;
1580         for (i = data->pos_cand;  i < data->n_cand;  i++) {
1581             if (data->cand[i].info != NULL) {
1582                 free(data->cand[i].info);
1583                 data->cand[i].info = NULL;
1584             }
1585         }
1586         free(data->cand);
1587     }
1588     if (data->net_info) {
1589         ConnNetInfo_Destroy(data->net_info);
1590     }
1591     free(data);
1592 }
1593 
1594 
1595 /*/////////////////////////////////////////////////////////////////////////////
1596 //                        MAIN MAPPER FUNCTIONS                              //
1597 /////////////////////////////////////////////////////////////////////////////*/
1598 /**  This function tests existence of the application that should always be
1599  *  found - LBOS itself. If it is not found, we turn LBOS off.               */
s_LBOS_Initialize(void)1600 static void s_LBOS_Initialize(void)
1601 {
1602     const char*     service     = "/lbos";
1603     SConnNetInfo*   net_info;
1604     SERV_ITER       iter;
1605     CORE_LOCK_WRITE;
1606         if (s_LBOS_Instance == NULL) {
1607             s_LBOS_Instance = g_LBOS_GetLBOSAddress();
1608         }
1609         if (s_EmptyNetInfo == NULL) {
1610             s_EmptyNetInfo = ConnNetInfo_Create(NULL);
1611         }
1612     CORE_UNLOCK;
1613     s_LBOS_TurnedOn = 1; /* To ensure that initialization does
1614                             not depend on this variable */
1615     s_LBOS_Init     = 1;
1616 
1617     /*
1618      * Load DTAB Local from registry
1619      */
1620     free(s_LBOS_DTABLocal);
1621     s_LBOS_DTABLocal = g_LBOS_RegGet("CONN", "DTAB", NULL);
1622     if (g_LBOS_StringIsNullOrEmpty(s_LBOS_DTABLocal)) {
1623         CORE_LOG_X(1, eLOG_Trace, "No DTAB in registry");
1624     } else {
1625         CORE_LOGF_X(1, eLOG_Trace,
1626                    ("DTAB from registry: %s ", s_LBOS_DTABLocal));
1627     }
1628 
1629     /* Check On/Off status */
1630     char* lbos_toggle = g_LBOS_RegGet("CONN", "LBOS_ENABLE", NULL);
1631     int lbos_toggled = ConnNetInfo_Boolean(lbos_toggle);
1632     free(lbos_toggle);
1633     if (lbos_toggled) {
1634         CORE_LOG_X(1, eLOG_Note, "LBOS is turned ON in config.");
1635     } else {
1636         CORE_LOG_X(1, eLOG_Warning,
1637                    "LBOS is NOT turned ON in config! Please provide "
1638                    "[CONN]LBOS_ENABLE=1");
1639         s_LBOS_TurnedOn = 0;
1640         return;
1641     }
1642     /*
1643      * Try to find LBOS
1644      */
1645     iter = (SERV_ITER)calloc(1, sizeof(*iter));
1646     assert(iter != NULL);  /* we can do nothing if this happens */
1647     iter->name  = service;
1648     net_info    = ConnNetInfo_Clone(s_EmptyNetInfo);
1649     iter->op    = SERV_LBOS_Open(iter, net_info, NULL);
1650     ConnNetInfo_Destroy(net_info);
1651     if (iter->op == NULL) {
1652         CORE_LOGF_X(1, eLOG_Warning,
1653                     ("Could not connect to LBOS, or "
1654                      "http://%s/lbos/text/mlresolve?name=%%2flbos "
1655                      "is empty. Turning LBOS off in this "
1656                      "process.", s_LBOS_Instance));
1657         s_LBOS_TurnedOn = 0;
1658     } else {
1659         s_LBOS_Close(iter);
1660         s_LBOS_TurnedOn = 1;
1661     }
1662     free(iter);
1663 
1664 }
1665 
1666 
1667 /** After we receive answer from dispd.cgi, we parse header
1668  * to get all hosts.                                                         */
s_LBOS_ParseHeader(const char * header,void * response,int server_error)1669 static EHTTP_HeaderParse s_LBOS_ParseHeader(const char*      header,
1670                                             void* /* SLBOS_UserData* */
1671                                                              response,
1672                                             int              server_error)
1673 {
1674     SLBOS_UserData* response_output;
1675     int             status_code = 0/*success code if any*/;
1676     /* For all we know, status message ends before \r\n */
1677     char*           header_end;
1678     char*           status_message;
1679     unsigned int    content_length;
1680     char*           content_length_pos;
1681 
1682     if (response == NULL) {
1683         /* We do not intervent in the process by default */
1684         return eHTTP_HeaderSuccess;
1685     }
1686     response_output = (SLBOS_UserData*)response;
1687     header_end      = strstr(header, "\r\n");
1688     status_message  = (char*)calloc(header_end-header, sizeof(char));
1689 
1690     if (sscanf(header, "%*s %d %[^\r]\r\n", &status_code,
1691                status_message) < 1)
1692     {
1693         if (response != NULL) {
1694             response_output->http_response_code = 503; /* server error */
1695         }
1696         free(status_message);
1697         return eHTTP_HeaderError;
1698     }
1699     if (status_code != 200) {
1700         CORE_LOGF(eLOG_Critical, ("s_LBOS_UrlReadAll: LBOS returned status "
1701                   "code %d", status_code));
1702     }
1703     char* temp_header = strlwr(strdup(header)); /* we cannot modify
1704                                                  * original */
1705     /* We want to be sure that found "content-length" is the tag itself,
1706      * not a value as if in "Some-Tag: content-length: 2". So we check for
1707      * either \n before tag, or it should be positioned at the very
1708      * beginning of the string
1709      */
1710     content_length_pos = strstr(temp_header, "content-length: ");
1711     if (
1712             content_length_pos != NULL
1713             &&
1714             (
1715                 (*(content_length_pos - 1) == '\n')
1716                 ||
1717                 (content_length_pos == temp_header)
1718             )
1719         )
1720     {
1721         sscanf(content_length_pos + strlen("content-length: "),
1722                "%u", &content_length);
1723     }
1724     /* If we could not read "content-length", we do not have any
1725      * estimation of the upper bound  */
1726     else {
1727         content_length = (unsigned int)(-1);
1728     }
1729     free(temp_header);
1730     /* check for empty document */
1731     response_output->http_response_code = status_code;
1732     response_output->http_status_mesage = status_message;
1733     response_output->content_length     = content_length;
1734     return eHTTP_HeaderSuccess;
1735 }
1736 
1737 
s_LBOS_Reset(SERV_ITER iter)1738 static void s_LBOS_Reset(SERV_ITER iter)
1739 {
1740     size_t i;
1741     SLBOS_Data* data;
1742     /*
1743      * First, we need to check if it is our iterator. It would be bad
1744      * to mess with other mapper's iterator
1745      */
1746     assert(g_LBOS_CheckIterator(iter, ELBOSIteratorCheckType_NoCheck));
1747     /*
1748      * Check passed, it is LBOS's iterator. Now we can be sure that we know
1749      * how to reset it
1750      */
1751     data = (SLBOS_Data*) iter->data;
1752     if (data != NULL) {
1753         if (data->cand) {
1754             for (i = data->pos_cand;  i < data->n_cand;  i++)
1755                 free(data->cand[i].info);
1756             free(data->cand);
1757             /*There will hardly be more than 20 candidates, so we allocate
1758              * memory for candidates straight away, no array of pointers
1759              */
1760             data->cand = (SLBOS_Candidate*)calloc(data->a_cand,
1761                                                   sizeof(SLBOS_Candidate));
1762             if (data->cand == NULL) {
1763                 CORE_LOG(eLOG_Critical, "s_LBOS_Reset: No RAM. "
1764                                         "Failed to create iterator.");
1765                 data->a_cand   = 0;
1766                 data->n_cand   = 0;
1767                 data->pos_cand = 0;
1768             }
1769 #if defined(_DEBUG)  &&  !defined(NDEBUG)
1770             /** We check both that it returns zero and does not crash accessing
1771              * allocated memory  */
1772             for (i = 0;  i < data->n_cand;  i++) {
1773                 assert(data->cand + i != NULL);
1774                 data->cand[i].info = (SSERV_Info*)malloc(sizeof(SSERV_Info));
1775                 free(data->cand[i].info);
1776                 data->cand[i].info = NULL;
1777             }
1778 #endif
1779         }
1780         data->n_cand = 0;
1781         data->pos_cand = 0;
1782     }
1783     return;
1784 }
1785 
1786 
1787 /** Not implemented in LBOS client. */
s_LBOS_Feedback(SERV_ITER a,double b,int c)1788 static int/*bool*/ s_LBOS_Feedback (SERV_ITER a, double b, int c)
1789 {
1790     return 0;
1791 }
1792 
1793 
1794 /** Not implemented in LBOS client. */
s_LBOS_Update(SERV_ITER iter,const char * text,int code)1795 static int/*bool*/ s_LBOS_Update(SERV_ITER iter, const char* text, int code)
1796 {
1797     return 1;
1798 }
1799 
1800 
s_LBOS_Close(SERV_ITER iter)1801 static void s_LBOS_Close (SERV_ITER iter)
1802 {
1803     SLBOS_Data* data;
1804     /*
1805      * First, we need to check if it is our iterator. It would be bad
1806      * to mess with other mapper's iterator
1807      */
1808     assert(g_LBOS_CheckIterator(iter, ELBOSIteratorCheckType_MustHaveData));
1809     data = (SLBOS_Data*) iter->data;
1810     if(data->n_cand > 0) {
1811         /*s_Reset() has to be called before*/
1812         s_LBOS_Reset(iter);
1813     }
1814     s_LBOS_DestroyData((SLBOS_Data*)iter->data);
1815     iter->data = NULL;
1816     return;
1817 }
1818 
1819 
1820 /** 1) If iterator is valid - find and return next server;
1821  *  2) If iterator is outdated or empty - we update it and fill it with hosts
1822  *     and go to 1)                                                          */
s_LBOS_GetNextInfo(SERV_ITER iter,HOST_INFO * host_info)1823 static SSERV_Info* s_LBOS_GetNextInfo(SERV_ITER iter, HOST_INFO* host_info)
1824 {
1825     SLBOS_Data* data;
1826     /*
1827      * We store snapshot of LBOS answer in memory. So we just go from last
1828      * known position. Position is stored in n_skip.
1829      * If we want to update list - well, we update all the list and start from
1830      * the beginning.
1831      */
1832 
1833     /* host_info is implemented only for lbsmd mapper, so we just set it to
1834      * NULL, if it even exists.
1835      */
1836     if (host_info) {
1837         *host_info = NULL; /*No host data*/
1838     }
1839 
1840     assert(iter != NULL);  /* we can do nothing if this happens */
1841 
1842     if (iter->data == NULL) {
1843         iter->data = s_LBOS_ConstructData(kInitialCandidatesCount);
1844     }
1845 
1846     data = (SLBOS_Data*)(iter->data); /** set typed variable
1847                                                      for convenience */
1848 
1849     if (data->n_cand == 0) { /**< this is possible after reset()*/
1850         g_LBOS_UnitTesting_GetLBOSFuncs()->FillCandidates(data, iter->name);
1851     }
1852     /*
1853      * If there are some servers left to show, we move iterator and show next
1854      * Otherwise, return NULL
1855      */
1856     if (data->pos_cand < data->n_cand) {
1857         data->pos_cand++;
1858         return data->cand[data->pos_cand-1].info;
1859     }  else  {
1860         return NULL;
1861     }
1862 }
1863 
1864 
1865 /** Creates iterator and fills it with found servers.
1866  * @param[in,out] iter
1867  *  Pointer to iterator. It is read and rewritten
1868  * @param[out] net_info
1869  *  Connection information.
1870  * @param[out] info
1871  *  Pointer to variable to return pointer to info about server. */
SERV_LBOS_Open(SERV_ITER iter,const SConnNetInfo * net_info,SSERV_Info ** info)1872 const SSERV_VTable* SERV_LBOS_Open( SERV_ITER            iter,
1873                                     const SConnNetInfo*  net_info,
1874                                     SSERV_Info**         info     )
1875 {
1876     SLBOS_Data* data;
1877     char* new_name = NULL; /* if we need to add dbaf */
1878     const char* orig_serv_name = iter->name; /* we may modify name with dbaf */
1879     if (!s_TurnOn()) {
1880         return NULL;
1881     }
1882     /*
1883      * First, we need to check arguments
1884      */
1885     assert(iter != NULL);  /* we can do nothing if this happens */
1886 
1887     /* Check that iter is not a mask - LBOS cannot work with masks */
1888     if (iter->ismask) {
1889         CORE_LOG(eLOG_Warning, "Mask was provided instead of service name. "
1890             "Masks are not supported in LBOS.");
1891         return NULL;
1892     }
1893 
1894     /* Check that service name is provided - otherwise there is nothing to
1895      * search for */
1896     if (iter->name == NULL) {
1897         CORE_LOG(eLOG_Warning, "\"iter->name\" is null, not able "
1898                                "to continue SERV_LBOS_Open");
1899         return NULL;
1900     }
1901 
1902     /* If dbaf is defined, we construct new service name and assign it
1903      * to iter */
1904     if ( iter->arg  &&  (strcmp(iter->arg, "dbaf") == 0)  &&  iter->val ) {
1905         size_t length = 0;
1906         new_name =
1907             g_LBOS_StringConcat(g_LBOS_StringConcat(g_LBOS_StringConcat(
1908                                 NULL, iter->name, &length),
1909                                       "/",        &length),
1910                                       iter->val,  &length);
1911         if (new_name == NULL) {
1912             CORE_LOG(eLOG_Warning, "Could not concatenate dbaf with service "
1913                                    "name, probably not enough RAM. Searching "
1914                                    "for service without dbaf");
1915         } else {
1916             iter->name = new_name;
1917         }
1918     }
1919     /*
1920      * Arguments OK, start work
1921      */
1922     if (info != NULL) {
1923         *info = NULL;
1924     }
1925     data = s_LBOS_ConstructData(kInitialCandidatesCount);
1926     if(net_info == NULL) {
1927         CORE_LOG(eLOG_Warning,
1928                  "Parameter \"net_info\" is null, creating net info. "
1929                  "Please, fix the code and provide net_info.");
1930         data->net_info = ConnNetInfo_Clone(s_EmptyNetInfo);
1931     } else {
1932         data->net_info = ConnNetInfo_Clone(net_info);
1933     }
1934     // Check if CONNECT_Init() has been run before
1935     if (g_CORE_GetRequestDtab == NULL) {
1936         CORE_LOG(eLOG_Critical,
1937                  "LBOS FAIL! Please run CONNECT_Init() prior to using LBOS!\n"
1938                  "Example:\n"
1939                  "CNcbiRegistry& config = CNcbiApplication::Instance()"
1940                  "->GetConfig();\n"
1941                  "CONNECT_Init(&config);\n"
1942                  "LBOS::Announce(...);");
1943         s_LBOS_DestroyData(data);
1944         if (iter->name != orig_serv_name) {
1945             free(new_name);
1946             iter->name = orig_serv_name;
1947         }
1948         return NULL;
1949     }
1950     const char* request_dtab = g_CORE_GetRequestDtab();
1951     if (!g_LBOS_StringIsNullOrEmpty(request_dtab)) {
1952         /* Add a semicolon to separate DTabs */
1953         ConnNetInfo_ExtendUserHeader(data->net_info, "DTab-Local: ;");
1954         ConnNetInfo_ExtendUserHeader(data->net_info, request_dtab);
1955     }
1956     g_LBOS_UnitTesting_GetLBOSFuncs()->FillCandidates(data, iter->name);
1957     /* Connect to LBOS, read what is needed, build iter, info, host_info
1958      */
1959     if (!data->n_cand) {
1960         s_LBOS_DestroyData(data);
1961         if (iter->name != orig_serv_name) {
1962             free(new_name);
1963             iter->name = orig_serv_name;
1964         }
1965         return NULL;
1966     }
1967     /* Something was found, now we can use iter */
1968 
1969     /*Just explicitly mention here to do something with it*/
1970     iter->data = data;
1971     if (iter->name != orig_serv_name) {
1972         free(new_name);
1973         iter->name = orig_serv_name;
1974     }
1975     return &s_lbos_op;
1976 }
1977 
1978 
1979 /*/////////////////////////////////////////////////////////////////////////////
1980 //                        ANNOUNCEMENT/DEANNOUCEMENT                         //
1981 /////////////////////////////////////////////////////////////////////////////*/
1982 /** For the unit testing this function is moved from LBOS_Announce
1983  *  (interception of
1984  * parameters). For full description see LBOS_Announce
1985  * @sa
1986  *  LBOS_Announce()                                                        */
1987 static
s_LBOS_Announce(const char * service,const char * version,const char * host,unsigned short port,const char * healthcheck_url,const char * meta_args,char ** lbos_answer,char ** http_status_message)1988 unsigned short s_LBOS_Announce(const char*             service,
1989                                const char*             version,
1990                                const char*             host,
1991                                unsigned short          port,
1992                                const char*             healthcheck_url,
1993 #ifdef LBOS_METADATA
1994                                const char*             meta_args,
1995 #endif /* LBOS_METADATA */
1996                                /* lbos_answer is never NULL  */
1997                                char**                  lbos_answer,
1998                                char**                  http_status_message)
1999     {
2000     char*           lbos_address;
2001     char*           lbos_addr; /* to store address of LBOS that
2002                                   did the announcement*/
2003     int             status_code;
2004     char*           status_message;
2005     SConnNetInfo*   net_info;
2006     const char*     query_format        = NULL;
2007     char*           buf                 = NULL; /* for answer from LBOS */
2008     int             parsed_symbols = 0;
2009     assert(!g_LBOS_StringIsNullOrEmpty(host));
2010 
2011     if (!s_TurnOn())
2012         return eLBOS_Disabled;
2013 
2014     lbos_address         = s_LBOS_Instance;
2015     status_code          = 0;
2016     status_message       = NULL;
2017     net_info             = ConnNetInfo_Clone(s_EmptyNetInfo);
2018     net_info->req_method = eReqMethod_Put;
2019     /* Format for announcement request. "ip" parameter is optional and
2020      * will be added separately, if provided */
2021     query_format         = "http://%s/lbos/v3/services%s?version=%s&"
2022                                                         "port=%hu&"
2023                                                         "check=%s&"
2024                                                         "ip=%s&"
2025                                                         "format=json";
2026     /*
2027      * Let's try announce
2028      */
2029     char* query;
2030     /* We do not count extra 1 byte for \0 because we still have extra
2031      * bytes because of %s placeholders in query_format */
2032     query = (char*)calloc(strlen(query_format) +
2033                           strlen(lbos_address) +
2034                           strlen(service) + strlen(version) +
2035                           5/* port */ + strlen(healthcheck_url) +
2036                           strlen(host),
2037                           sizeof(char));
2038     sprintf(query, query_format,
2039             lbos_address, service, version, port, healthcheck_url, host);
2040     if (!g_LBOS_StringIsNullOrEmpty(meta_args)) {
2041         query = g_LBOS_StringConcat(g_LBOS_StringConcat(
2042             query, "&",       NULL),
2043                    meta_args, NULL);
2044     }
2045     buf = s_LBOS_UrlReadAll(net_info, query, &status_code, &status_message);
2046     free(query);
2047     if (!g_LBOS_StringIsNullOrEmpty(buf)) {
2048         /* If this function is not able to parse LBOS output, original LBOS
2049          * response will be available to the caller. Otherwise, content of
2050          * lbos_answer will be replaced with parsed IP address of LBOS watcher
2051          * a bit later */
2052         *lbos_answer = strdup(buf);
2053     }
2054     if (http_status_message != NULL && status_message != NULL) {
2055         *http_status_message = strdup(status_message);
2056     }
2057     free(status_message);
2058     switch (status_code) {
2059     case 0:
2060         /* If no LBOS found */
2061         CORE_LOG(eLOG_Warning, "Announce failed. No LBOS found.");
2062         status_code = eLBOS_LbosNotFound;
2063         break;
2064     case eLBOS_NotFound: case eLBOS_BadRequest: case eLBOS_Server:
2065         /* If announced server has a broken healthcheck */
2066         CORE_LOGF(eLOG_Warning, ("Announce failed. "
2067                                  "LBOS returned error code %d.", status_code));
2068         break;
2069     case eLBOS_Success:
2070         /* If we announced successfully and status_code is 200,
2071          * let's extract LBOS address */
2072         lbos_addr = (char*)calloc(kMaxLineSize, sizeof(char)); /* will not be
2073                                                                 * free()'d */
2074         if (lbos_addr == NULL) {
2075             CORE_LOG(eLOG_Warning, "Failed memory allocation. Most likely, "
2076                                    "not enough RAM.");
2077             status_code = eLBOS_MemAlloc;
2078             break;
2079         }
2080         if (buf != NULL) {
2081             parsed_symbols = sscanf(buf, "{\"watcher\":\"%[^\"]\"}",
2082                                     lbos_addr);
2083         }
2084         if (parsed_symbols != 1) {
2085             CORE_LOG(eLOG_Warning, "g_LBOS_Announce: LBOS answered 200 OK, but "
2086                                    "output could not be parsed");
2087             free(lbos_addr);
2088             status_code = eLBOS_Protocol;
2089             break;
2090         }
2091         /* If announce finished with success, we parsed it to extract LBOS
2092          * ip:port. We free() original output and replace it with ip:port */
2093         free(*lbos_answer);
2094         *lbos_answer = lbos_addr;
2095         /* If we could not announce, it is really bad */
2096         break;
2097     default:
2098         CORE_LOGF(eLOG_Warning, ("Announce failed. "
2099                                  "LBOS returned error code %d. "
2100                                  "LBOS answer: %s.", status_code, buf));
2101     }
2102     /* Cleanup */
2103     free(buf);
2104     ConnNetInfo_Destroy(net_info);
2105     return status_code;
2106 }
2107 
2108 
LBOS_Announce(const char * service,const char * version,const char * host,unsigned short port,const char * healthcheck_url,const char * meta_args,char ** lbos_answer,char ** http_status_message)2109 unsigned short LBOS_Announce(const char*    service,
2110                              const char*    version,
2111                              const char*    host,
2112                              unsigned short port,
2113                              const char*    healthcheck_url,
2114 #ifdef LBOS_METADATA
2115                              const char*    meta_args,
2116 #endif /* LBOS_METADATA */
2117                              char**         lbos_answer,
2118                              char**         http_status_message)
2119 {
2120     char*           my_healthcheck_url      = NULL;
2121     char*           healthcheck_encoded     = NULL;
2122     char*           my_host                 = NULL;
2123     char*           service_encoded         = NULL;
2124     char*           version_encoded         = NULL;
2125     unsigned short  result;
2126 
2127     /*
2128      * First we check input arguments
2129      */
2130     if (s_LBOS_CheckAnnounceArgs(service, version, host, port, healthcheck_url,
2131                                  lbos_answer) == 0)
2132     {
2133         return eLBOS_InvalidArgs;
2134     }
2135     /*
2136      * Pre-assign variables
2137      */
2138     *lbos_answer = NULL;
2139     /* Check if we need to replace 0.0.0.0 with local IP, and do it if needed*/
2140     my_healthcheck_url = s_LBOS_Replace0000WithIP(healthcheck_url);
2141 
2142 	/*my_healthcheck_url = strdup(healthcheck_url); */
2143     if (my_healthcheck_url == NULL) {
2144         result = eLBOS_DNSResolve;
2145         goto clean_and_exit;
2146     }
2147     /* If host provided separately from healthcheck URL, check if we need to
2148      * replace 0.0.0.0 with local IP, and do it if needed                    */
2149     if (!g_LBOS_StringIsNullOrEmpty(host)) {
2150         my_host = s_LBOS_Replace0000WithIP(host);
2151     }
2152     else { /* If host was NOT provided, we append local IP to query,
2153            * just in case */
2154         SConnNetInfo * healthcheck_info;
2155         healthcheck_info = ConnNetInfo_Clone(s_EmptyNetInfo);
2156         healthcheck_info->host[0] = '\0'; /* to be sure that it will be
2157                                           * overridden                      */
2158         /* Save info about host */
2159         ConnNetInfo_ParseURL(healthcheck_info, my_healthcheck_url);
2160         my_host = strdup(healthcheck_info->host);
2161         /* If we could not parse healthcheck URL, throw "Invalid Arguments" */
2162         if (g_LBOS_StringIsNullOrEmpty(my_host)) {
2163             ConnNetInfo_Destroy(healthcheck_info);
2164             CORE_LOG_X(eLBOS_InvalidArgs, eLOG_Critical,
2165                        "Could not parse host from healthcheck URL. Please set "
2166                        "ip of the announced server explicitly.");
2167             result = eLBOS_InvalidArgs;
2168             goto clean_and_exit;
2169         }
2170         ConnNetInfo_Destroy(healthcheck_info);
2171     }
2172 
2173     healthcheck_encoded  = s_LBOS_URLEncode(my_healthcheck_url);
2174     service_encoded      = s_LBOS_ModifyServiceName(service);
2175     version_encoded      = s_LBOS_URLEncode(version);
2176 
2177     /* Announce */
2178     result =
2179             g_LBOS_UnitTesting_GetLBOSFuncs()->AnnounceEx(service_encoded,
2180                                                           version_encoded,
2181                                                           my_host,
2182                                                           port,
2183                                                           healthcheck_encoded,
2184                                                           meta_args,
2185                                                           lbos_answer,
2186                                                           http_status_message);
2187     if (result == eLBOS_Success) {
2188         CORE_LOCK_WRITE;
2189         s_LBOS_AddAnnouncedServer(service, version, port, healthcheck_url);
2190         CORE_UNLOCK;
2191     }
2192 
2193     /* Cleanup */
2194 clean_and_exit:
2195     free(healthcheck_encoded);
2196     free(my_healthcheck_url);
2197     free(my_host);
2198     free(version_encoded);
2199     free(service_encoded);
2200     return result;
2201 }
2202 
2203 
LBOS_AnnounceFromRegistry(const char * registry_section,char ** lbos_answer,char ** http_status_message)2204 unsigned short LBOS_AnnounceFromRegistry(const char*  registry_section,
2205                                          char**       lbos_answer,
2206                                          char**       http_status_message)
2207 {
2208     unsigned short  result      = eLBOS_Success;
2209     size_t          i           = 0;
2210     unsigned int    port;
2211     char*           srvc;
2212     char*           vers;
2213     char*           port_str;
2214     char*           hlth;
2215     char*           host;
2216     char*           meta;
2217 
2218     if (g_LBOS_StringIsNullOrEmpty(registry_section)) {
2219         registry_section = kLBOSAnnouncementSection;
2220     }
2221     srvc      = g_LBOS_RegGet(registry_section, kLBOSServiceVariable, NULL);
2222     vers      = g_LBOS_RegGet(registry_section, kLBOSVersionVariable, NULL);
2223     port_str  = g_LBOS_RegGet(registry_section, kLBOSPortVariable, NULL);
2224     host      = g_LBOS_RegGet(registry_section, kLBOSServerHostVariable, NULL);
2225     hlth      = g_LBOS_RegGet(registry_section, kLBOSHealthcheckUrlVariable,
2226                               NULL);
2227     meta      = g_LBOS_RegGet(registry_section, kLBOSMetaVariable,
2228                               NULL);
2229 
2230     /* Check port that it is a number of max 5 digits and no other symbols   */
2231     for (i = 0;  i < strlen(port_str);  i++) {
2232         if (!isdigit(port_str[i])) {
2233             CORE_LOGF_X(eLBOS_InvalidArgs, eLOG_Warning,
2234                         ("Port \"%s\" in section %s is invalid", port_str,
2235                         registry_section));
2236             result = eLBOS_InvalidArgs;
2237             goto clean_and_exit;
2238         }
2239     }
2240     if (strlen(port_str) > 5 || (sscanf(port_str, "%d", &port) != 1) ||
2241         port < 1 || port > 65535)
2242     {
2243         result = eLBOS_InvalidArgs;
2244         goto clean_and_exit;
2245     }
2246 
2247     /* Announce */
2248     result = LBOS_Announce(srvc, vers, host, (unsigned short)port, hlth, meta,
2249                            lbos_answer, http_status_message);
2250     if (result == eLBOS_Success) {
2251         CORE_LOCK_WRITE;
2252         s_LBOS_AddAnnouncedServer(srvc, vers, port, hlth);
2253         CORE_UNLOCK;
2254     }
2255 
2256     /* Cleanup */
2257 clean_and_exit:
2258     free(srvc);
2259     free(vers);
2260     free(port_str);
2261     free(hlth);
2262     free(host);
2263     free(meta);
2264     return result;
2265 }
2266 
2267 /* Separated from LBOS_Deannounce to easier control memory allocated for
2268  * variables */
s_LBOS_Deannounce(const char * service,const char * version,const char * host,unsigned short port,char ** lbos_answer,char ** http_status_message,SConnNetInfo * net_info)2269 unsigned short s_LBOS_Deannounce(const char*      service,
2270                                const char*        version,
2271                                const char*        host,
2272                                unsigned short     port,
2273                                char**             lbos_answer,
2274                                char**             http_status_message,
2275                                SConnNetInfo*      net_info)
2276 {
2277     const char*    query_format;
2278     char*          lbos_address;
2279     char*          status_message = NULL;
2280     char*          buf;
2281     int            status_code;
2282     lbos_address = s_LBOS_Instance;
2283     status_code = 0;
2284     buf = NULL;
2285     /* ip */
2286     query_format = "http://%s/lbos/v3/services%s?version=%s&"
2287                                                  "port=%hu&"
2288                                                  "ip=%s";
2289     /*
2290     * Try deannounce
2291     */
2292     char* query;
2293     assert(!g_LBOS_StringIsNullOrEmpty(host));
2294     /* We do not count extra 1 byte for \0 because we still have extra
2295         * bytes because of %s placeholders */
2296     query = (char*)calloc(strlen(query_format) +
2297                           strlen(lbos_address) + strlen(service) +
2298                           strlen(version) + 5/*port*/ + strlen(host),
2299                           sizeof(char));
2300     sprintf(query, query_format,
2301             lbos_address, service, version, port, host);
2302     buf = s_LBOS_UrlReadAll(net_info, query, &status_code,
2303                             &status_message);
2304     free(query);
2305     if (lbos_answer != NULL && !g_LBOS_StringIsNullOrEmpty(buf)) {
2306         *lbos_answer = strdup(buf);
2307     }
2308     free(buf);
2309     if (http_status_message != NULL && status_message != NULL) {
2310         *http_status_message = strdup(status_message);
2311     }
2312     free(status_message);
2313 
2314     if (status_code == 0) {
2315         status_code = eLBOS_LbosNotFound;
2316     }
2317     return status_code;
2318 }
2319 
2320 
LBOS_Deannounce(const char * service,const char * version,const char * host,unsigned short port,char ** lbos_answer,char ** http_status_message)2321 unsigned short LBOS_Deannounce(const char*        service,
2322                                const char*        version,
2323                                const char*        host,
2324                                unsigned short     port,
2325                                char**             lbos_answer,
2326                                char**             http_status_message)
2327 {
2328     SConnNetInfo*   net_info;
2329     char*           service_encoded;
2330     char*           version_encoded;
2331     char*           my_host;
2332     unsigned short  retval;
2333     /*
2334      * First we check input arguments
2335      */
2336     if (s_LBOS_CheckDeannounceArgs(service, version, host, port) == 0) {
2337         return eLBOS_InvalidArgs;
2338     }
2339     /*
2340      * Check if LBOS is ON
2341      */
2342     if (!s_TurnOn()) {
2343         return eLBOS_Disabled;
2344     }
2345     /*
2346      * If we are here, arguments are good!
2347      */
2348     if (!g_LBOS_StringIsNullOrEmpty(host)) {
2349         my_host = s_LBOS_Replace0000WithIP(host);
2350     }
2351     else { /* If host was NOT provided, we append local IP to query,
2352            * just in case */
2353         my_host = s_LBOS_Replace0000WithIP("0.0.0.0");
2354         if (g_LBOS_StringIsNullOrEmpty(my_host)){
2355             CORE_LOG_X(eLBOS_DNSResolve, eLOG_Critical,
2356                        "Did not manage to get local IP address.");
2357             free(my_host);
2358             return eLBOS_DNSResolve;
2359         }
2360     }
2361     net_info             = ConnNetInfo_Clone(s_EmptyNetInfo);
2362     net_info->req_method = eReqMethod_Delete;
2363     service_encoded      = s_LBOS_ModifyServiceName(service);
2364     version_encoded      = s_LBOS_URLEncode(version);
2365 
2366     retval = s_LBOS_Deannounce(service_encoded, version_encoded,
2367                                my_host, port, lbos_answer,
2368                                http_status_message, net_info);
2369 
2370     /* If eLBOS_NotFound or eLBOS_Success - we delete server from local storage
2371     * as no longer existing */
2372     if (retval == eLBOS_NotFound || retval == eLBOS_Success) {
2373         CORE_LOCK_WRITE;
2374         s_LBOS_RemoveAnnouncedServer(service, version, port, host);
2375         CORE_UNLOCK;
2376     }
2377 
2378     /*
2379      * Cleanup
2380      */
2381     free(version_encoded);
2382     free(service_encoded);
2383     free(my_host);
2384     ConnNetInfo_Destroy(net_info);
2385 
2386     return retval;
2387 }
2388 
2389 
2390 /** Deannounce all announced servers.
2391  * @note
2392  *  Though this function is mt-safe, you should fully recognize the fact
2393  *  results of simultaneous multiple de-announcement of the same service can be
2394  *  unpredictable. For example, if service has just been de-announced in
2395  *  different thread, this thread will return error "service is not announced".
2396 
2397  * @return
2398  *  1 - all services de-announced successfully
2399  *  0 - at least one server could not be de-announced */
LBOS_DeannounceAll()2400 void LBOS_DeannounceAll()
2401 {
2402     struct SLBOS_AnnounceHandle_Tag**   arr;
2403     struct SLBOS_AnnounceHandle_Tag*    local_arr;
2404     unsigned int                        servers;
2405     unsigned int                        i;
2406 
2407     CORE_LOCK_READ;
2408     arr       = &s_LBOS_AnnouncedServers;
2409     servers   = s_LBOS_AnnouncedServersNum;
2410     local_arr =
2411               (struct SLBOS_AnnounceHandle_Tag*)calloc(servers, sizeof(**arr));
2412     if (local_arr == NULL) {
2413         CORE_LOG_X(eLBOS_MemAlloc, eLOG_Warning,
2414                    "RAM error. Cancelling deannounce all.");
2415         CORE_UNLOCK;
2416         return;
2417     }
2418     /*
2419      * Copy servers list to local variable in case other thread
2420      * deletes it or wants to announce new server which we would not want to
2421      * deannounce, since it will be announced after call of this function.
2422      */
2423     for (i = 0;  i < servers;  i++) {
2424         local_arr[i].version        = strdup((*arr)[i].version);
2425         local_arr[i].service        = strdup((*arr)[i].service);
2426         local_arr[i].port           =        (*arr)[i].port;
2427         if (strcmp((*arr)[i].host, "0.0.0.0") == 0) {
2428         /* If host is "0.0.0.0", we do not provide it to LBOS in
2429          * HTTP request (because it returns error in this case).
2430          * With no host provided, LBOS understands that we want
2431          * to deannounce server from local host, which is the same
2432          * as 0.0.0.0
2433          */
2434             local_arr[i].host       = NULL;
2435         } else {
2436             local_arr[i].host       = strdup((*arr)[i].host);
2437         }
2438     }
2439     CORE_UNLOCK;
2440     for (i = 0;  i < servers;  i++) {
2441         /* Deannounce */
2442         LBOS_Deannounce(local_arr[i].service,
2443                         local_arr[i].version,
2444                         local_arr[i].host,
2445                         local_arr[i].port,
2446                         NULL,
2447                         NULL);
2448         /* Cleanup */
2449         free(local_arr[i].version);
2450         free(local_arr[i].host);
2451         free(local_arr[i].service);
2452     }
2453     free(local_arr);
2454     return;
2455 }
2456 
2457 
2458 /*/////////////////////////////////////////////////////////////////////////////
2459 //                             LBOS CONFIGURATION                            //
2460 /////////////////////////////////////////////////////////////////////////////*/
s_LBOS_CheckConfArgs(const char * service,const char ** lbos_answer)2461 static int s_LBOS_CheckConfArgs(const char* service, const char** lbos_answer)
2462 {
2463     unsigned int i;
2464     if (g_LBOS_StringIsNullOrEmpty(service)  ||  lbos_answer == NULL) {
2465         CORE_LOG_X(eLBOS_InvalidArgs, eLOG_Warning,
2466                     "s_LBOS_CheckConfArgs: service is NULL or lbos_answer "
2467                     "is NULL");
2468         return 0;
2469     }
2470     for (i = 0;  i < strlen(service);  i++) {
2471         if (isspace(service[i])) {
2472             CORE_LOGF_X(eLBOS_InvalidArgs, eLOG_Warning,
2473                         ("s_LBOS_CheckConfArgs: service "
2474                         "\"%s\" contains invalid character", service));
2475             return 0;
2476         }
2477     }
2478     return 1;
2479 }
2480 
2481 
2482 /** This request will show currently used version for a requested service.
2483  * Current and previous version will be the same.
2484  * @param service[in]
2485  *  Name of service for which to ask default version
2486  * @param lbos_answer[out]
2487  *  Variable to be assigned pointer to C-string with LBOS answer
2488  * @param http_status_message[out]
2489  *  Variable to be assigned pointer to C-string with status message from LBOS
2490  * @return
2491  *  Status code returned by LBOS
2492  */
LBOS_ServiceVersionGet(const char * service,char ** lbos_answer,char ** http_status_message)2493 unsigned short LBOS_ServiceVersionGet(const char*  service,
2494                                       char**       lbos_answer,
2495                                       char**       http_status_message)
2496 {
2497     char*          service_encoded;
2498     const char*    query_format;
2499     char*          query;
2500     unsigned short return_code;
2501     /*
2502      * First we check input arguments
2503      */
2504     if (!s_LBOS_CheckConfArgs(service, (const char**)lbos_answer)) {
2505         return eLBOS_InvalidArgs;
2506     }
2507     /*
2508      * Check if LBOS is ON
2509      */
2510     if (!s_TurnOn()) {
2511         return eLBOS_Disabled;
2512     }
2513 
2514     /*
2515      * Arguments are good! Let's do the request
2516      */
2517     service_encoded = s_LBOS_ModifyServiceName(service);
2518     query_format = "/lbos/v3/conf%s?format=xml";
2519     query = (char*)calloc(strlen(query_format) +
2520                           strlen(service_encoded),
2521                           sizeof(char));
2522     sprintf(query, query_format, service_encoded);
2523     return_code = s_LBOS_PerformRequest(query,
2524                                         lbos_answer,
2525                                         http_status_message,
2526                                         eReqMethod_Get,
2527                                         service);
2528     /*
2529      * Cleanup
2530      */
2531     free(query);
2532     free(service_encoded);
2533     return return_code;
2534 }
2535 
2536 
2537 /** This request can be used to set new version for a service. Current and
2538  * previous versions show currently set and previously used service versions.
2539  * @param[in] service
2540  *  Name of service for which the version is going to be changed
2541  * @param new_version[out]
2542  *  Version that will be used by default for specefied service
2543  * @param lbos_answer[out]
2544  *  Variable to be assigned pointer to C-string with LBOS answer
2545  * @param http_status_message[out]
2546  *  Variable to be assigned pointer to C-string with status message from LBOS
2547  */
LBOS_ServiceVersionSet(const char * service,const char * new_version,char ** lbos_answer,char ** http_status_message)2548 unsigned short LBOS_ServiceVersionSet(const char*  service,
2549                                       const char*  new_version,
2550                                       char**       lbos_answer,
2551                                       char**       http_status_message)
2552 {
2553     char*          service_encoded;
2554     const char*    query_format;
2555     char*          query;
2556     unsigned short return_code;
2557     /*
2558      * First we check input arguments
2559      */
2560     if (!s_LBOS_CheckConfArgs(service, (const char**)lbos_answer)) {
2561         return eLBOS_InvalidArgs;
2562     }
2563     if (g_LBOS_StringIsNullOrEmpty(new_version)) {
2564         CORE_LOG_X(eLBOS_InvalidArgs, eLOG_Warning,
2565                    "LBOS_ServiceVersionSet: new_version is empty. "
2566                    "If you want to delete service config, use "
2567                    "LBOS_ServiceVersionDelete");
2568         return eLBOS_InvalidArgs;
2569     }
2570     /*
2571      * Check if LBOS is ON
2572      */
2573     if (!s_TurnOn()) {
2574         return eLBOS_Disabled;
2575     }
2576     /*
2577      * Arguments are good! Let's do the request
2578      */
2579     service_encoded = s_LBOS_ModifyServiceName(service);
2580     query_format = "/lbos/v3/conf%s?version=%s&format=xml";
2581     query = (char*)calloc(strlen(query_format) +
2582                           strlen(service_encoded) +
2583                           strlen(new_version),
2584                           sizeof(char));
2585     sprintf(query, query_format, service_encoded, new_version);
2586     return_code = s_LBOS_PerformRequest(query,
2587                                         lbos_answer,
2588                                         http_status_message,
2589                                         eReqMethod_Put,
2590                                         service);
2591     /*
2592      * Cleanup
2593      */
2594     free(service_encoded);
2595     free(query);
2596     return return_code;
2597 }
2598 
2599 /** This service can be used to remove service from configuration. Current
2600  * version will be empty. Previous version shows deleted version.
2601  */
LBOS_ServiceVersionDelete(const char * service,char ** lbos_answer,char ** http_status_message)2602 unsigned short LBOS_ServiceVersionDelete(const char*  service,
2603                                          char**       lbos_answer,
2604                                          char**       http_status_message)
2605 {
2606     char*          service_encoded;
2607     const char*    query_format;
2608     char*          query;
2609     unsigned short return_code;
2610     /*
2611      * First we check input arguments
2612      */
2613     if (!s_LBOS_CheckConfArgs(service, (const char**)lbos_answer)) {
2614         return eLBOS_InvalidArgs;
2615     }
2616     /*
2617      * Check if LBOS is ON
2618      */
2619     if (!s_TurnOn()) {
2620         return eLBOS_Disabled;
2621     }
2622     /*
2623      * Arguments are good! Let's do the request
2624      */
2625     service_encoded = s_LBOS_ModifyServiceName(service);
2626     query_format = "/lbos/v3/conf%s?format=xml";
2627     query = (char*)calloc(strlen(query_format) +
2628                           strlen(service_encoded),
2629                           sizeof(char));
2630     sprintf(query, query_format, service_encoded);
2631     return_code = s_LBOS_PerformRequest(query,
2632                                         lbos_answer,
2633                                         http_status_message,
2634                                         eReqMethod_Delete,
2635                                         service);
2636     /*
2637      * Cleanup
2638      */
2639     free(service_encoded);
2640     free(query);
2641     return return_code;
2642 }
2643 
2644 
2645 #undef NCBI_USE_ERRCODE_X
2646 
2647