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