1 /*
2 * binkleyforce -- unix FTN mailer project
3 *
4 * Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * $Id: nodelist.c,v 1.1.1.1 2004/09/09 09:52:38 kstepanenkov Exp $
12 */
13
14 #include "includes.h"
15 #include "confread.h"
16 #include "logger.h"
17 #include "util.h"
18 #include "nodelist.h"
19
20 #define NODELIST_LOCK_TRIES 3
21 #define NODELIST_LOCK_DELAY 1 /* seconds */
22
23 struct keyword {
24 const char *keystr;
25 const int keyval;
26 } keywords[] = {
27 { "", KEYWORD_EMPTY },
28 { "Zone", KEYWORD_ZONE },
29 { "Region", KEYWORD_REGION },
30 { "Host", KEYWORD_HOST },
31 { "Hub", KEYWORD_HUB },
32 { "Pvt", KEYWORD_PVT },
33 { "Hold", KEYWORD_HOLD },
34 { "Down", KEYWORD_DOWN },
35 { "Boss", KEYWORD_BOSS },
36 { "Point", KEYWORD_POINT },
37 { NULL, 0 }
38 };
39
40 /*****************************************************************************
41 * Check flag for existing in nodelist flags string
42 *
43 * Arguments:
44 * nodeflags pointer to the node's nodelist flags string
45 * flag pointer to the flag that we want to check
46 *
47 * Return value:
48 * zero value if flag is presented in flags, and non-zero if not
49 */
nodelist_checkflag(const char * nodeflags,const char * flag)50 int nodelist_checkflag(const char *nodeflags, const char *flag)
51 {
52 char *p, *q;
53
54 if( (p = strstr(nodeflags, flag)) )
55 {
56 if( p == nodeflags || *(p-1) == ',' )
57 {
58 if( (q = strchr(p, ',')) == NULL || (q - p) == strlen(flag) )
59 return 0;
60 }
61 }
62
63 return 1;
64 }
65
66 /*****************************************************************************
67 * Get nodelist keyword (e.g. Host, Hub, Point, etc.) value
68 * (e.g. KEYWORD_HOST, KEYWORD_HUB, etc.)
69 *
70 * Arguments:
71 * keywordval pointer to the keyword string
72 *
73 * Return value:
74 * One of KEYWORD_* values, and -1 for unknown keywords
75 */
nodelist_keywordval(const char * keyword)76 int nodelist_keywordval(const char *keyword)
77 {
78 int i;
79
80 for( i = 0; keywords[i].keystr; i++ )
81 {
82 if( strcmp(keyword, keywords[i].keystr) == 0 )
83 {
84 return keywords[i].keyval;
85 }
86 }
87 return -1;
88 }
89
nodelist_parse_Txy(s_node * node,const char * xy)90 int nodelist_parse_Txy(s_node *node, const char *xy)
91 {
92 long beg;
93 long end;
94 long tz;
95
96 if( xy[0] >= 'A' && xy[0] <= 'X' )
97 beg = (xy[0] - 'A') * 60;
98 else if( xy[0] >= 'a' && xy[0] <= 'x' )
99 beg = (xy[0] - 'a') * 60 + 30;
100 else
101 return -1;
102
103 if( xy[1] >= 'A' && xy[1] <= 'X' )
104 end = (xy[1] - 'A') * 60;
105 else if( xy[1] >= 'a' && xy[1] <= 'x' )
106 end = (xy[1] - 'a') * 60 + 30;
107 else
108 return -1;
109
110 if( beg == end )
111 return -1;
112
113 /* Convert it to local time */
114 if( (tz = time_gmtoffset()) )
115 {
116 beg -= tz;
117 if( beg > 1440 )
118 beg = beg - 1440;
119 else if( beg < 0 )
120 beg = beg + 1440;
121 else if( beg == 1440 )
122 beg = 0;
123
124 end -= tz;
125 if( end > 1440 )
126 end = end - 1440;
127 else if( end < 0 )
128 end = end + 1440;
129 else if( end == 1440 )
130 end = 0;
131 }
132
133 timevec_add(&node->worktime, DAY_MONDAY, DAY_SUNDAY, beg, end);
134
135 return 0;
136 }
137
138 /*****************************************************************************
139 * Nodelist string parser
140 *
141 * Arguments:
142 * node put here all obtained information
143 * str pointer to the nodelist string
144 *
145 * Return value:
146 * zero value if string was parsed successfuly, and non-zero if wasn't
147 */
nodelist_parsestring(s_node * node,char * str)148 int nodelist_parsestring(s_node *node, char *str)
149 {
150 char *argv[NODELIST_POSFLAGS+1];
151 char *p;
152
153 if( string_parse(argv, NODELIST_POSFLAGS+1, str, ',') != NODELIST_POSFLAGS+1 )
154 return -1;
155
156 if( !ISDEC(argv[NODELIST_POSNUMBER]) || !ISDEC(argv[NODELIST_POSSPEED]) )
157 return -1;
158 if( (node->keyword = nodelist_keywordval(argv[NODELIST_POSKEYWORD])) == -1 )
159 return -1;
160
161 if( node->addr.zone )
162 {
163 int number = atoi(argv[NODELIST_POSNUMBER]);
164 bool goodstr = FALSE;
165
166 switch(node->keyword) {
167 case KEYWORD_ZONE:
168 goodstr = (node->addr.zone == number);
169 break;
170 case KEYWORD_REGION:
171 case KEYWORD_HOST:
172 goodstr = (node->addr.net == number);
173 break;
174 case KEYWORD_POINT:
175 goodstr = (node->addr.point == number);
176 break;
177 default:
178 goodstr = (node->addr.node == number);
179 break;
180 }
181 if( !goodstr ) return -1;
182 }
183
184 strnxcpy(node->name, argv[NODELIST_POSNAME], sizeof(node->name));
185 strnxcpy(node->location, argv[NODELIST_POSLOCATION], sizeof(node->location));
186 strnxcpy(node->sysop, argv[NODELIST_POSSYSOP], sizeof(node->sysop));
187 strnxcpy(node->phone, argv[NODELIST_POSPHONE], sizeof(node->phone));
188 strnxcpy(node->flags, argv[NODELIST_POSFLAGS], sizeof(node->flags));
189 node->speed = atoi(argv[NODELIST_POSSPEED]);
190
191 /*
192 * Replace all '_' by space character
193 */
194 string_replchar(node->name, '_', ' ');
195 string_replchar(node->location, '_', ' ');
196 string_replchar(node->sysop, '_', ' ');
197
198 /*
199 * Get system work time (usefull flags: CM,Txy)
200 */
201 if( nodelist_checkflag(node->flags, "CM") == 0 )
202 {
203 timevec_add(&node->worktime, DAY_MONDAY, DAY_SUNDAY, 0, 1440);
204 }
205 else
206 {
207 for( p = node->flags; p && *p; p = strchr(p, ',') )
208 {
209 if( p[1] == 'T' && p[2] && p[3] && (p[4] == ',' || p[4] == '\0') )
210 {
211 if( nodelist_parse_Txy(node, p+2) == -1 )
212 bf_log("invalid nodelist Txy flag in \"%s\"", node->flags);
213 break;
214 }
215 p++;
216 }
217
218 /*
219 * Set default work time according to ZMH
220 */
221 if( node->addr.point == 0 )
222 {
223 switch(node->addr.zone) {
224 case 1: nodelist_parse_Txy(node, "JK"); break;
225 case 2: nodelist_parse_Txy(node, "cd"); break;
226 case 3: nodelist_parse_Txy(node, "ST"); break;
227 case 4: nodelist_parse_Txy(node, "IJ"); break;
228 case 5: nodelist_parse_Txy(node, "BC"); break;
229 case 6: nodelist_parse_Txy(node, "UV"); break;
230 }
231 }
232 }
233
234 return 0;
235 }
236
237 /*****************************************************************************
238 * Open nodelist, nodelist index, do some checks
239 *
240 * Arguments:
241 * dir pointer to the nodelist's directory
242 * name pointer to the nodelist file name
243 * mode are we going to read nodelist index? Or write?
244 *
245 * Return value:
246 * pointer to the allocated nodelist structure (will be used with all
247 * nodelist operations), and NULL to indicate errors.
248 */
nodelist_open(const char * dir,const char * name,int mode)249 s_nodelist *nodelist_open(const char *dir, const char *name, int mode)
250 {
251 s_nodelist tmp;
252 const char *openmode;
253 int lockmode;
254
255 memset(&tmp, '\0', sizeof(s_nodelist));
256
257 /*
258 * Select nodelist index open mode
259 */
260 if( mode == NODELIST_READ )
261 {
262 openmode = "r";
263 lockmode = FILELOCK_READ;
264 }
265 else if( mode == NODELIST_WRITE )
266 {
267 openmode = "w";
268 lockmode = FILELOCK_WRITE;
269 }
270 else
271 ASSERT(0);
272
273 /*
274 * Get nodelist and nodelist index file names
275 */
276 if( *name == DIRSEPCHR )
277 {
278 strnxcpy(tmp.name_nodelist, name, sizeof(tmp.name_nodelist));
279 strnxcpy(tmp.name_index, name, sizeof(tmp.name_index));
280 }
281 else
282 {
283 strnxcpy(tmp.name_nodelist, dir, sizeof(tmp.name_nodelist));
284 strnxcat(tmp.name_nodelist, name, sizeof(tmp.name_nodelist));
285 strnxcpy(tmp.name_index, tmp.name_nodelist, sizeof(tmp.name_index));
286 }
287 strnxcat(tmp.name_index, ".index", sizeof(tmp.name_index));
288
289 DEB((D_NODELIST, "nodelist_open: nodelist name = \"%s\"", tmp.name_nodelist));
290 DEB((D_NODELIST, "nodelist_open: nodelist index = \"%s\"", tmp.name_index));
291 DEB((D_NODELIST, "nodelist_open: open mode = \"%s\"", openmode));
292
293 /*
294 * Try to open and lock nodelist
295 */
296 if( (tmp.fp_nodelist = file_open(tmp.name_nodelist, "r")) == NULL )
297 {
298 logerr("cannot open nodelist \"%s\"", tmp.name_nodelist);
299 return NULL;
300 }
301
302 /*
303 * Try to open and lock nodelist index
304 */
305 if( (tmp.fp_index = file_open(tmp.name_index, openmode)) == NULL )
306 {
307 logerr("cannot open nodelist index \"%s\"", tmp.name_index);
308 fclose(tmp.fp_nodelist);
309 return NULL;
310 }
311
312 /*
313 * If we open nodelist for reading then we should check that
314 * nodelist index has correct header and up to date
315 */
316 if( mode == NODELIST_READ )
317 {
318 if( nodelist_checkheader(&tmp) == -1 )
319 {
320 file_close(tmp.fp_nodelist);
321 file_close(tmp.fp_index);
322 return NULL;
323 }
324 }
325
326 return xmemcpy(&tmp, sizeof(s_nodelist));
327 }
328
329 /*****************************************************************************
330 * Check nodelist index header for valid nodelist date and size
331 *
332 * Arguments:
333 * nlp opened nodelist
334 *
335 * Return value:
336 * zero value if header is correct, and -1 if not
337 */
nodelist_checkheader(s_nodelist * nlp)338 int nodelist_checkheader(s_nodelist *nlp)
339 {
340 unsigned long nltime;
341 unsigned long nlsize;
342 struct stat nlstat;
343 char buffer[NODELIST_HDRSIZE];
344
345 if( fread(buffer, sizeof(buffer), 1, nlp->fp_index) != 1 )
346 {
347 logerr("cannot read header from nodelist index \"%s\"", nlp->name_index);
348 return -1;
349 }
350
351 if( fstat(fileno(nlp->fp_nodelist), &nlstat) == -1 )
352 {
353 logerr("cannot stat nodelist \"%s\"", nlp->name_nodelist);
354 return -1;
355 }
356
357 nltime = buffer_getlong(buffer + 0);
358 nlsize = buffer_getlong(buffer + 4);
359
360 if( (unsigned long)nlstat.st_mtime != nltime )
361 {
362 bf_log("invalid nodelist index: incorrect nodelist date %ld (expected %ld)",
363 (long)nlstat.st_mtime, (long)nltime);
364 return -1;
365 }
366
367 if( (unsigned long)nlstat.st_size != nlsize )
368 {
369 bf_log("invalid nodelist index: incorrect nodelist size %ld (expected %ld)",
370 (long)nlstat.st_size, (long)nlsize);
371 return -1;
372 }
373
374 return 0;
375 }
376
377 /*****************************************************************************
378 * Create/update nodelist index header. Nodelist must be opened for
379 * writing. Now nodelist index header contain nodelist modification
380 * time and nodelist size.
381 *
382 * Arguments:
383 * nlp opened nodelist
384 *
385 * Return value:
386 * zero value on success, and -1 at errors
387 */
nodelist_createheader(s_nodelist * nlp)388 int nodelist_createheader(s_nodelist *nlp)
389 {
390 struct stat nlstat;
391 char hdrbuf[NODELIST_HDRSIZE];
392
393 memset(&hdrbuf, '\0', sizeof(hdrbuf));
394
395 if( fstat(fileno(nlp->fp_nodelist), &nlstat) == -1 )
396 {
397 logerr("cannot stat nodelist \"%s\"", nlp->name_nodelist);
398 return -1;
399 }
400
401 buffer_putlong(hdrbuf + 0, nlstat.st_mtime);
402 buffer_putlong(hdrbuf + 4, nlstat.st_size);
403
404 if( fseek(nlp->fp_index, 0L, SEEK_SET) == -1 )
405 {
406 logerr("cannot seek to zero offset of index");
407 return -1;
408 }
409
410 if( fwrite(hdrbuf, sizeof(hdrbuf), 1, nlp->fp_index) != 1 )
411 {
412 logerr("cannot write nodelist index header");
413 return -1;
414 }
415
416 return 0;
417 }
418
419 /*****************************************************************************
420 * Close nodelist and nodelist index files
421 *
422 * Arguments:
423 * nlp pointer to the opened nodelist
424 *
425 * Return value:
426 * Zero value if close was successful, and non-zero if not
427 */
nodelist_close(s_nodelist * nlp)428 int nodelist_close(s_nodelist *nlp)
429 {
430 int rc = 0;
431
432 ASSERT(nlp && nlp->fp_nodelist && nlp->fp_index);
433
434 if( nlp->fp_nodelist && file_close(nlp->fp_nodelist) )
435 logerr("cannot close nodelist \"%s\"", nlp->name_nodelist);
436
437 if( nlp->fp_index && file_close(nlp->fp_index) )
438 {
439 logerr("cannot close nodelist index \"%s\"", nlp->name_index);
440 rc = 1;
441 }
442
443 free(nlp);
444
445 return rc;
446 }
447
nodelist_putindex(s_nodelist * nlp,const s_bni * bni)448 int nodelist_putindex(s_nodelist *nlp, const s_bni *bni)
449 {
450 char buffer[NODELIST_ENTRYSIZE];
451
452 ASSERT(nlp && nlp->fp_index);
453
454 buffer_putint(buffer + 0, bni->zone);
455 buffer_putint(buffer + 2, bni->net);
456 buffer_putint(buffer + 4, bni->node);
457 buffer_putint(buffer + 6, bni->point);
458 buffer_putint(buffer + 8, bni->hub);
459 buffer_putlong(buffer + 10, bni->offset);
460
461 if( fwrite(buffer, sizeof(buffer), 1, nlp->fp_index) != 1 )
462 {
463 logerr("error writing nodelist index file \"%s\"", nlp->name_index);
464 return -1;
465 }
466
467 return 0;
468 }
469
nodelist_findindex(s_nodelist * nlp,s_bni * bni,s_faddr addr)470 int nodelist_findindex(s_nodelist *nlp, s_bni *bni, s_faddr addr)
471 {
472 long readitems;
473 char buffer[NODELIST_ENTRYSIZE * NODELIST_READAHEAD];
474 char *p;
475
476 if( fseek(nlp->fp_index, NODELIST_HDRSIZE, SEEK_SET) ) return -1;
477
478 while(1)
479 {
480 if( (readitems = fread(buffer, NODELIST_ENTRYSIZE, NODELIST_READAHEAD, nlp->fp_index)) > 0 )
481 {
482 for( p = buffer; readitems > 0; readitems-- )
483 {
484 if( buffer_getint(p + 0) == addr.zone
485 && buffer_getint(p + 2) == addr.net
486 && buffer_getint(p + 4) == addr.node
487 && buffer_getint(p + 6) == addr.point )
488 {
489 bni->zone = buffer_getint(p + 0);
490 bni->net = buffer_getint(p + 2);
491 bni->node = buffer_getint(p + 4);
492 bni->point = buffer_getint(p + 6);
493 bni->hub = buffer_getint(p + 8);
494 bni->offset = buffer_getlong(p + 10);
495 return 0;
496 }
497 p += NODELIST_ENTRYSIZE;
498 }
499 } else
500 return -1;
501 }
502
503 return -1;
504 }
505
nodelist_getstr(s_nodelist * nlp,size_t offset,char * buffer,size_t buflen)506 int nodelist_getstr(s_nodelist *nlp, size_t offset, char *buffer, size_t buflen)
507 {
508 ASSERT(nlp && nlp->fp_nodelist);
509
510 if( fseek(nlp->fp_nodelist, offset, SEEK_SET) == 0
511 && fgets(buffer, buflen, nlp->fp_nodelist) )
512 {
513 string_chomp(buffer);
514 return 0;
515 }
516
517 return -1;
518 }
519
nodelist_lookup_string(char * buffer,size_t buflen,s_faddr addr)520 int nodelist_lookup_string(char *buffer, size_t buflen, s_faddr addr)
521 {
522 s_bni bni;
523 s_cval_entry *ptrl;
524 s_nodelist *nlp;
525 const char *ndldir = NULL;
526
527 ndldir = conf_string(cf_nodelist_directory);
528
529 /*
530 * Try all nodelists with matching address mask
531 */
532 for( ptrl = conf_first(cf_nodelist); ptrl; ptrl = conf_next(ptrl) )
533 {
534 if( ftn_addrcomp_mask(addr, ptrl->d.falist.addr) == 0 )
535 {
536 if( (nlp = nodelist_open(ndldir, ptrl->d.falist.what, NODELIST_READ)) )
537 {
538 int rc = nodelist_findindex(nlp, &bni, addr);
539
540 if( !rc )
541 rc = nodelist_getstr(nlp, bni.offset, buffer, buflen);
542
543 nodelist_close(nlp);
544
545 if( !rc )
546 return 0;
547 }
548 }
549 }
550
551 return -1;
552 }
553
nodelist_lookup(s_node * node,s_faddr addr)554 int nodelist_lookup(s_node *node, s_faddr addr)
555 {
556 char buf[512];
557 char abuf[BF_MAXADDRSTR+1];
558
559 nodelist_initnode(node, addr);
560
561 if( nodelist_lookup_string(buf, sizeof(buf), addr) == 0 )
562 {
563 node->listed = TRUE;
564 if( nodelist_parsestring(node, buf) == -1 )
565 {
566 bf_log("invalid nodelist string for address %s",
567 ftn_addrstr(abuf, addr));
568 }
569 return 0;
570 }
571
572 return -1;
573 }
574
nodelist_initnode(s_node * node,s_faddr addr)575 void nodelist_initnode(s_node *node, s_faddr addr)
576 {
577 memset(node, '\0', sizeof(s_node));
578
579 node->addr = addr;
580 strcpy(node->name, "<none>");
581 strcpy(node->sysop, "<none>");
582 strcpy(node->location, "<none>");
583 strcpy(node->phone, "<none>");
584 }
585
586