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