bforce/source/bforce/nodelist.c
Alexey Khromov 81b326ca45
All checks were successful
Altlinux build / build-alt (push) Successful in 2m34s
Archlinux build / build-arch (push) Successful in 2m55s
Archlinux build / make-test (push) Successful in 1m0s
Debian build / build-ubuntu (push) Successful in 3m43s
Added calllist option to nlookup, fixed bunch of PVS-Studio lint warnings
2025-04-26 07:56:48 +03:00

860 lines
21 KiB
C

/*
* binkleyforce -- unix FTN mailer project
*
* Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* $Id$
*/
#define _GNU_SOURCE
#include <string.h>
#include "includes.h"
#include "confread.h"
#include "logger.h"
#include "util.h"
#include "nodelist.h"
#define NODELIST_LOCK_TRIES 3
#define NODELIST_LOCK_DELAY 1 /* seconds */
struct keyword {
const char *keystr;
const int keyval;
} keywords[] = {
{ "", KEYWORD_EMPTY },
{ "Zone", KEYWORD_ZONE },
{ "Region", KEYWORD_REGION },
{ "Host", KEYWORD_HOST },
{ "Hub", KEYWORD_HUB },
{ "Pvt", KEYWORD_PVT },
{ "Hold", KEYWORD_HOLD },
{ "Down", KEYWORD_DOWN },
{ "Boss", KEYWORD_BOSS },
{ "Point", KEYWORD_POINT },
{ NULL, 0 }
};
/*****************************************************************************
* Check flag for existing in nodelist flags string
*
* Arguments:
* nodeflags pointer to the node's nodelist flags string
* flag pointer to the flag that we want to check
*
* Return value:
* zero value if flag is presented in flags, and non-zero if not
*/
int nodelist_checkflag(const char *nodeflags, const char *flag)
{
char *p;
const char *searchbase = nodeflags;
char *q;
DEB((D_NODELIST, "nodelist: checking flag \"%s\" in flags \"%s\"", flag, nodeflags));
if ( nodeflags ) {
while( p = strcasestr(searchbase, flag) ) // p - found flag
{
if ( ((p == searchbase)) || (*(p-1) == ','))
{
if( (q = strchr(p, ',')) == NULL || (q - p) == strlen(flag) ) {
DEB((D_NODELIST, "nodelist: found flag %s", flag));
return 0;
}
if( (strchrnul(p, ':') - p) == strlen(flag) ) {
DEB((D_NODELIST, "nodelist: found flag %s", flag));
return 0;
}
}
searchbase = p + 1; // avoid finding again the same
}
}
return 1;
}
/*****************************************************************************
* Get value from flag (e.g. INA:host.domain.ru)
*
* Arguments:
* nodeflags pointer to the node's nodelist flags string
* flag pointer to the flag that we want to check
* value pointer to string buffer for result. please allocate
* max possible value of strlen(nodeflags) bytes
*
* Return value:
* zero value if flag is presented in flags, and non-zero if not
* res is zero lengh string if no flag or flag is empty
*/
int nodelist_flagvalue(const char *nodeflags, const char *flag, char *res)
{
char *p, *q;
const char *searchbase = nodeflags;
int flaglen = strlen(flag);
while( p = strstr(searchbase, flag) )
{
if( p == nodeflags || *(p-1) == ',' ) // match flag
{
if( *(p+flaglen) == 0 || *(p+flaglen) == ',' )
{
// empty flag
res[0]=0;
return 0;
}
else
{
if( *(p+flaglen) == ':' ) {
// flag has data
p += flaglen + 1; // start of data
q = strchrnul(p, ','); // end of data: comma or EOS
strncpy(res, p, q-p);
return 0;
}
}
}
searchbase = p + 1;
}
res[0] = 0;
return 1;
}
/*****************************************************************************
* Get nodelist keyword (e.g. Host, Hub, Point, etc.) value
* (e.g. KEYWORD_HOST, KEYWORD_HUB, etc.)
*
* Arguments:
* keywordval pointer to the keyword string
*
* Return value:
* One of KEYWORD_* values, and -1 for unknown keywords
*/
int nodelist_keywordval(const char *keyword)
{
int i;
for( i = 0; keywords[i].keystr; i++ )
{
if( strcmp(keyword, keywords[i].keystr) == 0 )
{
return keywords[i].keyval;
}
}
return -1;
}
int nodelist_parse_Txy(s_node *node, const char *xy)
{
long beg;
long end;
long tz;
if( xy[0] >= 'A' && xy[0] <= 'X' )
beg = (xy[0] - 'A') * 60;
else if( xy[0] >= 'a' && xy[0] <= 'x' )
beg = (xy[0] - 'a') * 60 + 30;
else
return -1;
if( xy[1] >= 'A' && xy[1] <= 'X' )
end = (xy[1] - 'A') * 60;
else if( xy[1] >= 'a' && xy[1] <= 'x' )
end = (xy[1] - 'a') * 60 + 30;
else
return -1;
if( beg == end )
return -1;
/* Convert it to local time */
if( (tz = time_gmtoffset()) )
{
beg -= tz;
if( beg > 1440 )
beg = beg - 1440;
else if( beg < 0 )
beg = beg + 1440;
else if( beg == 1440 )
beg = 0;
end -= tz;
if( end > 1440 )
end = end - 1440;
else if( end < 0 )
end = end + 1440;
else if( end == 1440 )
end = 0;
}
timevec_add(&node->worktime, DAY_MONDAY, DAY_SUNDAY, beg, end);
return 0;
}
/*****************************************************************************
* Pointlist string parser
*
* Arguments:
* node put here all obtained information
* str pointer to the nodelist string
*
* Return value:
* zero value if string was parsed successfuly, and non-zero if wasn't
*/
int nodelist_parsepoint(s_node *node, char *str)
{
char *argv[NODELIST_POSFLAGS+1];
char *p;
int cnt;
DEB((D_NODELIST,"nodelist: parsepoint %s", str));
cnt = string_parse(argv, NODELIST_POSFLAGS+1, str, ',');
if( cnt < NODELIST_POSFLAGS-1 )
return -1;
DEB((D_NODELIST,"nodelist: parsepoint OK: %d", cnt));
strnxcpy(node->name, argv[NODELIST_POSNAME], sizeof(node->name));
DEB((D_NODELIST,"nodelist: parsepoint sys: %s", node->name));
strnxcpy(node->location, argv[NODELIST_POSLOCATION], sizeof(node->location));
DEB((D_NODELIST,"nodelist: parsepoint loc: %s", node->location));
strnxcpy(node->sysop, argv[NODELIST_POSSYSOP], sizeof(node->sysop));
DEB((D_NODELIST,"nodelist: parsepoint zyz: %s", node->sysop));
strnxcpy(node->phone, argv[NODELIST_POSPHONE], sizeof(node->phone));
DEB((D_NODELIST,"nodelist: parsepoint pho: %s", node->phone));
if (argv[NODELIST_POSFLAGS])
strnxcpy(node->flags, argv[NODELIST_POSFLAGS], sizeof(node->flags));
DEB((D_NODELIST,"nodelist: parsepoint fl: %s", node->flags));
if (argv[NODELIST_POSSPEED])
node->speed = atoi(argv[NODELIST_POSSPEED]);
DEB((D_NODELIST, "nodelist: Parsed common values SYS: %s, ZYZ: %s, LOC: %s, PHONE: %s", node->name, node->sysop, node->location, node->phone));
/*
* Replace all '_' by space character
*/
string_replchar(node->name, '_', ' ');
string_replchar(node->location, '_', ' ');
string_replchar(node->sysop, '_', ' ');
node->keyword = KEYWORD_POINT;
return 0;
}
/*****************************************************************************
* Nodelist string parser
*
* Arguments:
* node put here all obtained information
* str pointer to the nodelist string
*
* Return value:
* zero value if string was parsed successfuly, and non-zero if wasn't
*/
int nodelist_parsestring(s_node *node, char *str)
{
char *argv[NODELIST_POSFLAGS+1];
char *p;
DEB((D_NODELIST,"nodelist: parsestring %s", str));
if( string_parse(argv, NODELIST_POSFLAGS+1, str, ',') != NODELIST_POSFLAGS+1 )
return -1;
if( !ISDEC(argv[NODELIST_POSNUMBER]) || !ISDEC(argv[NODELIST_POSSPEED]) )
return -1;
if( (node->keyword = nodelist_keywordval(argv[NODELIST_POSKEYWORD])) == -1 )
return -1;
if( node->addr.zone )
{
int number = atoi(argv[NODELIST_POSNUMBER]);
bool goodstr = FALSE;
switch(node->keyword) {
case KEYWORD_ZONE:
goodstr = (node->addr.zone == number);
break;
case KEYWORD_REGION:
case KEYWORD_HOST:
goodstr = (node->addr.net == number);
break;
case KEYWORD_POINT:
goodstr = (node->addr.point == number);
break;
default:
goodstr = (node->addr.node == number);
break;
}
if( !goodstr ) return -1;
}
strnxcpy(node->name, argv[NODELIST_POSNAME], sizeof(node->name));
strnxcpy(node->location, argv[NODELIST_POSLOCATION], sizeof(node->location));
strnxcpy(node->sysop, argv[NODELIST_POSSYSOP], sizeof(node->sysop));
strnxcpy(node->phone, argv[NODELIST_POSPHONE], sizeof(node->phone));
strnxcpy(node->flags, argv[NODELIST_POSFLAGS], sizeof(node->flags));
node->speed = atoi(argv[NODELIST_POSSPEED]);
DEB((D_NODELIST, "nodelist: Parsed common values SYS: %s, ZYZ: %s, LOC: %s, PHONE: %s", node->name, node->sysop, node->location, node->phone));
/*
* Replace all '_' by space character
*/
string_replchar(node->name, '_', ' ');
string_replchar(node->location, '_', ' ');
string_replchar(node->sysop, '_', ' ');
/*
* Get system work time (usefull flags: CM,Txy)
*/
if( nodelist_checkflag(node->flags, "CM") == 0 )
{
timevec_add(&node->worktime, DAY_MONDAY, DAY_SUNDAY, 0, 1440);
}
else
{
for( p = node->flags; p && *p; p = strchr(p, ',') )
{
if( p[1] == 'T' && p[2] && p[3] && (p[4] == ',' || p[4] == '\0') )
{
if( nodelist_parse_Txy(node, p+2) == -1 )
log("invalid nodelist Txy flag in \"%s\"", node->flags);
break;
}
p++;
}
/*
* Set default work time according to ZMH
*/
if( node->addr.point == 0 )
{
switch(node->addr.zone) {
case 1: nodelist_parse_Txy(node, "JK"); break;
case 2: nodelist_parse_Txy(node, "cd"); break;
case 3: nodelist_parse_Txy(node, "ST"); break;
case 4: nodelist_parse_Txy(node, "IJ"); break;
case 5: nodelist_parse_Txy(node, "BC"); break;
case 6: nodelist_parse_Txy(node, "UV"); break;
}
}
}
node->do_binkp = nodelist_checkflag(node->flags, "IBN") == 0;
node->do_ifcico = nodelist_checkflag(node->flags, "IFC") == 0;
node->do_telnet = nodelist_checkflag(node->flags, "ITN") == 0;
//TODO: Add more INA flags to array
nodelist_flagvalue(node->flags, "INA", node->host);
DEB((D_NODELIST, "nodelist: Parsed inet values IBN: %d, IFC: %d, ITN: %d, INA: %s", node->do_binkp, node->do_ifcico, node->do_telnet, node->host));
return 0;
}
/*****************************************************************************
* Hidden adresses adder
*
* Arguments:
* node a node for which we add HIDDEN lines with IpAddr
* str pointer to the flags string
*
* Return value:
* -1 if error
* zero value if string had no hidden INA:, pos - num of INA's above
* first
*/
int nodelist_parsehiddenina(s_node *node,s_override *ov) {
int rc = 0;
s_override *ovr;
s_override *ovrbase = ov;
char *tmp, *p, *q;
const char *searchbase = node->flags;
int flaglen = 3;
tmp = xmalloc(BNI_MAXHOST+1);
while( p = strstr(searchbase, "INA") )
{
if( p == node->flags || *(p-1) == ',' ) // match flag
{
if( *(p+flaglen) == 0 || *(p+flaglen) == ',' )
{
// empty flag
strncpy(tmp, p,'\0');
}
else
{
if( *(p+flaglen) == ':' ) {
// flag has data
p += flaglen + 1; // start of data
q = strchrnul(p, ','); // end of data: comma or EOS
strncpy(tmp, p, q-p);
tmp[q-p]='\0';
if (!strcmp(tmp, node->host)) {
DEB((D_NODELIST, "nodelist: found host INA %s", tmp));
} else {
DEB((D_NODELIST, "nodelist: found hidden INA %s", tmp));
rc += 1;
// let's find last override to add
while (ovrbase->hidden != NULL) ovrbase = ovrbase->hidden;
// and add new element to hiddens:
ovr = xmalloc(sizeof(s_override));
memset(ovr,'\0',sizeof(s_override));
ovr->hidden = NULL;
ovr->sIpaddr = xstrcpy(tmp);
ovr->addr = node->addr;
ovrbase->hidden = ovr;
}
}
}
}
searchbase = p + 1;
}
free(tmp);
return rc;
//
}
/*****************************************************************************
* Open nodelist, nodelist index, do some checks
*
* Arguments:
* dir pointer to the nodelist's directory
* name pointer to the nodelist file name
* mode are we going to read nodelist index? Or write?
*
* Return value:
* pointer to the allocated nodelist structure (will be used with all
* nodelist operations), and NULL to indicate errors.
*/
s_nodelist *nodelist_open(const char *dir, char *name, int mode)
{
s_nodelist tmp;
const char *openmode;
int lockmode;
char *ext; /* extension */
char *lastname;
memset(&tmp, '\0', sizeof(s_nodelist));
/*
* Select nodelist index open mode
*/
if( mode == NODELIST_READ )
{
openmode = "r";
lockmode = FILELOCK_READ;
}
else if( mode == NODELIST_WRITE )
{
openmode = "w";
lockmode = FILELOCK_WRITE;
}
else
ASSERT(0);
/*
* Get nodelist and nodelist index file names
*/
if( *name == DIRSEPCHR )
/* if nodelist name contains full path */
{
logerr("nodelist.c: you shold specify only filename, without path");
exit(255);
}
/* if only just a filename */
else
{
/* checking, if nodelist name contains mask (see
* example config for details) */
/* If nodelist extension == "999" then we assume that it
* is a mask, and try to find the latest nodelist for
* this mask. If we find it, then we change "name"
* parameter.
* If not, we do logerr
*
* TODO: as in qico, do mask-search not case-sentesive
*/
if( strcmp(name+strlen(name)-4, ".999") == 0 )
{
char tmpseek[MAX_NAME];
char tmpseekdir[MAX_NAME];
char tmpname[MAX_NAME];
struct stat ndfile;
time_t lasttime = 0;
struct dirent *ndir;
DIR *ndirstream = NULL;
if( (ndirstream = opendir(dir)) == NULL )
{
log("error opening nodelist directory: %s", dir);
return NULL;
}
else
{
strnxcpy(tmpname, name, sizeof(tmpname));
while( (ndir = readdir(ndirstream)) )
{
strnxcpy(tmpseek, ndir->d_name, sizeof(tmpseek));
if ( strlen(tmpseek) < 3 ) /* Checking for */
continue; /* "." and ".." */
if( strlen(tmpseek) == strlen(tmpname) )
{
if( (strncmp(tmpseek, tmpname, strlen(tmpseek)-3 ) == 0) )
{
strnxcpy(tmpseekdir, dir, sizeof(tmpseekdir));
strnxcat(tmpseekdir, tmpseek, sizeof(tmpseekdir));
if( (stat(tmpseekdir, &ndfile)) == 0 )
{
if( ndfile.st_ctime > lasttime )
{
lasttime = ndfile.st_ctime;
strcpy(name, tmpseek);
}
}
}
}
}
closedir(ndirstream);
}
}
if( strcmp(name+strlen(name)-4, ".999") == 0 )
/* we haven`t found any nodelist for this mask */
{
logerr("No nodelist found for this mask: %s", name);
}
strnxcpy(tmp.name_nodelist, dir, sizeof(tmp.name_nodelist));
strnxcat(tmp.name_nodelist, name, sizeof(tmp.name_nodelist));
strnxcpy(tmp.name_index, tmp.name_nodelist, sizeof(tmp.name_index));
}
strnxcat(tmp.name_index, ".index", sizeof(tmp.name_index));
DEB((D_NODELIST, "nodelist_open: nodelist name = \"%s\"", tmp.name_nodelist));
DEB((D_NODELIST, "nodelist_open: nodelist index = \"%s\"", tmp.name_index));
DEB((D_NODELIST, "nodelist_open: open mode = \"%s\"", openmode));
/*
* Try to open and lock nodelist
*/
if( (tmp.fp_nodelist = file_open(tmp.name_nodelist, "r")) == NULL )
{
logerr("cannot open nodelist \"%s\"", tmp.name_nodelist);
return NULL;
}
/*
* Try to open and lock nodelist index
*/
if( (tmp.fp_index = file_open(tmp.name_index, openmode)) == NULL )
{
logerr("cannot open nodelist index \"%s\"", tmp.name_index);
fclose(tmp.fp_nodelist);
return NULL;
}
/*
* If we open nodelist for reading then we should check that
* nodelist index has correct header and up to date
*/
if( mode == NODELIST_READ )
{
if( nodelist_checkheader(&tmp) == -1 )
{
file_close(tmp.fp_nodelist);
file_close(tmp.fp_index);
return NULL;
}
}
return xmemcpy(&tmp, sizeof(s_nodelist));
}
/*****************************************************************************
* Check nodelist index header for valid nodelist date and size
*
* Arguments:
* nlp opened nodelist
*
* Return value:
* zero value if header is correct, and -1 if not
*/
int nodelist_checkheader(s_nodelist *nlp)
{
unsigned long nltime;
unsigned long nlsize;
struct stat nlstat;
char buffer[NODELIST_HDRSIZE];
if( fread(buffer, sizeof(buffer), 1, nlp->fp_index) != 1 )
{
logerr("cannot read header from nodelist index \"%s\"", nlp->name_index);
return -1;
}
if( fstat(fileno(nlp->fp_nodelist), &nlstat) == -1 )
{
logerr("cannot stat nodelist \"%s\"", nlp->name_nodelist);
return -1;
}
nltime = buffer_getlong(buffer + 0);
nlsize = buffer_getlong(buffer + 4);
if( (unsigned long)nlstat.st_mtime != nltime )
{
log("invalid nodelist index: incorrect nodelist date %ld (expected %ld)",
(long)nlstat.st_mtime, (long)nltime);
return -1;
}
if( (unsigned long)nlstat.st_size != nlsize )
{
log("invalid nodelist index: incorrect nodelist size %ld (expected %ld)",
(long)nlstat.st_size, (long)nlsize);
return -1;
}
return 0;
}
/*****************************************************************************
* Create/update nodelist index header. Nodelist must be opened for
* writing. Now nodelist index header contain nodelist modification
* time and nodelist size.
*
* Arguments:
* nlp opened nodelist
*
* Return value:
* zero value on success, and -1 at errors
*/
int nodelist_createheader(s_nodelist *nlp)
{
struct stat nlstat;
char hdrbuf[NODELIST_HDRSIZE];
memset(&hdrbuf, '\0', sizeof(hdrbuf));
if( fstat(fileno(nlp->fp_nodelist), &nlstat) == -1 )
{
logerr("cannot stat nodelist \"%s\"", nlp->name_nodelist);
return -1;
}
buffer_putlong(hdrbuf + 0, nlstat.st_mtime);
buffer_putlong(hdrbuf + 4, nlstat.st_size);
if( fseek(nlp->fp_index, 0L, SEEK_SET) == -1 )
{
logerr("cannot seek to zero offset of index");
return -1;
}
if( fwrite(hdrbuf, sizeof(hdrbuf), 1, nlp->fp_index) != 1 )
{
logerr("cannot write nodelist index header");
return -1;
}
return 0;
}
/*****************************************************************************
* Close nodelist and nodelist index files
*
* Arguments:
* nlp pointer to the opened nodelist
*
* Return value:
* Zero value if close was successful, and non-zero if not
*/
int nodelist_close(s_nodelist *nlp)
{
int rc = 0;
ASSERT(nlp && nlp->fp_nodelist && nlp->fp_index);
if( nlp->fp_nodelist && file_close(nlp->fp_nodelist) ) //-V0560
logerr("cannot close nodelist \"%s\"", nlp->name_nodelist);
if( nlp->fp_index && file_close(nlp->fp_index) ) //-V0560
{
logerr("cannot close nodelist index \"%s\"", nlp->name_index);
rc = 1;
}
free(nlp);
return rc;
}
int nodelist_putindex(s_nodelist *nlp, const s_bni *bni)
{
char buffer[NODELIST_ENTRYSIZE];
ASSERT(nlp && nlp->fp_index);
buffer_putint(buffer + 0, bni->zone);
buffer_putint(buffer + 2, bni->net);
buffer_putint(buffer + 4, bni->node);
buffer_putint(buffer + 6, bni->point);
buffer_putint(buffer + 8, bni->hub);
buffer_putlong(buffer + 10, bni->offset);
if( fwrite(buffer, sizeof(buffer), 1, nlp->fp_index) != 1 )
{
logerr("error writing nodelist index file \"%s\"", nlp->name_index);
return -1;
}
return 0;
}
int nodelist_findindex(s_nodelist *nlp, s_bni *bni, s_faddr addr)
{
long readitems;
char buffer[NODELIST_ENTRYSIZE * NODELIST_READAHEAD];
char *p;
if( fseek(nlp->fp_index, NODELIST_HDRSIZE, SEEK_SET) ) return -1;
while(1)
{
if( (readitems = fread(buffer, NODELIST_ENTRYSIZE, NODELIST_READAHEAD, nlp->fp_index)) > 0 )
{
for( p = buffer; readitems > 0; readitems-- )
{
if( buffer_getint(p + 0) == addr.zone
&& buffer_getint(p + 2) == addr.net
&& buffer_getint(p + 4) == addr.node
&& buffer_getint(p + 6) == addr.point )
{
bni->zone = buffer_getint(p + 0);
bni->net = buffer_getint(p + 2);
bni->node = buffer_getint(p + 4);
bni->point = buffer_getint(p + 6);
bni->hub = buffer_getint(p + 8);
bni->offset = buffer_getlong(p + 10);
return 0;
}
p += NODELIST_ENTRYSIZE;
}
} else
return -1;
}
return -1;
}
int nodelist_getstr(s_nodelist *nlp, size_t offset, char *buffer, size_t buflen)
{
ASSERT(nlp && nlp->fp_nodelist);
if( fseek(nlp->fp_nodelist, offset, SEEK_SET) == 0
&& fgets(buffer, buflen, nlp->fp_nodelist) )
{
string_chomp(buffer);
return 0;
}
return -1;
}
int nodelist_lookup_string(char *buffer, size_t buflen, s_faddr addr)
{
s_bni bni;
s_cval_entry *ptrl;
s_nodelist *nlp;
const char *ndldir = NULL;
ndldir = conf_string(cf_nodelist_directory);
/*
* Try all nodelists with matching address mask
*/
for( ptrl = conf_first(cf_nodelist); ptrl; ptrl = conf_next(ptrl) )
{
DEB((D_NODELIST,"nl_lookup_string: using %s",ptrl->d.falist.what));
if( ftn_addrcomp_mask(addr, ptrl->d.falist.addr) == 0 )
{
if( (nlp = nodelist_open(ndldir, ptrl->d.falist.what, NODELIST_READ)) )
{
int rc = nodelist_findindex(nlp, &bni, addr);
if( !rc )
rc = nodelist_getstr(nlp, bni.offset, buffer, buflen);
nodelist_close(nlp);
if( !rc )
return 0;
}
}
}
return -1;
}
int nodelist_lookup(s_node *node, s_faddr addr)
{
char buf[512];
char abuf[BF_MAXADDRSTR+1];
nodelist_initnode(node, addr);
if( nodelist_lookup_string(buf, sizeof(buf), addr) == 0 )
{
node->listed = TRUE;
if (addr.point == 0)
{
if( nodelist_parsestring(node, buf) == -1 )
{
DEB((D_NODELIST,"invalid nodelist string for address %s",
ftn_addrstr(abuf, addr)));
log("invalid nodelist string for address %s",
ftn_addrstr(abuf, addr));
}
} else {
if ( nodelist_parsepoint(node, buf) == -1 )
{
DEB((D_NODELIST,"invalid nodelist string for address %s",
ftn_addrstr(abuf, addr)));
log("invalid nodelist string for address %s",
ftn_addrstr(abuf, addr));
}
node->addr = addr;
}
return 0;
}
return -1;
}
void nodelist_initnode(s_node *node, s_faddr addr)
{
memset(node, '\0', sizeof(s_node));
node->addr = addr;
strcpy(node->name, "<none>");
strcpy(node->sysop, "<none>");
strcpy(node->location, "<none>");
strcpy(node->phone, "<none>");
}