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