1 /* $Id: ncbi_local.c 565391 2018-06-11 18:09:29Z lavr $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author:  Anton Lavrentiev
27  *
28  * File Description:
29  *   Low-level API to resolve NCBI service name to the server meta-address
30  *   with the use of local registry.
31  *
32  */
33 
34 #include "ncbi_ansi_ext.h"
35 #include "ncbi_comm.h"
36 #include "ncbi_lb.h"
37 #include "ncbi_local.h"
38 #include "ncbi_priv.h"
39 #include <stdlib.h>
40 
41 
42 #ifdef __cplusplus
43 extern "C" {
44 #endif /*__cplusplus*/
45     static SSERV_Info* s_GetNextInfo(SERV_ITER, HOST_INFO*);
46     static void        s_Reset      (SERV_ITER);
47     static void        s_Close      (SERV_ITER);
48 
49     static const SSERV_VTable s_op = {
50         s_GetNextInfo, 0/*Feedback*/, 0/*Update*/, s_Reset, s_Close, "LOCAL"
51     };
52 #ifdef __cplusplus
53 } /* extern "C" */
54 #endif /*__cplusplus*/
55 
56 
57 struct SLOCAL_Data {
58     SLB_Candidate* cand;
59     size_t       i_cand;
60     size_t       n_cand;
61     size_t       a_cand;
62     int/*bool*/  reset;
63 };
64 
65 
s_AddService(const SSERV_Info * info,struct SLOCAL_Data * data)66 static int/*bool*/ s_AddService(const SSERV_Info* info,
67                                 struct SLOCAL_Data* data)
68 {
69     SLB_Candidate* temp;
70     size_t n;
71 
72     if (data->a_cand <= data->n_cand) {
73         n = data->a_cand + 10;
74         temp = (SLB_Candidate*)(data->cand
75                                 ? realloc(data->cand,n * sizeof(*data->cand))
76                                 : malloc (           n * sizeof(*data->cand)));
77         if (!temp)
78             return 0/*false*/;
79         data->a_cand = n;
80         data->cand   = temp;
81     }
82 
83     n = (size_t) rand() % ++data->n_cand;
84     if (n < data->n_cand - 1) {
85         temp = data->cand + n++;
86         memmove(temp + 1, temp, (data->n_cand - n) * sizeof(*data->cand));
87     }
88     data->cand[n].info = info;
89     return 1/*true*/;
90 }
91 
92 
s_LoadSingleService(const char * name,SERV_ITER iter)93 static int/*bool*/ s_LoadSingleService(const char* name, SERV_ITER iter)
94 {
95     TSERV_Type types
96         = iter->types & (TSERV_Type)(~(fSERV_Stateless | fSERV_Firewall));
97     struct SLOCAL_Data* data = (struct SLOCAL_Data*) iter->data;
98     char key[sizeof(REG_CONN_LOCAL_SERVER) + 10];
99     int/*bool*/ ok = 0/*failed*/;
100     SSERV_Info* info;
101     int n;
102 
103     info = 0;
104     strcpy(key, REG_CONN_LOCAL_SERVER "_");
105     for (n = 0;  n <= 100;  ++n) {
106         const char* svc;
107         char buf[1024];
108 
109         if (info) {
110             free((void*) info);
111             info = 0;
112         }
113         sprintf(key + sizeof(REG_CONN_LOCAL_SERVER), "%d", n);
114         if (!(svc = ConnNetInfo_GetValue(name, key, buf, sizeof(buf), 0)))
115             continue;
116         if (!(info = SERV_ReadInfoEx
117               (svc, iter->ismask  ||  iter->reverse_dns ? name : "", 0))) {
118             continue;
119         }
120         if (iter->external  &&  (info->site & (fSERV_Local | fSERV_Private)))
121             continue;  /* external mapping for local server not allowed */
122         if (!info->host  ||  (info->site & fSERV_Private)) {
123             unsigned int localhost = SOCK_GetLocalHostAddress(eDefault);
124             if (!info->host)
125                 info->host = localhost;
126             if (!iter->ok_private  &&  (info->site & fSERV_Private)
127                 &&  info->host != localhost) {
128                 continue;  /* private server */
129             }
130         }
131         if (!iter->reverse_dns  &&  info->type != fSERV_Dns) {
132             if (types != fSERV_Any  &&  !(types & info->type))
133                 continue;  /* type doesn't match */
134             if (types == fSERV_Any  &&  info->type == fSERV_Dns)
135                 continue;  /* DNS entries have to be req'd explicitly */
136             if ((iter->types & fSERV_Stateless)&&(info->mode & fSERV_Stateful))
137                 continue;  /* skip stateful only servers */
138         }
139         if (!info->rate)
140             info->rate = LBSM_DEFAULT_RATE;
141         if (!info->time)
142             info->time = LBSM_DEFAULT_TIME;
143 
144         if (!s_AddService(info, data))
145             break;
146 
147         info = 0;
148         ok = 1/*succeeded*/;
149     }
150     if (info)
151         free((void*) info);
152 
153     return ok/*whatever*/;
154 }
155 
156 
s_LoadServices(SERV_ITER iter)157 static int/*bool*/ s_LoadServices(SERV_ITER iter)
158 {
159     int/*bool*/ ok = 0/*false*/;
160     char services[1024];
161     const char* c;
162     char* s;
163 
164     if (!iter->ismask) {
165         ok = s_LoadSingleService(iter->name, iter);
166         if (!ok  ||  !iter->reverse_dns)
167             return ok;
168     }
169     if (!(c = ConnNetInfo_GetValue(0, REG_CONN_LOCAL_SERVICES,
170                                    services, sizeof(services), 0))  ||  !*c) {
171         return ok;
172     }
173 
174     s = services;
175     ok = 0/*false*/;
176     for (s += strspn(s, " \t");  *s;  s += strspn(s, " \t")) {
177         size_t len = strcspn(s, " \t");
178         assert(len);
179         if (s[len])
180             s[len++] = '\0';
181         if (!(c = SERV_ServiceName(s)))
182             break;
183         if ((iter->reverse_dns
184              ||  (iter->ismask
185                   &&  (!*iter->name  ||  UTIL_MatchesMask(c, iter->name))))
186             &&  s_LoadSingleService(c, iter)) {
187             ok = 1/*succeeded*/;
188         }
189         free((void*) c);
190         s += len;
191     }
192 
193     return ok/*whatever*/;
194 }
195 
196 
s_Sort(const void * p1,const void * p2)197 static int s_Sort(const void* p1, const void* p2)
198 {
199     const SLB_Candidate* c1 = (const SLB_Candidate*) p1;
200     const SLB_Candidate* c2 = (const SLB_Candidate*) p2;
201     if (c1->info->type == fSERV_Dns  ||  c2->info->type == fSERV_Dns) {
202         if (c1->info->type != fSERV_Dns)
203             return -1;
204         if (c2->info->type != fSERV_Dns)
205             return  1;
206     }
207     if ((int) c1->info->type < (int) c2->info->type)
208         return -1;
209     if ((int) c1->info->type > (int) c2->info->type)
210         return  1;
211     return 0;
212 }
213 
214 
s_GetCandidate(void * user_data,size_t i)215 static SLB_Candidate* s_GetCandidate(void* user_data, size_t i)
216 {
217     struct SLOCAL_Data* data = (struct SLOCAL_Data*) user_data;
218     return i < data->i_cand ? &data->cand[i] : 0;
219 }
220 
221 
s_GetNextInfo(SERV_ITER iter,HOST_INFO * host_info)222 static SSERV_Info* s_GetNextInfo(SERV_ITER iter, HOST_INFO* host_info)
223 {
224     const TSERV_Type types = iter->types & (TSERV_Type)(~fSERV_Firewall);
225     struct SLOCAL_Data* data = (struct SLOCAL_Data*) iter->data;
226     int/*bool*/ dns_info_seen = 0/*false*/;
227     SSERV_Info* info;
228     size_t i, n;
229 
230     assert(data);
231     if (data->reset) {
232         data->reset = 0/*false*/;
233         if (!s_LoadServices(iter))
234             return 0;
235         if (data->n_cand > 1)
236             qsort(data->cand, data->n_cand, sizeof(*data->cand), s_Sort);
237     }
238 
239     i = 0;
240     data->i_cand = 0;
241     while (i < data->n_cand) {
242         /* NB all servers have been loaded in accordance with their locality */
243         info = (SSERV_Info*) data->cand[i].info;
244         if (info->rate > 0.0  ||  iter->ok_down) {
245             const char* c = SERV_NameOfInfo(info);
246             for (n = 0;  n < iter->n_skip;  n++) {
247                 const SSERV_Info* skip = iter->skip[n];
248                 const char* s = SERV_NameOfInfo(skip);
249                 if (*s) {
250                     assert(iter->ismask  ||  iter->reverse_dns);
251                     if (strcasecmp(s, c) == 0
252                         &&  ((skip->type == fSERV_Dns  &&  !skip->host)  ||
253                              SERV_EqualInfo(skip, info))) {
254                         break;
255                     }
256                 } else if (SERV_EqualInfo(skip, info))
257                     break;
258                 if (iter->reverse_dns  &&  skip->type == fSERV_Dns
259                     &&  skip->host == info->host
260                     &&  (!skip->port  ||  skip->port == info->port)) {
261                     break;
262                 }
263             }
264         } else
265             n = 0;
266         if (!iter->ismask) {
267             if (types == fSERV_Any) {
268                 if (iter->reverse_dns  &&  info->type != fSERV_Dns)
269                     dns_info_seen = 1/*true*/;
270             } else if ((types & info->type)  &&  info->type == fSERV_Dns)
271                 dns_info_seen = 1/*true*/;
272         }
273         if (n < iter->n_skip) {
274             if (i < --data->n_cand) {
275                 memmove(data->cand + i, data->cand + i + 1,
276                         (data->n_cand - i) * sizeof(*data->cand));
277             }
278             free(info);
279         } else {
280             if (types != fSERV_Any  &&  !(types & info->type))
281                 break;
282             if (types == fSERV_Any  &&  info->type == fSERV_Dns)
283                 break;
284             data->i_cand++;
285             data->cand[i].status = info->rate < 0.0 ? 0.0 : info->rate;
286             if (iter->ok_down)
287                 break;
288             i++;
289         }
290     }
291 
292     if (data->i_cand) {
293         n = LB_Select(iter, data, s_GetCandidate, 1.0);
294         info = (SSERV_Info*) data->cand[n].info;
295         if (iter->reverse_dns  &&  info->type != fSERV_Dns) {
296             dns_info_seen = 0/*false*/;
297             for (i = 0;  i < data->n_cand;  i++) {
298                 SSERV_Info* temp = (SSERV_Info*) data->cand[i].info;
299                 if (temp->type != fSERV_Dns   ||
300                     temp->host != info->host  ||  temp->port != info->port) {
301                     continue;
302                 }
303                 if (!iter->ismask)
304                     dns_info_seen = 1/*true*/;
305                 if (iter->external
306                     &&  (temp->site & (fSERV_Local | fSERV_Private))) {
307                     continue; /* external mapping req'd; local server */
308                 }
309                 if (temp->rate > 0.0  ||  iter->ok_down) {
310                     data->cand[i].status = data->cand[n].status;
311                     info = temp;
312                     n = i;
313                     break;
314                 }
315             }
316             if (i >= data->n_cand  &&  dns_info_seen)
317                 info = 0;
318         }
319 
320         if (info) {
321             info->rate  = data->cand[n].status;
322             info->time += iter->time;
323             if (n < --data->n_cand) {
324                 memmove(data->cand + n, data->cand + n + 1,
325                         (data->n_cand - n) * sizeof(*data->cand));
326             }
327         }
328     } else if (iter->last  ||  iter->n_skip  ||  !dns_info_seen) {
329         info = 0;
330     } else if ((info = SERV_CreateDnsInfo(0)) != 0)
331         info->time = NCBI_TIME_INFINITE;
332 
333     if (info  &&  host_info)
334         *host_info = 0;
335     return info;
336 }
337 
338 
s_Reset(SERV_ITER iter)339 static void s_Reset(SERV_ITER iter)
340 {
341     struct SLOCAL_Data* data = (struct SLOCAL_Data*) iter->data;
342     assert(data);
343     if (data->cand) {
344         size_t i;
345         assert(data->a_cand);
346         for (i = 0; i < data->n_cand; i++)
347             free((void*) data->cand[i].info);
348         data->n_cand = 0;
349     }
350     data->reset = 1/*true*/;
351 }
352 
353 
s_Close(SERV_ITER iter)354 static void s_Close(SERV_ITER iter)
355 {
356     struct SLOCAL_Data* data = (struct SLOCAL_Data*) iter->data;
357     /* NB: s_Reset() must have been called before */
358     assert(data &&  !data->n_cand  &&  data->reset);
359     if (data->cand) {
360         assert(data->a_cand);
361         data->a_cand = 0;
362         free(data->cand);
363         data->cand = 0;
364     }
365     iter->data = 0;
366     free(data);
367 }
368 
369 
370 /***********************************************************************
371  *  EXTERNAL
372  ***********************************************************************/
373 
374 /*ARGSUSED*/
SERV_LOCAL_Open(SERV_ITER iter,SSERV_Info ** info,HOST_INFO * u)375 const SSERV_VTable* SERV_LOCAL_Open(SERV_ITER iter,
376                                     SSERV_Info** info, HOST_INFO* u/*unused*/)
377 {
378     struct SLOCAL_Data* data;
379 
380     if (!(data = (struct SLOCAL_Data*) calloc(1, sizeof(*data))))
381         return 0;
382     iter->data = data;
383 
384     if (g_NCBI_ConnectRandomSeed == 0) {
385         g_NCBI_ConnectRandomSeed  = iter->time ^ NCBI_CONNECT_SRAND_ADDEND;
386         srand(g_NCBI_ConnectRandomSeed);
387     }
388 
389     if (!s_LoadServices(iter)) {
390         s_Reset(iter);
391         s_Close(iter);
392         return 0;
393     }
394     assert(data->n_cand);
395     if (data->n_cand > 1)
396         qsort(data->cand, data->n_cand, sizeof(*data->cand), s_Sort);
397 
398     /* call GetNextInfo subsequently if info is actually needed */
399     if (info)
400         *info = 0;
401     return &s_op;
402 }
403