#include "fido.h" #include #include "fidomem.h" #include "proto.h" char *cpystr(char *, char *); /* Message routing control. This builds the translate list, and translates message numbers for routing. open_route(tag,repeat) This build the N x N routing table for the specified schedule tag; if repeat is true, and the files are still intact from the last open_route() call, the previous tables are used; this speeds up FidoNet startup immensely. In all of the following, a node may be specified by the new longhand: ZONE:NET/NODE If ZONE or NET is not specified, then it defaults to the currently defined zone or net. ZONE n Change the default zone or net number to N. The default is NET n the current number. ACCEPT-FROM n1,n2,n3 ... Accept mail from these systems to be forwarded. When a msg is about to be sent, it is checked against the route table. If it originated here, then it is sent regardless. If not, it must be found in the accept list; this prevents turkeys (or mistakes) from forwarding mail on your phone bill. ROUTE-TO n1 n2,n3,n4 ... Route all mail for n2,n3,n4 ... to n1. N1 is assumed to be an incoming host. ALIAS-AS n1, n2,n3,n4 ... Similar to ROUTE-TO, except that it cannot be overridden by route-to or file attach status. IF-SCHEDULE tag Execute the statements starting with the next command until the next IF-SCHEDULE or END-IF statement or EOF, if the currently running event matches 'tag'. END-IF Defines the end of a block of statements. SEND-TO n1, n2, n3, ... Marks all the specified nodes as being in the current schedule. Only allowed for new format ROUTE.tag files. QUICK-SEND Same as "RUSH FIDONET" -- terminates the event early if there are no more calls to make. INTERLEAVE n Do QUICK-SEND, reawaken this event in n minutes. EXTERNAL-MAIL Only effect is to make Fido not delete the .OUT and .FLO work files at the end of the schedule. CM-ONLY Functionally similar to "send-to (nodes with #CM)". Disables all non-CM nodes. SEND-ONLY Causes Fido to not wait the usual one minute interval between outgoing calls. Assumes that the system is outgoing ONLY. RECV-ONLY Causes Fido to not send any mail. Same as the /X command line switch. Removes any routing (such as default host routing) to the list of nodes, if any. Note that though this is appears to the user as a "bit command", it is really unique, in that it has it's own flag and code. The problem is that Fido does default host routing, and that is not always what you want. DIAL-TRIES n CONNECT-TRIES n DIAL-INTERVAL n CONT-INTERVAL n RINGS n MODEM-STRING "s" DIAL-PREFIX "s" MDMTYPE n IODEV n CD-BIT n These have the same meaning as the ones in FIDO.INI The following are modifiers that can substitute for all or any node address arguments to the commands above. ALL All nodes in the list. THISNET All nodes in the default net MYNET All nodes in my net. THISZONE All nodes in the default zone MYZONE All nodes in my zone. HOSTS Means all host nodes. The routing table is an N by N matrix, implemented as a one level recursive table, where N is the number of nodes in the list. Each entry has a bunch of flags (routing controls, etc) and a "route-to" node address, that reindexes into the same list to make the matrix. By default, the table is 1:1 except for nodes not in our net. All route-to nodes are the same as the node address, except that nodes not in our zone go to zone:0/0, and nodes in our zone but not in our net go to zone:net/0. Routing controls here consist of setting routing control bits and modifying the route-to address. out_xlate() This returns the index into the node of the node to send this message to PLUS one. 0 if not to be sent, or -1 if error. If the node number doesnt translate, then there are four possibilities: (0) Wrong schedule. Not really an error, this is not the right schedule to send mail to this nmap. Returns 0. (1) Dest node not found. The destination node is not in the node list. This happens when the dest node number is not in the route list nor nodelist. out_xlate() returns -1 in this case. (2) Dest node not allowed. The originator of this msg (not us) is not an accepted nmap. This can happen if: (1) someone has a bad node list (wrong number, etc) and we ended up with a wrong message) or (2) someone sent us a msg on purpose, hoping we'd send it for them, saving THEM a toll call. out_xlate() returns -1 in this case. (3) Dest node is translatable. The dest node translated properly, ie. it was found in the route table. Returns the index into node PLUS ONE. (4) Node has no credit or not in the list For mail not originating on this system only. Applies only to messages received from another node, that need to be forwarded. If there is not enough credit left, or the node cannot be found, -1 is returned, else the destination node number. */ /* Static stuff global to router. */ #define ERROR 255 /* watch out for char vs. int ... */ #define IDLE 254 #define IGNORE 253 #define ROUTE 1 /* ROUTE-TO 3 states; */ #define ROUTE2 2 /* host, */ #define ROUTE3 3 /* nodes */ #define SCHED 4 /* IF-SCHEDULE */ #define SCHED2 5 /* keyword, */ #define NET 6 /* NET 2 states; */ #define NET2 7 /* keyword, net */ /* These are "bit" commands; they all share the same 2nd state. */ #define ACCEPT 8 /* ACCEPT-FROM 2 states; */ #define SEND 9 /* SEND-TO 2 states; */ #define PICKUP 10 /* PICKUP 2 states; */ #define POLL 11 /* POLL 2 states; */ #define HOLD 12 /* HOLD two states */ #define NOROUTE 13 /* NO-ROUTE 2 states; */ #define BITS 19 /* the 2nd state */ #define EXTMAIL 22 /* EXTERNAL-MAIL 1 state */ #define SENDONLY 23 /* SEND-ONLY one state */ #define RECVONLY 24 /* RECV-ONLY one state */ #define ZONE 25 /* ZONE n */ #define ZONE2 26 #define ZGATE 27 /* ZONEGATE n three states */ #define ZGATE2 28 /* get node address */ #define DIALTRY 29 /* DIAL-TRIES n two states */ #define DIALTRY2 30 /* get number of tries */ #define CNCTTRY 31 /* CONNECT-TRIES n two states */ #define CNCTTRY2 32 /* get number of tries */ #define MDMINI 33 /* MODEM-STRING string two states */ #define MDMINI2 34 /* get string */ #define SCHEDEND 35 /* END-IF */ #define BEGIN 36 /* BEGIN group */ #define END 37 /* END group */ #define ALIAS 38 /* ALIAS-AS 3 states */ #define ALIAS2 39 /* host */ #define ALIAS3 40 /* nodes */ #define RING1 41 /* RING n, two states */ #define RING2 42 #define DIALINT1 43 /* DIAL-INTERVAL n, two states */ #define DIALINT2 44 #define CONTINT1 45 /* CONT-INTERVAL n, two states */ #define CONTINT2 46 #define DIALPRF1 47 /* DIAL-PREFIX s, two states */ #define DIALPRF2 48 #define IODEV1 49 /* IODEV n, 2 states */ #define IODEV2 50 #define MDMTYPE1 51 /* MODEM-TYPE n, 2 states */ #define MDMTYPE2 52 #define MAXBAUD1 53 /* MAX-BAUD n, 2 states */ #define MAXBAUD2 54 #define CDBIT1 55 /* CD-BIT n, 2 states */ #define CDBIT2 56 #define MAILCOD1 57 /* MAIL-ERRORLEVEL n, 2 states */ #define MAILCOD2 58 #define FILECOD1 59 /* FILE-ERRORLEVEL n, 2 states */ #define FILECOD2 60 #define CMONLY 61 /* CM-ONLY one state */ #define FORWARD 62 /* FORWARD-TO 3 states; */ #define FORWARD2 63 /* host, */ /* #define FORWARD3 nodes (same as ROUTE-TO here) */ #define INTERLV1 64 /* CONTINUOUS 2 states */ #define INTERLV2 65 #define QKSEND 66 /* QUICK-SEND 1 state */ static struct { char *word; char state; } rct[] = { "begin",BEGIN, "end",END, "accept-from",ACCEPT, "forward-from",ACCEPT, /* Ken Ganshirt's suggestion */ "forward-to",FORWARD, "route-to",ROUTE, "alias-as",ALIAS, "if-schedule",SCHED, "end-if",SCHEDEND, "send-to",SEND, "zone",ZONE, "net",NET, "pickup",PICKUP, "poll",POLL, "no-route",NOROUTE, "send-only",SENDONLY, "recv-only",RECVONLY, "zonegate",ZGATE, "dial-tries",DIALTRY, "connect-tries",CNCTTRY, "modem-string",MDMINI, "hold",HOLD, "external-mail",EXTMAIL, "rings",RING1, "dial-interval",DIALINT1, "cont-interval",CONTINT1, "dial-prefix",DIALPRF1, "io-port",IODEV1, "cd-bit",CDBIT1, "modem-type",MDMTYPE1, "max-baud",MAXBAUD1, "file-errorlevel",FILECOD1, "mail-errorlevel",MAILCOD1, "cm-only",CMONLY, "interleave",INTERLV1, "quick-send",QKSEND, "a",IGNORE, /* sched tag A */ "b",IGNORE, /* the parser sees them */ "c",IGNORE, "d",IGNORE, "e",IGNORE, "f",IGNORE, "g",IGNORE, "h",IGNORE, "i",IGNORE, "j",IGNORE, "k",IGNORE, "l",IGNORE, "m",IGNORE, "n",IGNORE, "o",IGNORE, "p",IGNORE, "q",IGNORE, "r",IGNORE, "s",IGNORE, "t",IGNORE, "u",IGNORE, "v",IGNORE, "w",IGNORE, "",ERROR }; #define STACKMAX 5 /* how many we can stack */ static int sp; /* the stack pointer (index) */ static struct { struct _node def; /* stack frames contain this */ } sf[STACKMAX]; static struct _rptrcd rptrcd; /* we fill it in here */ static struct _node target; /* working target node */ static struct _node routedest; /* ROUTE-TO */ static unsigned dest_recd; /* for speed: recd # of routedest node */ static FLAG process; /* 1 == process statements (else ignore) */ /* Modifier word tables. */ static FLAG not; /* true if we are inverting commands */ static nr; /* 1 == "no route"; ie. set 1:1 routing */ static unsigned flags; /* accumulated bits, below. Flags gets cleared when a new keyword is found; this allows stacked arguments to all be used for one statement (ie. SEND-TO 1 2 3, MYZONE NOT MYNET) */ #define NOT 1 /* used only in the mct table */ #define ALL 2 #define MYZONE 4 #define THISZONE 8 #define MYNET 16 #define THISNET 32 #define HOSTS 64 #define FLAG_MAX 64 /* highest bit used */ static struct { /* table of modifier words */ char word[10]; /* its name, */ char bit; /* its bit pattern */ } mct[] = { "not",NOT, /* invert everything */ "all",ALL, /* all nodes */ "myzone",MYZONE, /* all in my zone */ "thiszone",THISZONE, /* all in default zone */ "mynet",MYNET, /* all in my net, */ "thisnet",THISNET, /* all in default net, */ "hosts",HOSTS, /* all hosts (z:f/0 or /-1) */ "",0 }; static char bitpat; /* NMAP_ bit pattern to set */ static int liodev; /* locally set comm. port number, or -1 */ /* Check the nodemap; if it is the correct schedule, load schedule-specific stuff and return true. */ static sched_settings(tag) char tag; { lseek(nodefile,0L,0); /* load the repeat record */ if (read(nodefile,&rptrcd,sizeof(struct _rptrcd)) != sizeof(struct _rptrcd)) return(0); /* dud file */ if (rptrcd.tag != tag) return(0); /* wrong schedule */ if (rptrcd.revision != NODEVERS) return(0); /* wrong revision */ /* Not all things are specified. rptrcd elements are all ints; < 0 means unspecified, ie. use FIDO.INI values. Values are bounded elsewhere. */ fido_settings(); /* load FIDO.INI settings */ send_only= rptrcd.bits & RPT_SO ? 1 : 0; recv_only= rptrcd.bits & RPT_RO ? 1 : 0; cm_only= rptrcd.bits & RPT_CM ? 1 : 0; ext_mail= rptrcd.bits & RPT_EXT ? 1 : 0; if (*rptrcd.mdmstr) strcpy(mdmstr,rptrcd.mdmstr); if (*rptrcd.dial_pref) strcpy(dial_pref,rptrcd.dial_pref); if (rptrcd.mailcode >= 0) mailcode=rptrcd.mailcode; if (rptrcd.filecode >= 0) filecode=rptrcd.filecode; if (rptrcd.connect_tries >= 0) connect_tries=rptrcd.connect_tries; if (rptrcd.dial_tries >= 0) dial_tries= rptrcd.dial_tries; if (rptrcd.rings >= 0) rings= rptrcd.rings; if (rptrcd.dial_interval >= 0) dial_interval= rptrcd.dial_interval; if (rptrcd.cont_interval >= 0) cont_interval= rptrcd.cont_interval; return(1); } /* Build the route table and compile the route list if it exists. */ void open_route(tag,event) char tag; int event; { int i; char bits,c,*cp; char buff[SS]; unsigned host_recd; FLAG ournet,ourzone; /* See if we can avoid recompiling the route files, by checking for the correct nodemap already on disk. NOTE: it loads the revision byte also. */ if (sched_settings(tag)) return; /* reload previous sched */ /* Different or new schedule. Clear out the structure, the route file processing will possibly fill it in. -1's allow detection of specified values. NOTE: We don't overwrite the revision byte. */ for (cp= (char *) &rptrcd.tag, i= sizeof(struct _rptrcd) - 1; i--;) *cp++= 255; /* set to all -1's */ rptrcd.bits= 0; /* except these */ *rptrcd.mdmstr= NUL; *rptrcd.dial_pref= NUL; /* First make the node map of all nodes in the system. This also sets default routing for other nets; for each node in another net it sets the route-to to zero, the host. For regions, -1 node, it does not set the host to route to. */ cprintf(SM+202); if (fbit(FBIT_FNB)) bits= NMAP_SEND | NMAP_PU; /* new default */ else bits= (tag == 'A') ? NMAP_SEND : 0; /* old default */ host_recd= -1; /* no host yet */ i= 0; /* preincrement ... */ while (1) { if (!(i & 31)) /* every 32 only */ if (evtabort()) return; /* (for speed) */ ++i; if (get_node(i) == -1) break; /* end of the table */ nmap.bits= bits; /* set the bits, */ /* If the net number changes, remember the zone:net/node for possible routing. If it is a different zone, then we route even for regions, because they are in a different region: DEST NODE ROUTE-TO NODE hosts (example: our zone == 1) (example: our net == 100) 1:100/222 1:100/222 in our zone/net 1:333/1 1:333/0 not our net 3:211/4 3:0/0 not our zone regions (example: our region == 20) 1:20/3 1:20/3 our region 1:30/5 1:30/5 still a region! 3:29/17 3:0/0 not our zone */ /* Remember the record number of the most recent host; outside our zone, we only remember zone hosts. */ if (is_host(&nmap.node)) { ournet= same_net(&id,&nmap.node) || same_net(&altid,&nmap.node); ourzone= same_zone(&id,&nmap.node) || same_zone(&altid,&nmap.node); if (!ourzone) { /* to another zone */ if (is_zonehost(&nmap.node)) /* remember Z:0/0 */ host_recd= i; /* else leave as last-set (zone) host */ } else if (!ournet) { /* to a net in our zone */ if (is_nethost(&nmap.node)) /* remember Z:N/0 */ host_recd= i; else host_recd= -1; /* else its a REGION host */ } else host_recd= -1; /* its our net */ } /* If a host is specified set it, else point it to itself. (Not very interesting to note is the fact that if this node is a host (see above) then we are setting a route-to; however it is itself ...) */ if (host_recd != -1) nmap.route_recd= host_recd; /* point to last host */ else nmap.route_recd= i; /* else 1:1 */ put_node(i); } /* Now process all route files. */ routefile("ROUTE.DEF",tag); /* do default routing */ sprintf(buff,0,"ROUTE.%c",tag); /* look for specific route list */ if (! routefile(buff,tag,event)) /* if it does not exist, */ routefile("ROUTE.BBS",tag,event); /* try the default */ /* Record some details on this session so a subsequent REPEAT schedule can potentially skip these route file processing steps. */ if (!evtabort()) { rptrcd.tag= tag; /* which schedule */ lseek(nodefile,0L,0); /* write to disk */ write(nodefile,&rptrcd,sizeof(struct _rptrcd)); cprintf(SM+203); } } /* Process the specified route file. Return true if it exists. */ static routefile(fn,tag,event) char *fn; char tag; /* schedule letter */ int event; /* event number */ { char line[132],atom[132]; int lineno,n,i,f,state; char c,*cp; FLAG clrbitpat; /* see comments */ if (evtabort()) return; /* check for manual abort */ makesname(line,fn); /* route file in system path */ f= open(line,0); /* try to open it, */ if (f == -1) return(0); /* doesnt exist */ stoupper(line); cprintf(SM+204,line); /* say which file */ state= IDLE; /* idle state machine */ process= 1; /* do process statements */ flags= /* clear everything */ not= /* invert-er off */ nr= /* no-route off */ bitpat= /* no bits set */ clrbitpat= 0; sp= 0; /* stack is empty */ cpy_node(&sf[sp].def,&id); /* default to us */ lineno= 0; /* for error reports */ while (rline(f,line,sizeof(line))) { if (evtabort()) break; /* manually aborted */ ++lineno; cprintf(0," %2d: ",lineno); /* pretty listing */ for (n= 0, cp= line; *cp; ++cp) { /* parser prints "*" if active */ if (*cp == TAB) { /* expand tabs */ do lconout(' '); while (++n % 8); } else { /* everything else */ lconout(*cp); /* as-is */ ++n; } } cprintf(0,"\r"); /* note CR only */ for (cp= line; *cp; ++cp) { /* delete comments */ if (*cp == ';') { /* by marking */ *cp= NUL; /* as the end of the line */ break; } } cp= skip_delim(line); while (num_args(cp)) { /* String arguments get processed only minimally; they do not affect the current state nor do they get any of the processing other arguements do. (Yes it is possible to generate silly arguments.) */ if (*cp == '"') { /* if a quoted string, */ cp= cpystr(atom,cp); /* copy it, */ goto execute; /* skip other processing */ } cpyatm(atom,cp); /* this atom, */ cp= next_arg(cp); /* for next time through */ stolower(atom); /* make it pretty, */ cpy_node(&target,&sf[sp].def); /* target is current operand */ set_nn(atom,&target); /* maybe */ /* First check for the special argument words; these set bits, and are arguments to command words. These do not change state, so we have to check for them first. If we find one, execute the currently set state. (Which hopefully will be able to use the bits just set.) */ for (i= 0; *mct[i].word; i++) { if (same(atom,mct[i].word)) { flags |= mct[i].bit; clrbitpat= 1; /* see below */ break; } } /* If "NOT" just found, clear the macro bits and then wait for the next keyword. For example: HOLD ALL NOT OURNET. HOLD ALL executes, and 'flags' still has ALL set. NOT is found; the flags are cleared and we get the next macro, OURNET. dobits() then executes the NOT OURNET. If we did not clear the ACTIVE flags after a NOT, it would do HOLD NOT ALL NOT OURNET. */ if (mct[i].bit == NOT) { not= 1; /* set NOT */ flags= 0; /* clear the flags, */ clrbitpat= 1; /* see below */ continue; } /* If its not a modifier word, then its either a digit (address?) or a command word. If its suspected of being a command word, look it up. This is what changes state. Numbers never change state. If we get a command that says "IGNORE", do just that; do not treat as a state change. This lets us ignore totally "words" such as the schedule tags. Note that state BITS lets bits accumulate until we get a number or modifier, at which point they are all applied. (Kludge for performance.) There is an important special case of all this bit-cacheing biz: send-to, hold all hold not ournet ...does not work. It does "(send-to hold) not ournet" because since the state doesn't change (BITS becomes BITS) bitpat is never cleared. So, whenever a modifier (OURNET, ALL, etc), NOT, or a number is found, we set the flag clrbitpat so that the next keyword parse clears bitpat. Sheesh. */ if (!*mct[i].word && !isdigit(*atom)) { not= flags= 0; /* clear flags */ /* We clear bitpat (POLL, PICKUP, etc) and nr (NO-ROUTE) before every command *except* when the previous command was one of the "bit" commands; in this case, we allow them to stack up and be executed all at once. */ if (state != BITS) clrbitpat= 1; for (i= 0; *rct[i].word; i++) { if (same(atom,rct[i].word)) break; } if (*rct[i].word) { /* if we matched it */ if (rct[i].state != IGNORE) state= rct[i].state; } else state= ERROR; /* new state, else error */ /* At this point, before getting more args/executing the new command, we clear bitpat and nr, unless the criteria above was all satisfied. */ if (clrbitpat) bitpat= nr= clrbitpat= 0; } execute:; i= 0; if (isdigit(*atom)) { i= atoi(atom); /* in case its a number */ clrbitpat= 1; } /* We process IF-SCHEDULE and SCHED-END statements specially; if 'process' is off we ignore all other statements; these two commands therefore control the execution of all other commands. */ if (! process) { /* if turned off, */ if ((state != SCHED) && /* allow only these */ (state != SCHED2) && /* states to run */ (state != SCHEDEND)) { continue; } } /* We switch states when we find a keyword, but only start executing states until the thing following a keyword. Ie. after SEND-TO we have to wait for a number. */ switch (state) { case IDLE: break; case IGNORE: break; case QKSEND: rptrcd.bits |= RPT_RUSH; set_rush(event); break; case INTERLV1: state= INTERLV2; case INTERLV2: set_cont(event); rptrcd.bits |= RPT_CONT; rptrcd.cont_interval= i; break; case BEGIN: if (++sp >= STACKMAX) { clprintf(SM+205,lineno); sp= STACKMAX - 1; } else cpy_node(&sf[sp].def,&sf[sp - 1].def); break; case END: if (--sp < 0) { clprintf(SM+206,lineno); sp= 0; } break; case ERROR: clprintf(SM+207,atom,lineno,fn); /* "unknown word" */ break; case SCHED: process= 0; state= SCHED2; break; case SCHED2: process= (*atom == tolower(tag)); state= IDLE; break; case SCHEDEND: process= 1; state= IDLE; break; case EXTMAIL: rptrcd.bits |= RPT_EXT; break; case SENDONLY: rptrcd.bits |= RPT_SO; break; case RECVONLY: rptrcd.bits |= RPT_RO; break; case CMONLY: rptrcd.bits |= RPT_CM; break; /* These are the bit commands; they can be stacked one after another until a modifier keyword is found, at which point they all get done at once. */ case SEND: bitpat |= NMAP_SEND; state= BITS; break; case HOLD: bitpat |= NMAP_HOLD; state= BITS; break; case PICKUP: bitpat |= NMAP_PU; state= BITS; break; case ACCEPT: bitpat |= NMAP_ACCEPT; state= BITS; break; case POLL: bitpat |= (NMAP_POLL | NMAP_PU); nr= 1; state= BITS; break; case NOROUTE: nr= 1; state= BITS; break; case BITS: if (bitpat) dobits(); if (nr) doroute(); break; case DIALTRY: state= DIALTRY2; break; case DIALTRY2: rptrcd.dial_tries= i; break; case CNCTTRY: state= CNCTTRY2; break; case CNCTTRY2: rptrcd.connect_tries= i; break; case MDMINI: state= MDMINI2; break; case MDMINI2: stoupper(atom); strcat(atom,"\r"); strcpy(rptrcd.mdmstr,atom); break; case RING1: state= RING2; break; case RING2: rptrcd.rings= i; break; case DIALINT1: state= DIALINT2; break; case DIALINT2: rptrcd.dial_interval= i; break; case CONTINT1: state= CONTINT2; break; case CONTINT2: rptrcd.cont_interval= i; break; case DIALPRF1: state= DIALPRF2; break; case DIALPRF2: stoupper(atom); strcpy(rptrcd.dial_pref,atom); break; case MDMTYPE1: state= MDMTYPE2; break; case MDMTYPE2: rptrcd.mdmtype= i; break; case MAXBAUD1: state= MAXBAUD2; break; case MAXBAUD2: rptrcd.maxbaud= i; break; case CDBIT1: state= CDBIT2; break; case CDBIT2: rptrcd.cd_bit= i; break; case IODEV1: state= IODEV2; break; case IODEV2: rptrcd.iodev= i - 1; break; /* local copy */ case MAILCOD1: state= MAILCOD2; break; case MAILCOD2: if ((i > 0) && ((i < 3) || (i > 255))) clprintf(SM+151);/* "must be 3 - 255" */ else rptrcd.mailcode= i; break; case FILECOD1: state= FILECOD2; break; case FILECOD2: if ((i > 0) && ((i < 3) || (i > 255))) clprintf(SM+151);/* "must be 3 - 255" */ else rptrcd.filecode= i; break; case ZONE: state= ZONE2; break; case ZONE2: sf[sp].def.zone= i; break; case NET: state= NET2; break; case NET2: sf[sp].def.net= i; break; case ZGATE: state= ZGATE2; break; case ZGATE2: flags= THISZONE; /* assumes prev. ZONE statement */ cpy_node(&routedest,&target); /* we route-to this */ dest_recd= find_node(&routedest); /* pre-locate it */ if (dest_recd == -1) { clprintf(SM+208,str_node(&routedest),lineno,fn); state= IDLE; } else doroute(); break; case ALIAS: state= ALIAS2; break; case ROUTE: state= ROUTE2; break; case FORWARD: state= FORWARD2; break; case ALIAS2: case ROUTE2: /* get */ dest_recd= find_node(&target); if (dest_recd == -1) { /* (missing dest node) */ clprintf(SM+208,str_node(&target),lineno,fn); state= IDLE; break; } cpy_node(&routedest,&target); /* remember node to route to */ if (state == ALIAS2) state= ALIAS3; else state= ROUTE3; break; /* FORWARD-TO is equiv. to ROUTE-TO ... once the SEND-TO bit is set on the node. */ case FORWARD2: dest_recd= find_node(&target); if (dest_recd == -1) { /* (missing dest node) */ clprintf(SM+208,str_node(&target),lineno,fn); state= IDLE; break; } cpy_node(&routedest,&target); /* remember node to route to */ bitpat= NMAP_SEND; dobits(); /* set SEND on */ state= ROUTE3; break; /* For ALIAS-AS, we have to make sure we have bitpat set, because doroute() will not modify a record with the ALIAS bit set, unless the ALIAS bit is also set in bitpat! (A kludge to prevent ROUTE-TO and NO-ROUTE etc from changing an ALIAS-AS.) */ case ALIAS3: bitpat= NMAP_ALIAS; dobits(); /* fall through */ case ROUTE3: doroute(); break; } } cprintf(0," %c\r\n",process ? '*' : '-'); /* flag the line */ } close(f); return(1); } /* Copy a quoted string. */ static char * cpystr(dp,sp) char *dp; /* dest string */ char *sp; /* source string */ { char lastc,c,q; if (*sp == '"') q= *sp++; else q= NUL; /* optional quote stripping */ lastc= NUL; while (c= *sp) { ++sp; /* next ... */ if ((c == q) && (lastc != '\\')) /* if the quote char & not quoted */ break; /* end of argument */ if (!q && delim(c)) break; /* else stop if a delimiter */ *dp++= c; /* else part of same arg */ lastc= c; /* remember last char */ } *dp= NUL; return(sp); /* points to next ... */ } /* Using the various flags, set or clear the bits in the specified node entries: n,m node and net, unless ALL, THISNET or MYNET all 1 == do all nodes mynet 1 == all nodes in my net thisnet 1 == all nodes in current net only hosts 1 == do all net/ region/ hosts not 1 == do the opposite bitpat bit pattern to add */ static dobits() { int b,n,i; struct _node d; /* If no flag bits are set, it must have been an explicit node address. */ if (!flags) { i= find_node(&target); if (i != -1) { if (not) nmap.bits &= ~bitpat; else nmap.bits |= bitpat; put_node(i); } else clprintf(SM+210,str_node(&target)); } else for (b= 1; b <= FLAG_MAX; b <<= 1) switch (flags & b) { case ALL: for (i= 1; get_node(i) != -1; i++) { if (not) nmap.bits &= ~bitpat; else nmap.bits |= bitpat; put_node(i); } break; case THISNET: cpy_node(&d,&sf[sp].def); /* make default net/0 */ d.number= 0; /* find the host first */ i= find_node(&d); /* faster searching */ if (i == -1) { clprintf(SM+211,str_node(&d)); break; } while (get_node(i) != -1) { /* if host found, */ if (! same_net(&nmap.node,&d)) break; if (not) nmap.bits &= ~bitpat; else nmap.bits |= bitpat; put_node(i); ++i; } break; case MYNET: cpy_node(&d,&id); /* make my net/0 */ d.number= 0; /* find the host first */ i= find_node(&d); /* faster searching */ if (i == -1) { clprintf(SM+211,str_node(&d)); break; } while (get_node(i) != -1) { if (! same_net(&nmap.node,&id)) break; if (not) nmap.bits &= ~bitpat; else nmap.bits |= bitpat; put_node(i); ++i; } break; case THISZONE: cpy_node(&d,&sf[sp].def); /* make default zone:0/0 */ d.net= d.number= 0; /* find the host first */ i= find_node(&d); /* faster searching */ if (i == -1) { clprintf(SM+213,str_node(&d)); break; } while (get_node(i) != -1) { if (! same_zone(&nmap.node,&d)) break; if (not) nmap.bits &= ~bitpat; else nmap.bits |= bitpat; put_node(i); ++i; } break; case MYZONE: cpy_node(&d,&id); /* make myzone:0/0 */ d.net= d.number= 0; /* find the host first */ i= find_node(&d); /* faster searching */ if (i == -1) { clprintf(SM+213,str_node(&target)); break; } while (get_node(i) != -1) { if (! same_zone(&nmap.node,&id)) break; if (not) nmap.bits &= ~bitpat; else nmap.bits |= bitpat; put_node(i); ++i; } break; case HOSTS: for (i= 1; get_node(i) != -1; i++) { if (is_host(&nmap.node)) { if (not) nmap.bits &= ~bitpat; else nmap.bits |= bitpat; put_node(i); } } break; } } /* Do routing for the specified nodes: host.node host to route-to host.net ditto n,m node,net unless ALL, THISNET or MYNET all do all nodes mynet all nodes in my net thisnet all nodes in current net only hosts all hosts not do the opposite What this really does is truly awful: "route-to ALL NOT THISNET" actually makes two passes: first it does "route-to ALL" and then does "route-to NOT thisnet", where NOT means un-do any routing applied. Will not modify the routing on any record marked ALIAS unless the to-change bits in 'bitpat' is also set to ALIAS; this prevents an ALIASed node from being changed by ROUTE, etc. */ static doroute() { int b,i; struct _node d; if (!flags) { /* if no flags set, */ i= find_node(&target); /* must be explicit */ if (i != -1) { if (set_route(i)) put_node(i); } else clprintf(SM+215,str_node(&target)); } else for (b= 1; b <= FLAG_MAX; b <<= 1) switch (flags & b) { case ALL: for (i= 1; get_node(i) != -1; i++) { if (set_route(i)) put_node(i); } break; case THISNET: cpy_node(&d,&sf[sp].def); /* make default net/0 */ d.number= 0; /* find the host first */ i= find_node(&d); /* faster searching */ if (i == -1) { clprintf(SM+211,str_node(&d)); break; } while (get_node(i) != -1) { if (! same_net(&nmap.node,&d)) break; if (set_route(i)) put_node(i); ++i; } break; case MYNET: cpy_node(&d,&id); /* make my net/0 */ d.number= 0; /* find the host first */ i= find_node(&d); /* faster searching */ if (i == -1) { clprintf(SM+211,str_node(&d)); break; } while (get_node(i) != -1) { if (! same_net(&nmap.node,&d)) break; if (set_route(i)) put_node(i); ++i; } break; case THISZONE: cpy_node(&d,&sf[sp].def); /* make default zone:0/0 */ d.net= d.number= 0; /* find the host first */ i= find_node(&d); /* faster searching */ if (i == -1) { clprintf(SM+213,str_node(&d)); break; } while (get_node(i) != -1) { if (! same_zone(&nmap.node,&d)) break; if (set_route(i)) put_node(i); ++i; } break; case MYZONE: cpy_node(&d,&id); /* make my net/0 */ d.net= d.number= 0; /* find the host first */ i= find_node(&d); /* faster searching */ if (i == -1) { clprintf(SM+213,str_node(&d)); break; } while (get_node(i) != -1) { if (! same_zone(&nmap.node,&d)) break; if (set_route(i)) put_node(i); ++i; } break; case HOSTS: for (i= 1; get_node(i) != -1; i++) { if (is_host(&nmap.node)) { if (set_route(i)) put_node(i); } } break; } } /* Mark the destination of this nmap entry to either the specified destination (routedest) or its original destination. (ie. NOT). Returns true if the record was changed, else 0. (ie. we can avoid writing it out if unchanged.) */ static set_route(r) int r; /* this nmap's recd number */ { struct _node d; int n; if (nmap.bits & NMAP_ALIAS) { /* if recd is ALIASed */ if (! (bitpat & NMAP_ALIAS)) return(0); /* do not change */ } /* unless another ALIAS */ if (nr) { /* absolutely */ n= r; /* no routing */ } else if (not) { cpy_node(&d,&nmap.node); /* un-do routing */ if (! same_zone(&nmap.node,&sf[sp].def)) {/* if not our zone, */ d.net= 0; /* route to zone host */ d.number= 0; } else if (!same_net(&nmap.node,&sf[sp].def)) {/* if not our net, */ d.number= 0; /* route to its own host */ } n= find_node(&d); /* find recd # of route-to */ if (n != r) get_node(r); /* restore orig. recd */ } else n= dest_recd; /* else do routing */ if (nmap.route_recd == n) return(0); /* hasnt changed */ nmap.route_recd= n; /* set it */ return(1); } /* This function does the high-level routing for the FidoNet routines. Given the source node in msg_orig, and the destination node in msg_dest, performs the necessary lookups in the nodemap to determine where (if anywhere) this node is going. Returns: 0 if not to be sent (not an error; wrong schedule, etc); -1 if error (not in the nodelist, msg_orig not marked ACCEPT-FROM etc); or the nodemap index PLUS ONE of the node the message is to be packeted for, and struct nmap loaded with the nodelist record for the dest node. (Return values run 1 - N; 0 == no send, -1 == error). */ int out_xlate() { int i; /* recd number of node to packet for */ int bits; if (is_us(&msg_dest)) /* if its To: us, */ return(0); /* ignore it */ i= route(); /* do routing/lookup, */ if (i == -1) return(-1); /* (node not in nodelist) */ /* struct nmap now contains the destination node info; 'i' is its index into the nodelist. Check the status of the destination node next. */ if (! (nmap.bits & NMAP_SEND)) /* dont send (but no error) */ return(0); /* if not enabled (not SEND-TO) */ /* (NOT IMPLEMENTED YET IN MAKELIST.EXE) */ if (cm_only && !(nmap.bits & NMAP_CM)) /* CM-ONLY and not a CM node */ return(0); /* (same as not "SEND-TO") */ /* Now check the source node; if it is not us, make sure it's marked ACCEPT-FROM. */ if (is_us(&msg_orig)) /* if from us, its OK */ return(i + 1); if (find_node(&msg_orig) == -1) /* find source node */ return(-1); /* (missing from nodelist) */ bits= nmap.bits; /* copy of bits from source node */ get_node(i); /* (re-load dest node recd) */ fix_node(&nmap.node); /* logicalize (sic) node numbers */ return( (bits & NMAP_ACCEPT) ? i + 1 : -1); /* check ACCEPT-FROM */ } /* Check the routing for this message and return the record number of the destination node, or -1 if error. */ static route() { int i; i= find_node(&msg_dest); /* locate the logical dest. */ if (i == -1) return(i); /* (no such node) */ if (msg.attr & (MSGFILE | MSGFREQ)) { /* file attach/request: no routing */ if (! (msg.attr & MSGLOCAL) || /* must be local and from us */ !is_us(&msg_orig)) return(-1);/* else its bad */ } else { /* not a file attach/request */ i= get_node(nmap.route_recd); /* route to host */ if (i == -1) return(-1); /* (no such node) */ } if (nmap.bits & NMAP_ALIAS) { /* if this node aliased */ i= get_node(nmap.route_recd); /* route to indicated node */ } fix_node(&nmap.node); /* logicalize (sic) node numbers */ return(i); /* record number or -1 */ }