mmdf/ 775 0 12 0 3671117344 4676 mmdf/h/ 755 0 12 0 3664425352 5126 mmdf/h/ap.h 444 0 12 6215 3655234577 5775 /* environment for address parser */ #define AP_SAME 0000 /* do not transorm the address */ #define AP_733 0001 /* follow RFC #733 rules */ #define AP_822 0002 /* follow RFC #822 rules */ #define AP_NODOTS 0004 /* strip down to hostname on next hop */ #define AP_BIG 0010 /* Use Big-endian domains, FLAG */ struct ap_node { char ap_obtype, /* parsing type of this object */ #define APV_NIL 0 #define APV_NAME 1 /* personal name */ #define APV_MBOX 2 /* mailbox-part of address */ #define APV_DOMN 3 /* host-part of address */ #define APV_DTYP 4 /* "data-type" (e.g., :include:...,) */ #define APV_CMNT 5 /* comment (...) */ #define APV_WORD 6 /* generic word */ #define APV_PRSN 7 /* start of personal addr list <...> */ #define APV_NPER 8 /* name of person */ #define APV_EPER 9 /* end of personal address list */ #define APV_GRUP 10 /* start of group address list x:..; */ #define APV_NGRP 11 /* name of group */ #define APV_EGRP 12 /* end of group list */ #define APV_DLIT 13 /* domain literal */ ap_ptrtype; /* next node is continuation of this */ /* address, start of new, or null */ #define APP_NIL 0 /* there is no next node */ #define APP_ETC 1 /* next is part of this address */ #define APP_NXT 2 /* next is start of new address */ char *ap_obvalue; /* pointer to string value of object */ struct ap_node *ap_chain; /* pointer to next node */ }; typedef struct ap_node *AP_ptr; struct ap_prevstruct { /* for use when getting indirect input*/ FILE *ap_curfp; /* handle on current file input */ struct ap_prevstruct *ap_prvptr; /* next input down the stack, using...*/ int (*ap_prvgfunc) (); /* getchar function for that input */ int ap_opeek, /* with this as peek-ahead char for it*/ ap_ogroup, /* nesting level of group list */ ap_opersn; /* nesting level of personal list */ }; extern char ap_llex; extern AP_ptr ap_pstrt; extern AP_ptr ap_pcur; extern int ap_1adr(); extern AP_ptr ap_1delete(); extern AP_ptr ap_add(); extern AP_ptr ap_alloc(); extern AP_ptr ap_append(); extern int ap_char(); extern char * ap_dmflip(); extern int ap_dmnormalize(); extern int ap_flget(); extern int ap_fpush(); extern int ap_lex(); extern AP_ptr ap_move(); extern AP_ptr ap_new(); extern AP_ptr ap_normalize(); extern char * ap_p2s(); extern AP_ptr ap_pinit(); extern int ap_ppush(); extern char * ap_s2p(); extern AP_ptr ap_s2tree(); extern AP_ptr ap_sqdelete(); extern AP_ptr ap_sqinsert(); extern AP_ptr ap_sqmove(); extern AP_ptr ap_t2parts(); extern AP_ptr ap_t2s(); /* host-part of address */ #define APV_DTYP 4 /* "data-type" (e.g., :include:...,) */ #define APV_CMNT 5 /* comment (...) */ #define APV_WORD 6 /* generic word */ #define APV_PRSN 7 /* start of personal addr list <...> */ #define APV_NPER 8 mmdf/h/ap_lex.h 444 0 12 4327 3620510347 6627 /* lexical symbols for address parser */ #define LT_EOL 0 /* New-Line */ #define LT_SPC 0 /* Space and tab */ #define LT_ERR 1 /* Illegal chars (control chars) */ #define LT_EOD 2 /* End of Data (null) */ #define LT_COM 3 /* , */ #define LT_AT 4 /* @ */ #define LT_COL 5 /* : */ #define LT_SEM 6 /* ; */ #define LT_LES 7 /* < */ #define LT_GTR 8 /* > */ #define LT_SQT 9 /* \ (only in quoted strings) */ #define LT_LTR 10 /* alphabetics, numbers, and others */ #define LT_XTR 10 /* alphabetics, numbers, and others */ #define LT_NUM 10 /* alphabetics, numbers, and others */ #define LT_LPR 11 /* ( */ #define LT_RPR 12 /* ) */ #define LT_QOT 13 /* " */ #define LT_LSQ 14 /* [ */ #define LT_RSQ 15 /* ] */ #define LV_EOD 0 /* End of Data */ #define LV_ERROR 1 /* These Values go with the above Types */ #define LV_COMMA 2 /* , */ #define LV_AT 3 /* @ */ #define LV_COLON 4 /* : */ #define LV_SEMI 5 /* ; */ #define LV_COMMENT 6 /* (text text text) */ #define LV_LESS 7 /* < */ #define LV_GRTR 8 /* > */ #define LV_WORD 9 /* atom / string */ #define LV_FROM 10 /* << */ #define LV_DLIT 11 /* [text text text] */ (); /* getchar function for that input */ int ap_opeek, /* with this as peek-ahead char for it*/ ap_ogroup, /* nesting level of group list */ ap_opersn; /* nesting level of personal list */ }; extern char ap_llex; extern APmmdf/h/ap_norm.h 444 0 12 524 3620510347 6765 /* ap_norm.h - table declaration for ap_normalize.c */ /* Structure of translation table */ struct ap_hstab { char *name; /* Host (NODE) Name to look for */ char *at; /* use this a hostname */ char *dot; /* append this to mbox portion */ }; 3 /* , */ #define LT_AT 4 /* @ */ #define LT_COL 5 /* : mmdf/h/d_clogcodes.h 444 0 12 502 3620510347 7573 /* * codes used for logging telephone calls through the auto-dialer */ # define LOG_ABAN 'A' /* call was abandoned */ # define LOG_COMP 'C' /* call was completed normally */ # define LOG_START 'S' /* call starting */ # define LOG_OPEN 'O' /* open the call log */ tion */ }; 3 /* , */ #define LT_AT 4 /* @ */ #define LT_COL 5 /* : mmdf/h/d_proto.h 444 0 12 14475 3664425125 7061 /* * message type codes */ # define DATA '0' /* data packet */ # define DATAACK '1' /* acknowledge DATA packet */ # define XPATH '2' /* transmit path packet */ # define XPATHACK '3' /* acknowledge XPATH packet */ # define RPATH '4' /* receive path packet */ # define RPATHACK '5' /* acknowledge RPATH packet */ # define ESCAPE '6' /* escape packet */ # define ESCAPEACK '7' /* acknowledge ESCAPE packet */ # define QUIT '8' /* quit packet */ # define QUITACK '9' /* acknowledge QUIT packet */ # define NBUFF 'A' /* buffering specification packet */ # define NBUFFACK 'B' /* acknowledge NBUFF pack */ /* * lengths of the fixed length packets */ # define LHEADER 6 /* length of the header on all packets */ /* If changed, change TEXTOFF also */ # define LTERM 2 /* length of the terminator for all pack */ # define LACK 6 /* length of the ACK packet */ # define LRESET 40 /* length of the RESET packet */ # define LRESETACK 6 /* length of the RESETACK packet */ # define LESCAPE 8 /* length of the ESCAPE packet */ # define LNBUFF 8 /* length of the NBUFF packet */ # define LQUIT 6 /* length of the QUIT packet */ # define LQUITACK 6 /* length of the QUITACK packet */ # define LPATH 40 /* length of an XPATH or RPATH packet */ /* * protocol constants and packet header offsets */ # define CHKOFF 0 /* start of 4 character checksum */ # define TYPEOFF 4 /* type character */ # define FLAGOFF 5 /* flag offset */ # define TEXTOFF 6 /* text offset */ # define MAXPOFF 6 /* maximum packet length field offset */ # define ESCOFF 6 /* escape character field offset */ # define NBOFF 6 /* buffer limit field offset */ # define LEGALOFF 10 /* legal input character set field */ /* * bits in the flag byte */ # define MASTERBIT 010 /* set of the packet is being sent by the */ /* host which originated the call */ # define EOFBIT 04 /* set if the last packet in a stream */ /* * various timeouts */ # define CONNWAIT 45 /* number of seconds to wait for a direct */ /* line open to succeed. */ # define DIALWAIT 120 /* number of seconds to wait for a dial */ /* line open to succeed. */ # define ACUWAIT 300 /* number of seconds to wait for a child */ /* to succeed opening via the acu */ # define XMITWAIT 60 /* number of seconds to wait for a packet */ /* to be transmitted. this is for the */ /* alarm around the port write calls */ # define QACKWAIT 10 /* number of seconds to wait for response */ /* to a QUIT packet before retransmission */ # define EACKWAIT 10 /* number of seconds to wait for response */ /* to an ESCAPE packet before resending */ # define DACKWAIT 10 /* number of seconds to wait for ACK */ /* before retransmission */ # define ESCAPEWAIT 60 /* number of seconds to wait for an */ /* ESCAPE packet during protocol */ /* startup */ # define NBUFFWAIT 60 /* number of seconds to wait for NBUFF */ # define DATAWAIT 180 /* amount of time to wait for a DATA */ /* packet */ # define XPATHWAIT 60 /* number of seconds to wait for XPATH */ # define RPATHWAIT 60 /* number of seconds to wait for RPATH */ # define XPAWAIT 10 /* number of seconds to wait for XPATHACK */ # define RPAWAIT 10 /* number of seconds to wait for RPATHACK */ # define NBACKWAIT 10 /* number of seconds to wait for NBUFFACK */ /* * manifest configuration constants */ # define MAXPACKET 255 /* maximum length of packet excluding */ /* <cr> */ # define MINPACKET 6 /* minimum length of packet excluding */ /* <cr> */ # define MINPATHSIZ 40 /* minimum packet size that a system must */ /* be capable of receiving or transmitting */ /* excluding <cr> */ # define MAXSCRLINE 256 /* maximum length of line in script file */ # define MAXFIELDS 10 /* maximum fields on a script line */ # define MATCHLEN 128 /* maximum length of string to be matched */ /* in the script file */ # define NSENDTRY 5 /* number of times to attempt message */ /* transmission */ # define NBUFFMAX 2 /* d_nbuff upper limit */ #ifdef SYS5 /* PORT*I is for c_iflag field in termio */ /* PORT*O is for c_oflag field in termio */ /* PORT*C is for c_cflag field in termio */ /* PORT*L is for c_lflag field in termio */ # define PORTSONI (IXON|ICRNL|ISTRIP|IGNBRK|IGNPAR) # define PORTSONO (0) # define PORTSONC (CS7|PARENB|CREAD|HUPCL) # define PORTSONL (0) /* bits to turn on in script mode */ # define PORTPONI (IXON|ICRNL|ISTRIP|IGNBRK|IGNPAR) # define PORTPONO (0) # define PORTPONC (CS7|PARENB|CREAD|HUPCL) # define PORTPONL (ICANON) #else # define PORTSON (EVENP|ODDP|RAW) /* bits to turn on in script mode */ # define PORTSOFF (ALLDELAY|CRMOD|ECHO|LCASE) /* bits to turn off in script mode */ # define PORTPON (EVENP|ODDP) /* bits to turn on in protocol mode */ # define PORTPOFF (ALLDELAY|CRMOD|ECHO|RAW|LCASE) /* bits to turn off in protocol mode */ #endif SYS5 04 /* set if the last packet in a stream */ /* * various timeouts */ # define CONNWAIT 45 /* number of seconds to wait for a direct */ mmdf/h/d_returns.h 444 0 12 5666 3620510350 7365 # define D_CONTIN 2 /* continue with process */ # define D_OK 1 /* everything is fine */ # define D_NO 0 /* something is wrong */ # define D_FATAL -1 /* fatal error. this is returned to */ /* the calling program. */ # define D_NONFATAL -2 /* non-fatal error. this is only used */ /* internally and will not be returned */ /* to the calling program. */ # define D_INTRPT -3 /* interrupt received during system call */ # define D_EOF -4 /* unexpected EOF on script file */ # define D_UNKNOWN -5 /* unknown command word */ # define D_QUOTE -6 /* unmatched quotes */ # define D_NFIELDS -7 /* wrong number of fields for cmd */ # define D_RETRY -8 /* retry a failed packet transmission */ /* error codes. these values are loaded into 'd_errno' to indicate the */ /* reason for one of the above error returns */ # define D_BUSY 1 /* the number dialed is busy */ # define D_ABAN 2 /* the call was abandoned or no answer */ # define D_BADDIG 3 /* bad digit passed to dialer (internal) */ # define D_NOPWR 4 /* the dialer has no power */ # define D_SYSERR 5 /* undistinguished system error */ # define D_LOCKERR 6 /* error opening or creating lock file */ # define D_FORKERR 7 /* couldn't fork after several tries */ # define D_PORTOPEN 8 /* error trying to open port */ # define D_NOPORT 9 /* no port or line available */ # define D_TSOPEN 10 /* error opening transcript file */ # define D_TSWRITE 11 /* error writing on transcript file */ # define D_PORTRD 12 /* error on port read */ # define D_PORTWRT 13 /* error on port write */ # define D_PORTEOF 14 /* eof on port */ # define D_RCVQUIT 15 /* a QUIT packet has been received */ # define D_PRTSGTTY 16 /* error doing stty or gtty on port */ # define D_SCRERR 17 /* error in script file */ # define D_ACCERR 18 /* error in access file */ # define D_NORESP 19 /* no response to transmitted packet */ # define D_PATHERR 20 /* error in path packet */ # define D_CALLLOG 21 /* error opening call log file */ # define D_PACKERR 22 /* error in packet format */ # define D_INTERR 23 /* internal package error */ # define D_NONUMBS 24 /* no numbers given to dialer */ # define D_NOESC 25 /* no esacpe character could be found */ # define D_BADSPD 26 /* bad speed designation */ # define D_LOGERR 27 /* log error */ # define D_TIMEOUT 28 /* alarm during port read or write call */ # define D_INITERR 29 /* trouble initializing */ calling program. */ # define D_INTRPT -3 /* intmmdf/h/d_script.h 444 0 12 3754 3641232574 7177 # /* script file command codes. These values have been knocked * up to start at 10, as opposed to the previous 1, in order to * guarentee that they don't conflict with any of the return * values (D_...) which, for some reason, start at 2. */ # define S_DIAL 10 /* dial the remote system */ # define S_XMIT 12 /* transmit a string */ # define S_RECV 13 /* watch for a string */ # define S_GO 14 /* start the protocol */ # define S_END 15 /* drop the connection */ # define S_XILL 16 /* set illegal transmit characters */ # define S_RILL 17 /* set illegal receive characters */ # define S_XPCK 18 /* set max transmit packet size */ # define S_RPCK 19 /* set max receive packet size */ # define S_RETR 20 /* set number of retran. retries */ # define S_TOAK 21 /* set Time Out limit for AcK */ # define S_TODA 22 /* set Time Out limit for Data */ # define S_SELST 23 /* select begin */ # define S_SELEND 24 /* select end */ # define S_ALT 25 /* alternate */ # define S_REM 26 /* a comment (remember) */ # define S_LOG 27 /* log a message */ # define S_ABORT 28 /* abort the script */ # define S_REPLAY 29 /* Reuse old input */ # define S_MARK 30 /* Mark the place to start a reuse */ # define S_PRTTY 31 /* set tty status bits for protocol mode */ # define S_SCTTY 32 /* set tty status bits for script mode */ # define S_DBLBUF 33 /* status of double buffering */ # define S_USEFILE 34 /* Read script commands from another file */ # define S_EOF 35 /* not actually a command. This is returned * by 'getcmd' to indicate an EOF on the * script src file. */ # define S_PROMPT 36 /* check for a prompt from the host to which */ /* the master is speaking */ # define S_BILL 37 /* start call logging on given number */ /* a QUIT packet hammdf/h/d_structs.h 444 0 12 5100 3641232574 7365 # /* * structure of file descriptors returned from 'pipe' */ struct pipedesc { int p_readfd; /* read file descriptor */ int p_writefd; /* write file descriptor */ }; /* * this structure contains the names and relationships of the available * dial out ports, their lock files, and the acu name. * * Feb 84 Dan Long added p_type for line selection */ struct dialports { char *p_port; /* path name of port */ char *p_lock; /* path name of lock file */ char *p_acu; /* path name of acu device */ char *p_acupref; /* initialization string for acu */ char *p_acupost; /* dialing termination string, for acu */ int p_speed; /* port speed index */ char *p_ltype; /* line type (e.g. WATS) */ char *p_access; /* path name of access file */ }; /* * this structure identifies the available direct connect lines, their * speeds, lock files, and access files. */ struct directlines { char *l_name; /* line name */ char *l_tty; /* local tty path name */ char *l_lock; /* lock file path name */ int l_speed; /* speed index */ char *l_access; /* path name of access list */ }; /* * structure passed to 'd_dial' which contains the phone number/port * speed pairings for the possible connection paths. */ struct telspeed { int t_speed; /* speed index */ char t_number[40]; /* phone number in canonical form */ }; /* structure of speed names/index pairs. */ struct speedtab { char *s_speed; /* pointer to speed name string */ int s_index; /* speed index */ }; /* A structure used in keeping track of packets as they are sent */ struct pkbuff { char p_data[MAXPACKET + 2]; /* the actual data to be sent */ int p_length; /* # of chars in the data */ int p_seqnum; /* the sequence number */ int p_acktype; /* the ack type expected */ int p_ntries; /* # of times it has been sent */ int p_timeo; /* time out value for ACK */ char p_pktype; /* the packet type */ }; /* Used to keep track of the script files that are open */ struct opfile { char o_fname[82]; FILE *o_chan; unsigned o_line; int o_nfields; char *o_fields[MAXFIELDS]; }; oing stty or gtty on port */ # define D_SCRERR 17 /* error in script file */ # define D_ACCERR 18 /* error in access file */ # define D_NORESP 19 /* no response to transmitted packet */ # define D_PATHERR 20 /* error in path packet */ # define D_CALLLOG 21 /* error opening call log file */ # define D_PACKERR 22 /* error in packet format */ # define D_INTERR mmdf/h/d_syscodes.h 444 0 12 361 3620510350 7462 /* * extensions to normal errno values */ # define EDNPWR 80 /* acu has no power */ # define EDNABAN 81 /* acu abandon call and retry */ # define EDNDIG 82 /* illegal digit in number */ /* dialing termination string, for acu */ int p_speed; /* port speed index */ char *p_ltype; /* line type (e.g. WATS) */ char *p_access; /* path name of access file */ }; /* * this stmmdf/h/util.h 444 0 12 3432 3652602610 6330 #ifndef DIDUTIL #define DIDUTIL #include <stdio.h> /* minus the ctype stuff */ #include <ctype.h> #include <setjmp.h> #include <sys/types.h> #include <errno.h> /* declarations that should have been in the system files */ extern char *strcat (); extern char *strcpy (); extern char *sprintf (); /* */ extern jmp_buf timerest; /* some common logical values */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef YES #define YES 1 #endif #ifndef NO #define NO 0 #endif #ifndef OK #define OK 0 #endif #ifndef DONE #define DONE 1 #endif #ifndef NOTOK #define NOTOK -1 #endif #ifndef MAYBE #define MAYBE -2 #endif /* some C compilers do not support 'global' statics properly */ #ifndef LOCFUN #define LOCFUN static /* function local to module */ #endif #ifndef LOCVAR #define LOCVAR static /* variable local to module */ #endif /* stdio extensions */ #define lowtoup(chr) (islower(chr)?toupper(chr):chr) #define uptolow(chr) (isupper(chr)?tolower(chr):chr) #define min(a,b) ((b<a)?b:a) #define isstr(ptr) ((ptr) != 0 && (ptr)[0] != '\0') #define isnull(chr) ((chr) == '\0') #define FOREVER for (;;) union pipunion { int pipcall[2]; struct pipstruct { int prd; int pwrt; } pip; }; typedef union pipunion Pip; #ifdef v6 long siz2lon(); #define st_gsize(ino_ptr) ((long)(siz2lon((struct stat *)ino_ptr))) #else #define st_gsize(inode) ((long)((struct stat *)inode)->st_size) #endif #define gwaitval(val) ((val) >> 8) /* get exit() value from child */ /* val is the argument to wait() */ /* this macro expects to extract the */ /* high byte from the "returned" */ /* value */ #endif DIDUTIL been sent */ int p_timeo; /* time out value for ACK */ char p_pktype; /* the packet type */ }; /* Used to keep track of the script files that are open */ struct opfile { char o_fname[82]; FILE *o_chan; unmmdf/h/dm.h 444 0 12 5677 3622776543 6006 /* Domain-Table definitions * * A Domain table contains entries that reference specific host names * (i.e., symbolic/routing addresses). A table may either be used * strictly as a domain table, in which case the format is: * * domain-name host-name [ domain-name, ... ] * * so that 'a.b.c' will look up 'a' in 'b.c' and find it on the * left-hand side. The right-hand part of the entry is then used * as a host name and is looked up in the routing tables. If the * right-hand part has more than one field, then the first part is * taken as the 'next' host to send to and the remaining parts are * taken as routing references, by domain name. The order is * sequentially forward, so that the second field references the * host that is to receive the message, after the one we hand it to. * (This is the same as the way that SMTP orders its routing info.) * * Alternately, a table may be a standard host table, in which case, its * format is: * * host-name address-value * * such as 'udel-relay 10.0.0.96'. That is, the table was originally * built as a name-to-net-address table, but is doubling as a domain * table. In this case, the 'final' sub-domain name is the same as the * host name, such as 'udel-relay.arpa' mapping to 'udel-relay'. * * A table fully specifies its location within the domain hierarchy, so * that a site may have a 'udel.arpa' table, containing the name of * sites at udel, and also have an 'arpa' table, containing the names * of other domains/sites within the arpa domain. In the list of * domain tables, 'udel.arpa' comes first, so that it intercepts * references to the 'udel' sub-domain. * * Note that this domain usage involves 'flat' tables, in that you * only do one domain lookup, at this site, to acquire the symbolic * host address. This lacks some generality, but should provide some * efficiency. The design is predicated on the expectation that a * site will not have many domain tables (which are subordinate to * other tables at that site). */ struct dm_struct { char *dm_name; /* internal name of domain */ char *dm_domain; /* full name of containing domain */ char *dm_lname; /* name of this host within the domain */ char *dm_show; /* pretty-print version of name */ Table *dm_table; /* table containing entries for domain */ }; typedef struct dm_struct Domain; typedef struct dm_struct Dmn; /*HACK*/ #define dm_tell(thetable) (((Table *)thetable) -> dm_tpos) Domain *dm_nm2struct (); #define DM_NFIELD 16 /* maximum number of routing fields */ struct dm_rtstruct { int dm_argc; /* number of domain value fields */ char *dm_argv[DM_NFIELD]; /* pointers to each field */ char dm_buf[LINESIZE]; /* where to stash domain value string */ }; typedef struct dm_rtstruct Dmn_route; also have an 'arpa' table, containing the names * of other domammdf/h/cmd.h 444 0 12 403 3620510351 6064 /* structure for holding command table info */ struct cmdstruct { char *cmdname; /* string reference */ int cmdtoken; /* internal token */ int cmdnargs; /* minimum argument count */ }; typedef struct cmdstruct Cmd; at this site, to acquire the symbolic * host address. This lacks some generality, but should provide some * efficiency. The design is predicated on the expectation that a * site will not have many domain tables (which are subordinate to * othemmdf/h/cnvtdate.h 444 0 12 544 3620510351 7137 # /* parameters for cnvdate () */ #undef TIMJUL 1 /* unique id string */ #undef TIMID 1 #define TIMCOM 1 /* compact date (yymmddhhmm) */ #define TIMSECS 2 /* full time, down to the second */ #define TIMREG 3 /* rfc 733 standard format */ #define TIMSHRT 4 /* rfc 733 format, without day of week */ extern char *cnvtdate (); rovide some * efficiency. The design is predicated on the expectation that a * site will not have many domain tables (which are subordinate to * othemmdf/h/ch.h 444 0 12 24335 3620510352 5766 #include <stdio.h> /* * INFORMATION KEPT FOR ALL TABLES */ struct tb_struct { char *tb_name; /* internal name of table */ char *tb_show; /* displayable human-oriented string */ char *tb_file; /* name of file containing table */ FILE *tb_fp; /* stdio file pointer */ long tb_pos; /* position in file */ int tb_flags; /* various bits (type of table, etc) */ }; #define TB_SRC 000007 /* Source of table data */ #define TB_FILE 000000 /* Read from file */ #define TB_DBM 000000 /* Read from DBM database */ #define TB_NS 000001 /* Read from Nameserver */ #define TB_TYPE 000070 /* what type of question to ask NS */ #define TB_DOMAIN 000010 /* ask the nameserver for a domain */ #define TB_CHANNEL 000020 /* ask the nameserver for a channel */ #define TB_ROOT 000040 /*ask the nameserver for a root domain*/ #define TB_PARTIAL 000100 /* don't ask the nameserver in certain cases */ typedef struct tb_struct Table; Table *tb_nm2struct (); /*****************************************************************/ /* * STRUCTURE USED FOR CACHING (Version 1, this may change) */ struct cache { char *ca_hostid; /* host identification */ int ca_value; /* its bad connection value */ time_t ca_expire; /* time at which to expire entry */ struct cache *ca_next; /* the next dead host... or NULL */ }; typedef struct cache Cache; /*****************************************************************/ /* * STRUCTURE USED FOR ALIAS SOURCES */ struct al_source { int al_flags; Table *al_table; struct al_source *al_next; }; /* For use with al_flags */ #define AL_TRUSTED 00001 /* this alias file is trustworthy */ #define AL_NOBYPASS 00002 /* this alias file cannot be bypassed */ typedef struct al_source Alias; /****************************************************************** * * STRUCTURES WHICH DESCRIBE CHANNELS * * Ch_struct (typedef Chan) contains all of the parametric information * about a channel. There may be several telephone and/or pobox channels, * but each type is exec'd from a single executable file, so that the * structure provides distinguishing information. For telephone channels, * Ch_struct has a pointer to an extension structure, called ch_phstruct. * The member is null for other channels. */ /* TELEPHONE-SPECIFIC PARAMETERS * * The existing "telephone" channel also is used for low-speed hardwired * machine-machine exchange, through tty ports. * * If a phone channel is inactive, by virtue of not having any mail going * from the local machine to the "other side", then we may need to poll * them, periodically, to see if they have any mail to be brought over. * Currently, you are limited to specifying a delay time; ch_poltime * indicates how many 15-minute units of time to wait, since the last * completed exchange, before polling. NOTE: you cannot currently * specify time-of-day / day-of-week windows. * * In order to keep track of the channel's activity, the files _dstrt, * _ddone, and _pdone are used to note when outbound delivery was last * started, when it was last completed and when the last pickup (inbound * retrieval) was last completed. The files are zero-length and only * their modification time is of interest. * * Ch_access indicates that mail may be retrieved, as well as sent. * * ch_script specifies the script file to be followed for creating a * connection to the slave on the other machine. it usually indicates * what number to call or line to connect to, login sequence, and program * to start running. it's use is more fully described in the dial * package's documentation. */ /**/ /* DESCRIPTION OF OUTPUT CHANNELS * * For active channels, an attempt is made to send queued mail. For * passive channels, the mail is held until called for. Some active * channels may be invoked by anyone, others are restricted to the Deliver * daemon (runnable an background). * * A channel may have two "names". One (ch_show) is intended for display * and may contain any characters that will enhance its presentation. * The other (ch_name) is intended for user input and more limited * display. It should have no special characters (e.g., dash, space) and * should be entirely in lower case. It must be unique. * * A channel services a subset of the entire queue. It access mail * stored under the sub-queue, specified by ch_queue. More than one * channel may access a sub-queue, such as having an arpanet channel * and a backup phone channel. * * Each channel has an associated table, with the names of hosts on that * channel. The module, ch_table, handles access to the table and defines * its format. The local host is usually named in the table. In order to * capture references to the local machine, we need to know its "official" * name in the table. ch_lname permits this. * * ch_ldomain specifies the RFC822 domain name to use, for this host, * for the channel. * * It also may allow some other special handling. Usually, ch_lname is * the same as the global, locname[], but this is not required. For * example, if there are several machines providing service to a single * organization, you may want ch_lname to be an internal, local-network * name for the local machine, while locname[] would be the public name * for the organization. * * Channels often involve contact with a single, other host, which acts as * a relay for the other machines on a network. If that is the case, then * the "official" name of that host should be specified (ch_host). This is * particularly necessary when mail is picked up from (or submitted by) * the relay, so that the Via: field can cite it. * * When the channel is passive and a single site acts as a relay for the * entire channel's mail, then there will be a single Unix login * authorized to perform the pickup. That must be indicated with * ch_login. * * If the channel is a telephone-like channel, then the above-described * ch_wait, ch_access, ch_login, must be filled in. */ /**/ struct ch_struct { char *ch_name; /* specification name of channel */ char *ch_show; /* pretty-print name for display */ Table *ch_table; /* pointer to host name table */ char *ch_queue; /* 'channel' name to enter in queue */ int ch_access; /* assorted switches */ #define DLVRREG 00 /* Active delivery, by any Deliver */ #define DLVRBAK 01 /* Only runnable by daemon */ #define DLVRPSV 02 /* Passive wait for pickup */ #define DLVRDID 04 /* (internal flag) chan was touched */ /* a flag set by mmdf, sometimes */ #define DLVRIMM 010 /* may be run immediately (fast/cheap)*/ #define CH_PICK 020 /* ok to pickup mail */ #define CH_SEND 040 /* ok to send mail */ #define MQ_INQ 0100 /* queued under this channel */ char *ch_ppath; /* path to file containing chan pgm */ char *ch_lname; /* Official host name of local machine */ #define DFLNAME 0 /* use locname as our name on chan */ #define NOLNAME -1 /* we are not know on the channel (??)*/ char *ch_ldomain; /* Official domain name of this host */ char *ch_host; /* name of host doing relay for chan */ #define NORELAY 0 /* direct receipt, rather than relay */ char *ch_login; /* valid login name for pickup relay */ /* ch_access must have PSV bit on */ /* some PSV chans are direct recip- */ /* ients and not relays */ #define NOLOGIN 0 /* not a pickup channel */ char ch_poltime; /* # of 15-minute periods to wait */ #define ALLPOLL -1 /* -1 => poll on every wakeup */ #define NOPOLL 0 /* 0 => no polling allowed */ char *ch_trans; /* filename of the phone transcript */ /* to use relative to logdfldir */ #define DEFTRANS 0 /* 0 => use the default trn file */ char *ch_script; /* pathname to dialing script */ #define NOSCRIPT 0 Table *ch_outsource; /* list of sources authorized to */ /* access this channel */ Table *ch_insource; /* list of sources authorized to */ /* access this channel */ #define NOFILTER 0 Table *ch_outdest; /* authorized destinations */ Table *ch_indest; /* authorized destinations */ Table *ch_known; /* table of hosts known to hosts on * * this channel, normally == ch_table */ char *ch_confstr; /* general purpose string */ int ch_apout; /* default 733 / 822 /same format */ /* for header munging */ char ch_auth; /* For authorisation by user control */ #define CH_FREE 0 #define CH_IN_LOG 01 #define CH_IN_WARN 02 #define CH_IN_BLOCK 04 #define CH_OUT_LOG 010 #define CH_OUT_WARN 020 #define CH_OUT_BLOCK 040 #define CH_HAU 0100 /* authorise on basis of host AND user */ #define CH_DHO 0200 #define CH_IN_AUTH 07 #define CH_OUT_AUTH 070 Cache *ch_dead; /* Current dead hosts */ time_t ch_ttl; /* Number of seconds to keep dead host records */ char *ch_logfile; /* Logfile for this channel */ int ch_loglevel; /* Logging level for this channel */ }; typedef struct ch_struct Chan; /* make it a "type" */ Chan * ch_nm2struct (), /* chan name -> ptr to its table entry */ * ch_qu2struct (), /* queue name -> ptr to its table entry */ * ch_h2chan (); /* use host name to find chan name */ Chan *ch_h2chan (); /* which chan name table is host in? */ ation name of channel */ char *ch_show; /* pretty-print name for display */ Table *ch_table; /* pointer to host name table */ char *ch_queue; /* 'channel' name to enter in queue */ int ch_access; /*mmdf/h/ll_log.h 444 0 12 3053 3620510352 6616 /* Definitions for the llog status-recording package */ #define LLOGCLS 01 /* Keep log closed, except when writing */ #define LLOGCYC 02 /* Cycle to beginning when log full */ #define LLOGWAT 04 /* Wait, if log locked and LLOGCLS */ #define LLOGSOME 07 /* Occasional logging */ #define LLOGFAT -1 /* Fatal error */ #define LLOGTMP 5 /* Temporary (minor?) error */ #define LLOGGEN 10 /* General information */ #define LLOGBST 20 /* Basic statistics */ #define LLOGFST 25 /* Full statistics */ #define LLOGPTR 40 /* Trace of overall program phases */ #define LLOGBTR 50 /* Trace of basic program acitivity */ #define LLOGFTR 55 /* Full program trace */ #define LLOGMAX 126 /* Maximum possible value */ struct ll_struct { char *ll_file; /* path name to logging file */ char *ll_hdr; /* text to put in opening line */ int ll_level; /* don't print entries higher than this */ int ll_msize; /* max size for log, in 100's of blocks */ int ll_stat; /* assorted switches */ int ll_fd; /* holds file descriptor */ FILE *ll_fp; /* Standard IO stream pointer */ }; typedef struct ll_struct Llog; typedef struct ll_struct LLog; 20 /* Basic statistics */ #define LLOGFST 25 /* Full statistics */ #define LLOGPTR 40 /* Trace of overall program phases */ #define LLOGBTR 50 /* Trace of basic program acitivity */ #define LLOGFTR 55 /* Full program trace */ #define LLOGMAX 126 /* Maximum possible value */ struct ll_struct { char *ll_file; /* path name to loggingmmdf/h/nexec.h 444 0 12 1406 3620510352 6450 /* Parameters for newpgml() fork/exec subroutine */ /* parameter for fdarray */ #define CLOSEFD -1 /* if equals fdarray[i], close (i); */ /* parameter for proctyp */ #define PUREXEC 0 /* simply exec over current process */ #define FRKEXEC 1 /* run it in lower process */ #define SPNEXEC 2 /* separate new process from old */ /* parameter for pgmflags */ #define FRKWAIT 1 /* wait for FRKEXEC to complete */ #define FRKPIGO 2 /* Parent's signals off during FRKWAIT */ #define FRKCIGO 4 /* Childs signals off before execl */ #define FRKERRR 8 /* Give error return, if child exec */ /* fails; else exit() */ #define NUMTRY 30 race of basic program acitivity */ #define LLOGFTR 55 /* Full program trace */ #define LLOGMAX 126 /* Maximum possible value */ struct ll_struct { char *ll_file; /* path name to loggingmmdf/h/adr_queue.h 444 0 12 3167 3620510352 7326 # /* Address info stored in queue files */ /* Every message has an "address list" queued with it. This file */ /* contains the definitions of standard values and formats used in */ /* that file. The primary info is the structure of a single address */ /* entry. */ struct adr_struct /* 1 address entry in queued list */ { char adr_tmp; /* temp ok on this address */ #define ADR_AOK '+' /* only address sent, so far */ #define ADR_CLR '-' /* address is clear to send */ char adr_delv; /* send to mailbox and/or tty */ #define ADR_MAIL 'm' /* Deliver as mail */ #define ADR_TTY 't' /* Deliver to tty */ #define ADR_BOTH 'b' /* Deliver both ways */ #define ADR_TorM 'e' /* either tty or else mail */ #define ADR_DONE '*' /* No more processing to be done */ char *adr_que; /* name of output channel queue */ char *adr_host; /* host string */ char *adr_local; /* local-part of address */ char adr_buf[LINESIZE]; /* where to stash the unparsed text */ long adr_pos; /* position in address list of this msg */ }; #define ADR_DLOFF 2 /* file offset to adr_delv */ #define ADR_TMOFF 0 /* file offset to adr_tmp */ /* address is clear to send */ char adr_delv; /* send to mailbox and/or tty */ #define ADR_MAIL 'm' /* Deliver as mail */ #define ADR_TTY 't' /* Deliver to tty */ #define ADR_BOTH 'b' /* Deliver both ways */ #define ADR_TorM 'e' /* eimmdf/h/amp.h 444 0 12 634 3620510353 6106 /* amp.h - global defs (not many) */ /* Structure of translation table */ struct hstab { char *name; /* Host (NODE) Name to look for */ char *at; /* use this a hostname */ char *dot; /* append this to mbox portion */ }; /* addresses of the form "<mbox> @ name" become "<mbox>.dot @ at" */ long amp_hdr(); long adr_pos; /* position in address list of this msg */ }; #define ADR_DLOFF 2 mmdf/h/jnt.h 444 0 12 435 3620510353 6123 /* * Return value for OK reception - the NIFTP has excepted * the file + address without problems */ #define JNT_OK 0 /* * Temporary error - a retry may be successful */ #define JNT_TEMP 1 /* * Return value for JNT Mail permanent error - total reject. */ #define JNT_PERM 75 this to mbox portion */ }; /* addresses of the form "<mbox> @ name" become "<mbox>.dot @ at" */ long amp_hdr(); long adr_pos; /* position in address list of this msg */ }; #define ADR_DLOFF 2 mmdf/h/mmdf.h 444 0 12 17777 3656424562 6353 # /* NOTE: h/util.h must be included BEFORE mmdf.h, in the source * file. It is not simply included, here, because some * C compilers cannot handle enough levels of #include * nesting. (sigh.) */ #include "ll_log.h" /* file-logging package */ #include "conf.h" /* mmdf site-specific general info */ short domsg; /* verbose reporting to controlling tty */ #define printx if (domsg) printf /* Reply Codes for MMDF * Based on: "Revised FTP Reply Codes", by Jon Postel & Nancy Neigus Arpanet * RFC 640 / NIC 30843, in the "Arpanet Protocol Handbook", E. Feinler * and J. Postel (eds.), NIC 7104, Network Information Center, SRI * International: Menlo Park, CA. (NTIS AD-A0038901) * * Actual values are different, but scheme is same. Codes must fit into * 8-bits (to pass on exit() calls); fields are packed 2-3-3 and interpreted * as octal numbers. * * Basic format: * * 0yz: positive completion; entire action done * 1yz: positive intermediate; only part done * 2yz: Transient negative completion; may work later * 3yz: Permanent negative completion; you lose forever * * x0z: syntax * x1z: general; doesn't fit any other category * x2z: connections; truly transfer-related * x3z: user/authentication/account * x4x: mail * x5z: file system * * 3-bit z field is unique to the reply. In the following, * the RP_xVAL defines are available for masking to obtain a field. */ /*************** FIELD DEFINITIONS & BASIC VALUES ***************** */ /* Field 1: Basic degree of success (2-bits) */ #define RP_BTYP '\200' /* good vs. bad; on => bad */ #define RP_BVAL '\300' /* basic degree of success */ #define RP_BOK '\000' /* went fine; all done */ #define RP_BPOK '\100' /* only the first part got done */ #define RP_BTNO '\200' /* temporary failure; try later */ #define RP_BNO '\300' /* not now, nor never; you lose */ /* Field 2: Basic domain of discourse (3-bits) */ #define RP_CVAL '\070' /* basic category (domain) of reply */ #define RP_CSYN '\000' /* purely a matter of form */ #define RP_CGEN '\010' /* couldn't find anywhere else for it */ #define RP_CCON '\020' /* data-transfer-related issue */ #define RP_CUSR '\030' /* pertaining to the user */ #define RP_CMAI '\040' /* specific to mail semantics */ #define RP_CFIL '\050' /* file system */ #define RP_CLIO '\060' /* local i/o system */ /* Field 3: Specific value for this reply (3-bits) */ #define RP_SVAL '\007' /* specific value of reply */ /********************* SPECIFIC SUCCESS VALUES ******************** */ /* Complete Success */ #define RP_DONE (RP_BOK | RP_CGEN | '\000') /* done (e.g., w/transaction) */ #define RP_OK (RP_BOK | RP_CGEN | '\001') /* general-purpose OK */ #define RP_MOK (RP_BOK | RP_CMAI | '\000') /* message is accepted (w/text) */ #define RP_DOK (RP_BOK | RP_CGEN | '\003') /* accepted for the delayed submission channel */ /* Partial Success */ #define RP_MAST (RP_BPOK| RP_CGEN | '\000') /* you are the requestor */ #define RP_SLAV (RP_BPOK| RP_CGEN | '\001') /* you are the requestee */ #define RP_AOK (RP_BPOK| RP_CMAI | '\000') /* message address is accepted */ #define RP_HOK (RP_BPOK| RP_CMAI | '\001') /* host processing completed */ /********************* SPECIFIC FALURE VALUES ********************* */ /* Partial Failure */ #define RP_AGN (RP_BTNO | RP_CGEN | '\000') /* not now; maybe later */ #define RP_TIME (RP_BTNO | RP_CGEN | '\001') /* timeout */ #define RP_NOOP (RP_BTNO | RP_CGEN | '\002') /* no-op; nothing done, this time */ #define RP_EOF (RP_BTNO | RP_CGEN | '\003') /* encountered an end of file */ #define RP_NET (RP_BTNO | RP_CCON | '\000') /* channel went bad */ #define RP_BHST (RP_BTNO | RP_CCON | '\001') /* foreign host screwed up */ #define RP_DHST (RP_BTNO | RP_CCON | '\002') /* host went away */ #define RP_NIO (RP_BTNO | RP_CCON | '\004') /* general net i/o problem */ #define RP_NS (RP_BTNO | RP_CCON | '\005') /* temporary nameserver failure */ #define RP_FIO (RP_BTNO | RP_CFIL | '\000') /* error reading/writing file */ #define RP_FCRT (RP_BTNO | RP_CFIL | '\001') /* unable to create file */ #define RP_FOPN (RP_BTNO | RP_CFIL | '\002') /* unable to open file */ #define RP_LIO (RP_BTNO | RP_CLIO | '\000') /* general local i/o problem */ #define RP_LOCK (RP_BTNO | RP_CLIO | '\001') /* resource currently locked */ /* Complete Failure */ #define RP_MECH (RP_BNO | RP_CGEN | '\000') /* bad mechanism/path; try alternate? */ #define RP_NO (RP_BNO | RP_CGEN | '\001') /* general-purpose NO */ #define RP_PROT (RP_BNO | RP_CCON | '\000') /* general prototocol error */ #define RP_RPLY (RP_BNO | RP_CCON | '\001') /* bad reply code (PERMANENT ERROR) */ #define RP_NAUTH (RP_BNO | RP_CUSR | '\001') /* bad authorisation */ /* SEK this will be used for user checks*/ #define RP_NDEL (RP_BNO | RP_CMAI | '\000') /* couldn't deliver */ #define RP_HUH (RP_BNO | RP_CSYN | '\000') /* couldn't parse the request */ #define RP_NCMD (RP_BNO | RP_CSYN | '\001') /* no such command defined */ #define RP_PARM (RP_BNO | RP_CSYN | '\002') /* bad parameter */ #define RP_UCMD (RP_BNO | RP_CSYN | '\003') /* command not implemented */ #define RP_USER (RP_BNO | RP_CUSR | '\000') /* unknown user */ /* STRUCTURE OF A REPLY STRING */ struct rp_construct /* for constant reply conditions */ { char rp_cval; char rp_cline[50]; }; struct rp_bufstruct /* for reading reply strings */ { char rp_val; char rp_line[256]; }; typedef struct rp_bufstruct RP_Buf; #define rp_conlen(bufnam) (strlen (bufnam.rp_cline) + sizeof (bufnam.rp_cval)) /* PSEUDO-FUNCTIONS TO ACCESS REPLY INFO */ #define rp_gval(val) ((char) (val)) /* get the entire return value */ /* The next three give the field's bits, within the whole value */ #define rp_gbval(val) (rp_gval (val) & RP_BVAL) /* get the basic part of return value */ #define rp_gcval(val) (rp_gval (val) & RP_CVAL) /* get the domain part of value */ #define rp_gsval(val) (rp_gval (val) & RP_SVAL) /* get the specific part of value */ /* The next three give the numeric value withing the field */ #define rp_gbbit(val) ((rp_gval (val) >> 6) & 03) /* get the basic part right-shifted */ #define rp_gcbit(val) ((rp_gval (val) >> 3 ) & 07) /* get the domain part right-shifted */ #define rp_gsbit(val) (rp_gval (val) & 07) /* get the specific part right-shifted */ /* The following works with SIGNED or UNSIGNED chars! */ #define rp_isgood(val) (! rp_isbad(val)) /* is return value positive? */ #define rp_isbad(val) (rp_gval(val) & 0200) /* is return value negative? */ extern char *rp_valstr (); mmdf/h/bboards.h 444 0 12 3062 3620510353 6763 /* bboards.h - definition of a BBoard structure */ #define BBOARDS "bboards" /* name in /etc/passwd */ #define DISTADR "dist-" /* prefix for distribution addresses */ #define BBMODE 0644 /* default BBoards mode */ struct bboard { char *bb_name; /* name of the bboard */ char **bb_aka; /* aliases for the bboards */ char *bb_file; /* file it resides in */ char *bb_archive; /* file where archives reside */ char *bb_info; /* file where maxima resides */ char *bb_map; /* file where binary map resides */ char *bb_passwd; /* password for it */ char **bb_leader; /* list of local leaders */ char *bb_addr; /* network address */ char *bb_request; /* network address for requests */ char *bb_relay; /* host acting as relay in local domain */ char **bb_dist; /* distribution list */ unsigned int bb_flags; /* various flags */ #define BB_NULL 0x0000 #define BB_ARCH 0x0007 /* archive policy */ #define BB_ASAV 0x0001 /* save in archives/ directory */ #define BB_AREM 0x0002 /* remove without saving */ #define BB_INVIS 0x0010 /* invisible to bbc */ unsigned int bb_count; /* unassigned */ unsigned int bb_maxima; /* highest BBoard-Id in it */ char *bb_date; /* date that maxima was written */ struct bboard *bb_next; /* unassigned */ struct bboard *bb_link; /* unassigned */ }; int setbbent (), endbbent (), setbbfile (), ldrbb (), ldrchk (), getbbdist (); char *getbberr (); struct bboard *getbbent (), *getbbnam (), *getbbaka (), *getbbcpy(); st */ #define RP_NCMD (RP_BNO | RP_CSYN | '\001') /* no such command defined */ #define RP_PARM (RP_BNO | RP_CSYN | '\002') /* bad parameter */ #define RP_UCMD (RP_BNO | RP_CSYN | '\003') /* command not implemented */ #define RP_USER (RP_BNO | RP_CUSR | '\000') /* unknown user */ /* STRUCTURE OF A REPLY STRING */ structmmdf/h/chan.h 444 0 12 1123 3620510353 6254 /* definitions for channel programs */ /* the following are used to indicate the state condition of a * channel program. this may be passed to qu_fakrply() to keep * Deliver happy if the foreign site goes away. */ #define SND_ABORT 0 /* terminate on error */ #define SND_RINIT 1 /* goto qu_rinit */ #define SND_RDADR 2 /* goto qu_radr */ #define SND_ARPLY 3 /* goto qu_wrply, for address */ #define SND_TRPLY 4 /* goto qu_wrply, for msg text */ char **bb_leader; /* list of local leaders */ char *bb_addr; /* network address */ char *bb_request; /* network address for requests */ char *bb_relay; /* host acting as relay in local domain */ char **bb_dist; /* distribution list */ unsigned int bb_flags; /* various flags */ #define BB_NULL 0x0000 #define BB_ARCH 0x0007 /* archive policy */ #define BB_ASAV 0x0001 /* save in ammdf/h/chdbm.h 444 0 12 422 3623131444 6404 #define FS '\034' #define MAXVAL 20 /* allow this many values per entry */ #define ENTRYSIZE 1024 typedef struct DBvalues { char *RECname; /* name of table */ char *RECval; /* value for this record, in this table */ } DBMValues[MAXVAL + 1]; error */ #define SND_RINIT 1 /* goto qu_rinit */ #define SND_RDADR 2 /* goto qu_radr */ #define SND_ARPLY 3 /* goto qu_wrply, for address mmdf/h/hdr.h 444 0 12 625 3620510354 6107 /* hdr_read return values */ #define HDR_NAM -1 /* no name */ #define HDR_EOH 0 /* end of headers */ #define HDR_OK 1 /* nice header */ #define HDR_NEW 2 /* new header field */ #define HDR_MOR 3 /* continuation of header field */ /* goto qu_radr */ #define SND_ARPLY 3 /* goto qu_wrply, for address mmdf/h/msg.h 444 0 12 2547 3620510354 6145 struct msg_struct /* Structure for sorting files */ { time_t mg_time; /* Creation time of message */ #define LARGESIZE 100000 /* threshold for large message */ #ifdef LARGESIZE char mg_large; /* message is over-sized */ #endif char mg_stat; /* options */ char mg_mname[MSGNSIZE]; /* Filename */ char mg_null; /* Null for terminating string */ }; typedef struct msg_struct Msg; /** The following bits are use in mg_stat **/ #define ADR_CITE 0001 /* When a message is determined to be */ /* undeliverable to some addressee(s),*/ /* MMDF notifies the sender (if a */ /* return address was given) and an */ /* entire copy of the message is */ /* included in the return, unless this*/ /* flag is set, in which case only */ /* a "citation" is returned. */ #define ADR_NORET 0002 /* Do NOT return to sender on error */ #define ADR_NOWARN 0004 /* Do NOT send non-delivery warnings */ #define ADR_WARNED 0010 /* Warning has been sent */ #define msg_cite(val) (val&ADR_CITE) #define msg_noret(val) (val&ADR_NORET) #define msg_nowarn(val) (val&ADR_NOWARN) #define msg_warned(val) (val&ADR_WARNED) _link; /* unassigned */ }; int setbbent (), endbbent (), setbbfile (), ldrbb (), ldrchk (), getbbdist (); char *getbberr (); struct bboard *getbmmdf/h/phs.h 444 0 12 1366 3620510354 6147 /* * activity phase-recording * * use files to timestamp activity phases for channels */ #define PHS_CNSTRT 1 /* start connecting to site */ #define PHS_CNGOT 2 /* got connection to site */ #define PHS_CNEND 3 /* end connection to site */ #define PHS_RESTRT 4 /* start reading (sending) mail from site */ #define PHS_REMSG 5 /* finished reading one message */ #define PHS_REEND 6 /* end reading (sending) mail from site */ #define PHS_WRSTRT 7 /* start writing (picking up) mail to site */ #define PHS_WRMSG 8 /* finished writing one message */ #define PHS_WREND 9 /* end writing (picking up) mail to site */ extern time_t phs_get (); DF notifies the sender (if a */ /* return address was given) and an */ /* entire copy of the message is */ /* included in the return, unless this*/ /* flag is set, in which case only */ /* a "citation" is returned. mmdf/h/ns.h 444 0 12 563 3644274345 5770 /* * timer values used to adjust nameserver performance */ /* extreme limits */ #define NS_MINRETRY 1 #define NS_MAXRETRY 7 #define NS_MINRETRANS 5 #define NS_MINTIME (NS_MINRETRY * NS_MINRETRANS) /* some timer values */ #define NS_UIPTIME 20 /* default for UIP programs */ #define NS_DELTIME 120 /* delay channel */ #define NS_NETTIME 20 /* network programs */ ne PHS_REMSG 5 /* finished reading one message */ #define PHS_REEND 6 /* end reading (sending) mail from site */ #defmmdf/h/gettys.h 444 0 12 274 3620510354 6651 # /* the definitions needed to use the package which accesses * the /etc/ttys file. */ struct ttys { int t_valid; int t_code; char t_name[8]; }; #define BADDATA -2 NS_MINRETRANS) /* some timer values */ #define NS_UIPTIME 20 /* default for UIP programs */ #define NS_DELTIME 120 /* delay channel */ #define NS_NETTIME 20 /* network programs */ ne PHS_REMSG 5 /* finished reading one message */ #define PHS_REEND 6 /* end reading (sending) mail from site */ #defmmdf/h/conf.h 444 0 12 2732 3656424247 6316 # /* This contains the most site-dependent definitions */ #define USERSIZE 8 /* max length of a user system name */ #define MAILIDSIZ 64 /* max length of a mailid, * must be at least USERSIZE. */ #define MSGNSIZE 14 /* max length of a message file name */ #define FILNSIZE 64 /* max length of full file name */ #define ADDRSIZE 256 /* max length of an address */ #define LINESIZE 256 /* max length of a line/record */ #define NUMCHANS 20 /* max number of channels */ #define NUMTABLES 30 /* max number of tables */ #define NUMDOMAINS 20 /* max number of domains */ /************ DIAL IN/OUT (CH_PHONE / SLAVE) TAILORING *****************/ #define DL_TRIES 2 /* number of re-connects OK for phone */ #define DL_RCVSZ 255 /* longest line receivable */ #define DL_XMTSZ 255 /* longest line sendable */ /**************** JNTMAIL Tailoring *******************/ #undef JNTMAIL #undef BOTHEND #undef VIATRACE /**************** Include File Tailoring ****************************/ #include <time.h> #include <sys/dir.h> /********************* Privileged UIDs *************************/ /* * This can be a macro as give here or you can provide a function. * It should always authorize root and mmdf. * The global variable effecid is assumed to have the MMDF uid in it. */ #define PRIV_USER(x) ((x)==0 || (x)==1 || (x)==effecid) /* a "citation" is returned. mmdf/h/ph.h 444 0 12 701 3646043304 5740 struct ps_rstruct { /* save last reply obtained */ int sm_rval; /* rp.h value for reply */ int sm_rlen; /* current lengh of reply string */ char sm_rgot; /* true, if have a reply */ char sm_rstr[LINESIZE]; /* human-oriented reply text */ }; #define rp_structlen(bufnam) (strlen (bufnam.rp_line) + sizeof (bufnam.rp_val)) / /**************** JNTMAIL Tailoring *******************/ #mmdf/h/smtp.h 444 0 12 2737 3657737607 6371 /* timeouts for smtpsrvr */ #define NTIMEOUT 180 /* 3 minute timeout on net I/O */ #define DTIMEOUT 600 /* 10 minute timeout on submit I/O */ /* smtp timeouts */ #define SM_TIME 20 /* default timeout */ #define SM_HTIME SM_TIME /* Time allowed for HELO command */ #define SM_OTIME 40 /* Time allowed for a netopen */ /* * for waiting for first response after connection * give a lot of time to first MX, then back off */ #define SM_ATIME 240 /* Time allowed for answer: 1st open */ #define SM_ATMIN 90 /* Time allowed for answer: minimum */ #define SM_ATINC 30 /* backoff increment */ #define SM_STIME 120 /* Time allowed for MAIL command */ #define SM_TTIME 300 /* Time allowed for RCPT command */ #define SM_QTIME SM_TIME /* Time allowed for QUIT command */ #define SM_RTIME SM_TIME /* Time allowed for a RSET command */ #define SM_DTIME SM_TIME /* Time allowed for a DATA command */ #define SM_PTIME 120 /* Time allowed for the "." command */ #define SM_BTIME 120 /* Time allowed for a block of text */ struct sm_rstruct { /* save last reply obtained */ int sm_rval; /* rp.h value for reply */ int sm_rlen; /* current lengh of reply string */ char sm_rgot; /* true, if have a reply */ char sm_rstr[LINESIZE]; /* human-oriented reply text */ }; getbberr (); struct bboard *getbmmdf/doc/ 755 0 12 0 3652766646 5457 mmdf/doc/review/ 755 0 12 0 3635164765 6753 mmdf/doc/review/p0 444 0 12 3721 3620510336 7262 .TL MMDFII: A Technical Review .AU Douglas P. Kingston III .AI Ballistic Research Laboratory Aberdeen Proving Grounds, Maryland 21005 <dpk@brl> .AB .PP The Multi-channel Memo Distribution Facility (MMDF) is one of the most sophisticated mail systems available for the UNIX\(dg .FS \(dg Unix is a trademark of Bell Laboratories. .FE operating system. MMDF is a mail transport system that supports a variety of user interfaces and delivery mechanisms. The design was not encumbered with the need to be compatible with existing mail systems, and as a result MMDF has a unified family of mail handling programs. This review will discuss MMDF's design and operation, concentrating on those features that are unique to MMDFII, the latest release of MMDF. .PP MMDF's design allows it to grow from a single-host system to a large mail relay without degradation of mail system performance, and to degrade gracefully as the load becomes huge. The demands of a high volume mail relay have led to many of MMDF's innovative design choices. .PP Unlike some other systems, MMDF has separate processes for mail submission and delivery. Recent changes to the delivery software to permit intelligent retry strategies based on the retry history for each dead host will be explained. The effect of the new domain server mechanism on address validation will be discussed. .PP The separation of mail into channels is key to MMDF's ability to handle large amounts of mail. Each channel represents a different class of delivery and each channel has its own queue. This isolates problems and allows one to provide different ``levels of service'' to different channels. .PP Other topics to be discussed will include available user interfaces, the mailing list processor, aliasing, runtime configuration, and domain based naming. .PP The MMDF system was originally developed at the University of Delaware and has since seen significant development work at the Ballistic Research Laboratory and University College London. .AE F A REPLY STRING */ structmmdf/doc/review/p1 444 0 12 11555 3642650444 7320 .SH Introduction and History .PP The Multi-channel Memo Distribution Facility, commonly called MMDF, is a suite of software that has seen a great deal of work since it was originally released in 1980. The original code was designed and implemented by Dave Crocker working under Professor David Farber at the University of Delaware (UDEL). The MMDF system was then chosen to form the initial backbone software for the CSNET project and has been in use for several years by elements of the U.S. Army. The software has seen a great deal of change in the process. The original code is commonly referred to as MMDFI or MMDF Version 1. A number of minor additions and changes were made while fielding MMDFI as the result of collaboration between UDEL and BRL and some other sites. After the original code was fielded in CSNET, Dave Crocker began the development of a upgraded version of the MMDF system which was designed to work in the new Internet domain naming system and was to incorporate numerous design changes suggested by experience with MMDFI. Dave Crocker left the CSNET project before completing this work, approximately two weeks before the TCP/IP switchover of the ARPANET, 1 January 1983. At this time, BRL was a solid MMDF site. We were reluctant to try to retrofit the existing version of MMDFI to handle the new mail protocols that also took effect on 1 January, so Doug Kingston of BRL undertook the task of finishing the work needed to make MMDFII operational. A production version of MMDFII was installed at BRL during the third week of January 1983, and served as BRL's mail system on three hosts, but there was no stable version of the MMDFII code until June 1983. The first few months of MMDFII were quite rough and it needed a great deal of ``tender loving care''. .PP For reasons that will be clear in a moment, this stable version of June 1983 is now referred to as the MMDFII-pre-England version. Around June, a copy of this stable version was delivered to Steve Kille of University College London (UCL) and to Brendan Reilly of UDEL, who had taken over Dave Crocker's work on MMDF at UDEL. Steve Kille made a number of major changes to the handling of domains, address parsing, and handling of the alias files. Steve also added support for NIFTP, a European file transfer protocol used for sending mail in a batch environment. At the same time that Steve was making his enhancements, Doug Kingston continued to develop BRL's copy of MMDFII to make it an even more solid mail system. BRL's changes were not as major as Steve's but covered a great deal of code and fixed several major outstanding bugs. This dual development led to two variants of MMDFII that each needed the other's improvements. In late September of 1983 Brendan Reilly and Doug Kingston spent a week in England with Steve to merge the variants and to discuss future changes and directions for MMDF. The result of this meeting was a merged version of MMDFII which I will call MMDFII-post-England. Just prior to this trip, the CSNET Information Center (CIC) received a copy of the pre-England MMDF. Their later changes were based on this pre-England version which made merging of their changes into the post-England version somewhat difficult. .PP After the England meeting, Brendan Reilly of UDEL took the role of coordinator of the subsequent changes to MMDF. Copies of the MMDF-post-England were made simultaneously available to BRL, UCL, and UDEL. Since then many minor changes have been made by all four sites; in essentially all cases these changes have been bug fixes or changes to make MMDF a more stable and robust system. .PP Since then, Doug Kingston at BRL has made changes to the local delivery mechanism, rewriting much of the original code, and the central delivery program has been upgraded to take advantage of large-address-space machines, when possible, to keep retry histories for messages on a host-by-host basis. Bernie Cosell at the CIC has undertaken to speed up MMDF execution by providing a facility for compiling in some of the information normally included in the ASCII text-based version. Steve Kille an alternative to the ASCII text based version. Steve Kille has continued to refine the address handling and the British ``backwards'' domain code. .FN The British do domains backwards. For example, if in the US (Internet) we write ``user@VAX1.EE.UDEL.ARPA'' known as ``little endian'' order, the British (SERC Net) write ``user@ARPA.UDEL.EE.VAX1'' or ``big endian'' order. Put another way, ``big endians'' put the largest, most general, or most significant element of the domain first. ``Little endians'' use the other order, with the most significant part last. [See .I Gulliver's Travels .R by Joanthan Swift. The "big endian" vs. "little endian" controversy was a .I causus belli .R in Lilliput.] .FE Brendan Reilly has made changes to the package to allow it to run on the Altos system and has fixed numerous bugs in the PhoneNet code. of the MMDF system which was designed to work in the new Internet domain naming system and was to incorporate numerous design changes suggested bymmdf/doc/review/p2 444 0 12 15716 3620510337 7314 .SH The MMDF Design .PP The MMDF system design has not changed fundamentally from the original design proposed and implemented for MMDFI. The design of MMDFI is covered in detail in the paper ``An Internetwork Memo Distribution Capability''. .FN D. Crocker, E. Szurkowski, and D. Farber, ``An Internetwork Memo Distribution Capability'', Datacom Conference, September 1979. .FE In this chapter, I will summarize the basic design and discuss those changes that have been made in MMDFII. .PP Mail software is normally classed into one of two groups. A user agent (UA) is a program that is responsible for providing a ``user friendly'' interface for reading or writing mail and converting to the canonical interface of the mail transfer agent as necessary. A mail transfer agent (MTA) is a program or system to which you or your user agent entrusts a message for delivery to someone else's user agent. There has been a great deal of confusion caused by people who fail to realize the difference between a mail transfer agent and a user agent, or even that a difference exists! There is a wide variety of user agents which can be used with MMDF, and it is the responsibility of the use agent to provide a user interface. This separation of the functions of user agent and mail transfer agent has many advantages, not the least of which is that MMDF can support many different user interfaces with ease. Currently, there are at least five different interfaces available including the Rand MH system, V6 style mail, Berkeley's Mail (cap-mail), the Tenex-style Send and Msg programs, and Rmail (for UUCP). MMDF is a mail transfer agent. MMDF does not have, nor does it claim to have, a good ``user interface''; instead it has a good program (MTA-UA) interface. MMDF accepts messages for delivery either locally or to a remote site. It attempts to verify the validity of the addresses at submission time to the extent possible given only a host table and a list of local addresses. If accepted, it will continually try to resend the message until the retry time is exhausted at which time the message is returned to the sender. .PP The MMDF system can be thought of as two subsystems, responsible for mail submission and mail delivery, respectively. Between these two halves is the mail queue. The mail queue will be discussed later, but basically it stores each message as two files, an address list with some control information, and a separate file containing only the message text (header and body). .PP The submission half of the system consists mainly of one program, called ``\fBsubmit\fR'', which is responsible for enqueuing mail to be delivered. As much verification as possible is performed on the message at submission time. For mail destined for the local machine, this means making sure the destination account exists, and that any local mailing list or aliases expand properly. For mail that has a non-local host specification, the \fBsubmit\fR process checks to see if it knows how to reach the specified host. Mail which for any reason is known to be undeliverable, is not accepted for delivery. \fBSubmit\fR is called by two types of processes. The first group includes user agents such as the \fBsend\fR program. The second group comprises channel programs such as \fBrmail\fR .FN The \fBrmail\fR program (/bin/rmail) is invoked by uucp when delivering mail on your system. .FE which are interfacing to remote mail transfer agents. .PP The delivery portion of MMDF is represented by two main elements: the \fBdeliver\fR program which manages the queue, and the channel programs which handle the details of delivery to a specific network, host, or mail system. The \fBdeliver\fR program takes each message which is eligible to be delivered, and opens the appropriate address list. For each address in the list, \fBdeliver\fR ensures that it is running the appropriate channel program and then passes the envelope information .FN The MMDF envelope information consists of the the return address, the destination addresses, some delivery options and a reference to the message text file. .FE to the channel. A reference to the file containing the actual message text is passed to the channel program. The channel decides how to deliver the message and sees to any necessary message reformatting that may be necessary (e.g. ``header munging''). .PP There is currently a variety of channels and the number is growing. The local channel handles delivery of messages to local addresses. The list channel is a special, somewhat incestuous, channel which acts the role of channel, receiving user agent, and sending user agent all in one. The list channel resubmits mail back into the mail system by calling \fBsubmit\fR. This has several benefits that will be discussed later. .FN See the section on the list channel. .FE The SMTP channel delivers mail via TCP/IP connections using the SMTP protocol. .FN J. Postel, ``RFC821 - Simple Mail Transfer Protocol'', Network Information Center, SRI International, August 1982. .FE The phone channel uses the PhoneNet link-level protocol software developed at the University of Delaware for sending mail over dialup or hard-wired terminal lines. The NIFTP channel queues mail as files to be transferred using the NIFTP protocol used in the British research community. The UUCP channel is used to queue requests for transfer using UUCP. .PP Development of MMDFII was started about the same time that the Sendmail mail transfer agent was being written by Eric Allman at Berkeley. Dave Crocker met with Eric on a number of occasions and was impressed by his work. Some elements of the Berkeley software were so useful that they inspired the development of similar facilities in MMDFII. Not the least of these was the runtime configuration file. It is now possible to configure the MMDF software totally from a single, ASCII-text-based configuration file. Unlike Sendmail's terse configuration syntax, MMDF uses much more verbose keyword/value pairing for configuration information. MMDFII can be configured either from compiled-in values, for fast startup, or totally from the text configuration file, or from any combination of the two. As a result of the fact that many values can be compiled-in, the usual MMDF tailoring file is one-tenth the size of a Sendmail tailoring file, and can be even smaller, even on a large relay site. The runtime configuration file is one of the most useful additions in MMDFII, especially for sites supporting more than one host, since one can now run the same binaries on all machines of the same type. Another Berkeley-inspired facility was the ability to have an alias file entry that forces a delivery to a file or pipe. While initially insecure, this facility has been made reliable by adding code to /fBsubmit/fR that knows when you are processing an alias file entry and when you are processing some other type of address. Simple ownership of the file containing the address was not considered sufficient protection since there are too many files left writable to the world that are owned by root or other privileged users. Mail which for any reason is known to be undelivemmdf/doc/review/p3 444 0 12 13327 3642650513 7316 .SH The MMDF File Hierarchy .PP The MMDFI queue structure consisted of three directories. The ``msg'' directory contained the files containing actual message text. The ``addr'' directory contained the address list file for each message, and the ``tmp'' directory contained the address list while it was being created before moving it to the ``addr'' directory. The names of the files in ``msg'' and ``addr'' were identical although the contents differed. This made it easy to find the companion file given either filename. The MMDFII queue structure has changed in one major way from that of MMDFI. There is now one extra directory per channel named q.\fIchannel\fR. If an address list still has any addresses destined to be sent on any channels, then a link will exist from the address list file in ``addr'' to a file with the same name in each queue directory which is referenced. All the above directories usually live in /usr/mmdf/lock/home, although the root name of this tree can be changed. The lock directory is kept in 700 or 770 mode, accessible only to the ``mmdf'' user and possibly a ``systems'' group for ease of maintenance. The ``home'' directory and all the underlying queue directories are kept in 777 mode to allow ease of movement and access for the trusted but unprivileged programs that operate there. In particular, the \fBsubmit\fR process is setuid to ``mmdf'' to allow it to enter the tree, but it then restores its UID and GID to those of the invoker. \fBDeliver\fR and the channel programs normally run as the ``mmdf'' user. Only the local channel, which must access the real user's mailbox files, and the TCP/IP network daemons, which must access privileged sockets, need run privileged. There are several other programs that are setuid to ``root'', but only so they can change their UID to ``mmdf'' so as to to be a ``trusted submitter'', which allows them to specify an arbitrary From: line. .PP The addition of the separate queuing directories on a per channel basis was a valuable change. UNIX performs poorly with large directories, as any UUCP backbone site can tell you. This new mechanism allows easy partitioning of channel activities into separate directories, since \fBdeliver\fR will never access the copy of the address list in the ``addr'' directory until it goes to finally expunge the message from the queues. If one channel or site gets backed up, it does not affect the performance of any of the other channels. .PP The format of the address lists has changed somewhat since MMDFI. The old format was: .in +1i .sp .6 S T channel-name host-on-channel address-at-host .in -1i .sp .6 where S was either a `\-' indicating unsent or a `+' indicating that only the address but not the text had been sent. The T was either the type of delivery (almost always `m' for mail) or a `*' indicating the message has been completely delivered. The channel-name and host-on-channel should be obvious. The address-at-host parameter was only the local part of an address. The new version differs in two ways. First, the host-on-channel is now the full domain address of the host to deliver to, as specified in the routing information of the domain table. .FN See the manual section on the MMDF database (queue.5) for more information. .FE Second, the address-at-host is now the full address with both the local-part and the domain specifier. .PP In the future, I will probably change the location of the ``message completely delivered'' flag to be in the first position (S), since I feel it was a mistake to not have it there in the past and it is confusing in its current position. This has not been done as of February 1985. .PP The MMDF file hierarchy also has a logging directory. Separate logs are kept here for \fBsubmit\fR and \fBdeliver\fR, the channel programs, and the phone dialing package. This directory is generally accessible although unreadable and the logs are normally write only. This could be made ``mmdf'' access only for some sites, with the only penalty being that some programs would be unable to add entries to the log. A subdirectory of the log directory called ``log/phase'' contains time stamp files whose modification times are changed by \fBsubmit\fR and \fBdeliver\fR to indicate such things as last pickup time, last delivery time, last poll made, and similar information. .PP There are two other directories in the MMDF hierarchy which should be mentioned. The ``chans'' directory contains all of the channel programs invoked by the \fBdeliver\fR program, and other ancillary daemon programs such as the SMTP daemon and the SMTP server. The ``table'' directory contains all files necessary to maintain the MMDF database, including domain tables, host address files, mailbox alias files, dialing scripts, and programs to build these files and incorporate them into the DBM library. .FN The DBM package is a set of simple hashed database access routines that were distributed with V7 Unix and are still widely used. .FE .PP Use of some sort of keyed database system is almost essential for large MMDF systems, since the table lookup overhead is unbearable otherwise. Currently DBM is the only readily available alternative and it does seem to work well, but it is running out of steam, particularly due to its limited record length. A more flexible replacement for this package would be welcome. .PP The top of the MMDF directory tree contains the directories mentioned above, the \fBsubmit\fR and \fBdeliver\fR programs, and a few maintenance programs. If the site is polled for PhoneNet mail then the ``\fBslave\fR'' program is normally also located here. The \fBslave\fR program is used as the remote site's login shell and acts much as \fBuucico\fR in managing the link level communications. It in turn calls upon \fBsubmit\fR and \fBdeliver\fR to send and receive mail. delivery (almost always `m' for mail) or a `*' indicating the message has been completely delivered. The channel-name and host-on-channel should be obvious. The address-at-host parameter was only the local part of an address. The new version differs in two ways. First, the host-on-channel is mmdf/doc/review/p4 444 0 12 7321 3620510337 7267 .SH Submit .PP For all the changes to its internals, the external interface of \fBsubmit\fR has changed remarkably little since MMDFI. The only notable external changes to \fBsubmit\fR are that it now processes domain-style addresses (both forward and reverse!) and both RFC822 and RFC733 format addresses. .FN D. Crocker, J. Vittal, K. Pogran, D. Henderson, ``RFC733 - Standard for the Format of ARPA Network Text Message'', Network Information Center, SRI International, November 1977. D. Crocker, ``RFC822 - Standard for the Format of ARPA Network Text Message'', Network Information Center, SRI International, August 1982. .FE A couple of new options have been added to \fBsubmit\fR to give some control over the handling of returned mail in the event that a message cannot be delivered. This is useful if the user is a program and does not care if the message is undeliverable! It is also now possible to feed a bare message to \fBsubmit\fR and tell \fBsubmit\fR to find all the addresses and show them and their validity on a one-by-one basis. This makes it convenient to feed mail to \fBsubmit\fR from a smart mail composer that doesn't want to know how to parse addresses (a major task these days!). .PP The \fBsubmit\fR program can operate in one of two modes. In \fIprotocol\fR mode, \fBsubmit\fR accepts options, a return address, and optionally a list of addresses for each message, followed by the message text. Multiple messages can be submitted one after another without reinvoking the \fBsubmit\fR process. Each address is individually acknowledged. If there is an error, the submission of that letter is aborted and a new submission may be made. In \fIone-shot\fR mode a single message is submitted on the standard input. As in \fIprotocol\fR mode, it can be preceded by options and addresses, or the options can be given on the command line and the addresses taken from the message text. .PP The internal address verification process of \fBsubmit\fR has changed greatly since MMDFI. Most of the changes have been made to properly support domain style addresses. Additional changes were made to support per-channel and per-user access controls. While \fBsubmit\fR is checking each address, regardless of origin, it is also compiling the address list for the message. Each address list entry contains the destination domain, .FN By ``domain'' we mean the full domain specification of a host, e.g. VAX1.UDEL.ARPA. .FE the destination mailbox, and the channel in whose channel table \fBsubmit\fR first found the destination host. The lookup is somewhat complicated. The destination domain is looked up in the domain tables; the first entry found is used. Each domain table entry has associated with it the routing to be used to reach that domain/host. Normally this is just the name of the host itself if the host is directly accessible, but it can be a sequence of hosts if the destination is not directly accessible. This ``routing host'' is then looked up in the channel tables to find a channel which can reach it. The routing host specifications and the entries in the channel tables are always full domain specifications, so as to be unambiguous. .PP Authorization is checked after a valid channel for a host is found. Access to send via a given channel depends on the originating channel and/or the submitting user. If access to a given channel is denied, \fBsubmit\fR will continue to look at subsequent channels to see if some other channel has access to the same host and is authorized. This mechanism is commonly used to restrict access to expensive transport systems or to restrict message transfer between channels representing private and public data networks. It can also be used to restrict relaying of messages between two "controlled-access" networks. ftware totally from a single, ASCII-text-based configuration file. Unlike Sendmail's terse configuration syntax, MMDF uses much more verbose keyword/value pairing for configuration information. MMDFII can be configured either from compiled-in values, for fast startup, or totally from the text configurammdf/doc/review/p5 444 0 12 4151 3620510340 7260 .SH Deliver .PP The \fBdeliver\fR process changed little until late 1984, when a dead-host caching facility and advanced retry mechanisms were added to \fBdeliver\fR. Until then, the major changes to \fBdeliver\fR were bug fixes and changes to enhance error recovery. Most notably, the mechanism to return mail was made much more persistent. In MMDFI, if a message could not be returned to the sender, it was ``dropped on the floor''. This is not acceptable. The new mechanism first tries to return to the sender; if that fails, an attempt is made to send the message to the local ``Postmaster'' address. If this too fails, then \fBdeliver\fR tries to write the message into a ``dead letter'' file (usually /usr/mmdf/lock/home/DeadLetters). The last resort is to scream loudly into the log files. .PP The recent changes to \fBdeliver\fR are designed to reduce \fBdeliver\fR's load on a system that handles a large amount of mail. The first change is to move the dead-host caching function out of the channels (currently only SMTP) and into \fBdeliver\fR. This has two advantages. First, \fBdeliver\fR no longer has to hand every address to the channel to find out that the host is dead. This was expensive, since communications between \fBdeliver\fR and the channel are interactive using pipes and thus involved a great deal of context-switching and pipe I/O. Second, the dead-host caching is now a generally available facility that can be used by any channel without duplication of code. .PP The second change is to add a mechanism for storing the retry history for each dead host on each channel including retry count and last retry time. From this information it is possible to implement intelligent retry strategies using exponential backoff and maximal retry times. This greatly reduces the overhead caused by recalcitrant hosts that are unavailable over a long period of time. The retry history change is conditionally compiled into deliver since it can, by design, use quite a bit of memory if there are a lot of dead hosts or pending messages (or both!). Machines with a relatively limited address space may not be able to use this feature. is used. Each domain table entry has associated with it the routing to be used to reach that domain/host. Normally this is just the name of the host itself if the host is directly accessible, but it can be a sequence of hosts if the destination is not directly accessible. This ``routing host'' is then looked up in the channel tables to find a channel which can reach it. The routing host specificationsmmdf/doc/review/p6 444 0 12 11246 3642650541 7320 .SH The Local Channel .PP The local channel remained unchanged until late 1983, when a major reworking of the channel was done by BRL. The old local channel could handle delivery in three basic ways. The first and most common was to deliver directly to the user's default mailbox, appending the message to the end. Second, the user could put a program in his private bin directory called ``rcvmail'' that would be called by the local channel to handle delivery of the message. If the user program (rcvmail) did not complete successfully, a standard delivery was made instead. .PP In early versions of the local channel for MMDFII, Dave Crocker added support for mailing to files and pipes, but the original version had a number of security problems, mostly due to \fBsubmit\fR, so the capability was not much used. .PP The latest version of the local channel has kept the alias-file-originated delivery to files and pipes, and the changes Steve Kille has made to the \fBsubmit\fR program have also made this facility reliable from a security standpoint. The rcvmail mechanism has been totally scrapped in favor of a more general and powerful mechanism which I will call ``mail delivery files''. .PP Mail delivery files were designed to give the user as much flexibility over how his mail was delivered as possible without opening security holes. The mail system was changed to allow local addresses to have a suffix appended which consists of an `=' and any simple text; this suffix is totally ignored except when using mail delivery files. Each user may create a ``.maildelivery'' file in his home directory which contains one or more delivery specifications. A delivery specification has five parts, separated by field separators, \fI<FS>\fR, which may be tabs, spaces or commas ``,''. .sp .5 .ti .6i \fIfield\ \ <FS>\ \ pattern\ \ <FS>\ \ action\ \ <FS>\ \ A/R/\?\fR\ \ <FS>\ \ ``\fIoptional string\fR'' .sp .5 The \fIfield\fR is the name of a field that is to be searched for a pattern. Currently supported fields are \fIFrom\fR, \fITo\fR, \fISubject\fR, \fISender\R, and \fICc\fR, plus three special fields, \fIaddr\fR, \fI*\fR, and \fI*\fR. The \fIaddr\fR field is used to match against the address being delivered to \fIincluding\fR the suffix (e.g. ``dpk=unixwizards''). If the user subscribes to different lists with different suffixes he can use his mail delivery file to segregate his mail by source. To do this based on the message text alone is impossible to do right 100% of the time. The \fIdefault\fR field matches if the message has not been delivered by any of the preceding lines in the ``.maildelivery'' file. The \fI*\fR field always matches, regardless of any other action. .PP The \fIpattern\fR is some sequence of characters that may be matched in the \fIfield\R. Case is not significant, and multiple fields of the same name are concatenated, separated by spaces. If the field does not need as pattern, a dash (-) or similar symbol is usually inserted to show that the field is present but not used. .PP The \fIaction\fR is ``file'' or ``>'', ``pipe'' or ``|'', or ``destroy''. ``File'' or ``>'' appends the message in standard mailbox format to the file specified in the optional string. ``Pipe'' or ``|'' causes the program in the optional string to be run with the message available on the standard input. ``Destroy'' causes the mail to be thrown away silently. This is useful if you go away on a long trip and don't want to unsubscribe to lists, but also don't want to come home to several thousand messages. .PP The \fIA/R/?\fR flag is a single character: `A' for accept, `R' for reject, or `?' for accept if not delivered yet. This flag indicates whether the action, if successful, is sufficient to mark the message as delivered. If the message is undelivered at the end of the .maildelivery file, the local channel next consults a system-wide file, such as \fI/usr/lib/maildevliery\fR. If the message is still undelivered at the end of the system-wide file, a standard delivery is made to the default mailbox. This protects against mail being lost due to lack of foresight or errors in the maildelivery files. .PP The file is always read completely so that several matches can be made, and several actions taken. For example, the user could have a TTY alert message sent to his terminal and also have the message resent to his new home machine by the following .maildelivery file: .sp .5 .in +.6i addr\ \ \ \ dpk\ \ \ \ pipe\ \ \ R\ \ ``/usr/mmdf/mailutils/ttyalert'' .br addr\ \ \ \ dpk\ \ \ \ pipe\ \ \ A\ \ ``/usr/brl/bin/resend dpk@brl-vgr.arpa'' .in -.6i .sp .5 The last line, if completed without error (a return code of 0 from resend), would mark the message as delivered because of the A (accept) flag in the fourthcolumn. endmail tailoring file, and can be even smaller, even on a large relay site. The runtime configuration file is one of the most useful additions in MMDFII, especially for sites supporting more than one host, since one can now run the same binaries on all machines of the same type. Another Berkeley-inspired facility was the ability to have an alimmdf/doc/review/p7 444 0 12 7126 3620510340 7267 .SH The List Channel .PP The list channel was developed as the result of BRL's experience in managing \fBlarge\fR Internet mailing lists. Two major problems were discovered with dealing with mailing lists in MMDFI. First, since \fBsubmit\fR always verifies all known addresses for a message at submission time, if you were on the machine with a large list and submitted mail to the list you would have to wait for every address on that list to be verified. On a busy machine with a list of hundreds of addresses, this could take five or ten minutes. Annoying as this may be for people, the situation was worse for mail submitted over communications channels like TCP/IP. The remote end would continually time out before the message had been completely verified so the message could never be sent. .PP The second problem with large lists was that there were always rejected mail notices going back to those least able to do anything about the mail problem, the original sender of the message. What was really desired was a method to try to have returned mail go to the list maintainer instead of the message's original sender. .PP The solution to both of the problems is embodied in the list channel. This is a channel with an incestuous relationship to \fBsubmit\fR, \fBdeliver\fR, and the alias file. Use of the list channel is best described in parallel with the special entries for the list channel in the alias file. If we were maintaining a large list called ``biglist'', the following entries would be in the alias file: .nf .ta 2.6i .sp .5 .in +.6i biglist: biglist-outbound@list-processor .br biglist-outbound: </usr/mmdf/lists/biglist-file .br biglist-request: maintainer .in -.6i .fi .sp .5 The pseudo-host ``list-processor'' has its own domain table and its own host table but represents no actual host. If someone submits mail to biglist, \fBsubmit\fR will find the alias entry and upon finding that it's not a local address, will queue it to the host ``list-processor'', so verification is complete after only one lookup. Unknown to \fBdeliver\fR, the list channel simply calls \fBsubmit\fR and feeds it the aliased addresses, ``biglist-outbound''. This time the actual verification is done on the contents of the address list ``biglist-file''. Since the list channel is processed by a background daemon, no one is forced to wait through the verification process except the background daemon itself, which doesn't care how long it takes so long as it completes. .PP The list channel also performs another function to try to eliminate the problem of failed mail messages. For each address given to it, (normally just one), the list channel sees if there is a matching ``\fIlistname\fR-request'' entry in the alias table. It knows enough to try stripping any ``\-outbound''s from the name first though. If a ``\-request'' entry is found, then that address is substituted instead of the original return address. The message text is \fInot\fR altered, but the new return address is recorded for use when resending the message. The new return address is supplied in SMTP ``MAIL FROM:<\fIaddress\fR>'' commands and any other situations where the return address is directly specifiable. .PP The changing of the return address is useful only if mail is rejected when submitted to the foreign host or if that host is smart enough to keep the return address information around. Many hosts do not maintain this information, and many of the same hosts are also problematic in that they will completely accept a message containing total garbage and decide to tell you about it later. This is precisely what MMDF tries to avoid by submission-time verification. nce in managing \fBlarge\fR Internet mailing lists. Two major problems were discovered with dealing with mailing lists in MMDFI. First, since \fBsubmit\fR always verifies all known addresses for a message at submission time, if you were on the machine with a large list and submitted mail to the list you would have to wait for every address on that list to be verified. On a busy machine with a list of hundreds of addressesmmdf/doc/review/p8 444 0 12 7555 3620510340 7276 .SH The BBOARDS Channel .PP Sites that run the MH message system, version mh.5, may install a \fIbboards\fR channel which delivers messages from interest-group mailing lists to a special ``bboards'' directory . The bboards software, which is compatible with the MH message system, keeps track of which messages have been seen by individual users, and allows designated bboards managers to control the size and access for different bboards. .FN See the \fIman\fR entry mh-gen.8 in the MH distribution. It is important to note that the choice to install ``bboards'' must be made when MMDF is generated. The ``news'' facility mentioned in the MH documentation is not supported by CSNET. .FE .SH The UUCP Channel .PP The task of integrating UUCP mail into MMDF was a prime goal for BRL. Our users would not tolerate having to use two radically different mail interfaces for two different kinds of mail connections. We decided to write a channel to interface to the UUCP mail world that would take care of the necessary format conversions to allow mail to traverse the two mail worlds. The channel has two parts. The input portion of the channel is the program \fB/bin/rmail\fR which is executed by the UUCP program \fBuuxqt\fR when mail is being delivered. The output portion is a standard channel that invokes the UUCP system after reformatting the message. .PP The \fBrmail\fR program has been totally rewritten to interface to MMDF. \fBRmail\fR's primary task is to collect and reformat the address strings in the message. To reformat each address, \fBrmail\fR uses the UUCP channel table to determine what hosts are known to this host and shortens an host!host!host! string down to the single most distant host we know about and any subsequent hosts we do not know. For example knownA!knownB!knownC!unknown1!unknown2!user would become knownC!unknown1!unknown2!user. It then converts this to an RFC822 style address by putting the unknown hosts and the user in the local part and putting the known host with a domain in the domain portion, e.g. unknown1!unknown2!user@knownC.UUCP. If all the hosts are known, then only the user is left in the local part, and the address winds up being user@known.UUCP. Since you always know the hosts you talk to, you can build any arbitrary UUCP path by simply saying arbitrary-host-path@neighbor.UUCP. .PP \fBRmail\fR is prepared to accept destination addresses in two forms. If the addressee is just another UUCP host addressed using host!host!... notation, then \fBrmail\fR forwards the letter via UUCP without header munging since the destination host may not support RFC822 style mail. An addressee of the form user@domain will cause the message to be fed to \fBsubmit\fR and into MMDF proper where the message can be delivered to another UUCP site or any other site accessible via MMDF. \fBRmail\fR will reformat the message header in the latter case to conform as much as possible with the RFC822 specifications. .PP The outbound portion of the UUCP channel is a MMDF channel program called ``uucp'' which is invoked by \fBdeliver\fR. The job of this program is much easier since all it must do is reformat the ``From:'' line to be compatible with UUCP mail. The outbound channel must also reformat the destination addresses which become arguments to \fBuux\fR. The outbound channel uses the same channel table that \fBrmail\fR used but performs the reverse action on the address so, for example, root@mcnc.UUCP becomes unc!duke!mcnc!root and this is then further divided to form the uux command ``uux unc!rmail duke!mcnc!root'' (assuming the channel table maps mcnc.UUCP into duke!unc!mcnc). .PP The UUCP channel would have to be classed as the only ``flakey'' portion of MMDF since some of these address transformation really need an advanced AI system to make an intelligent transformation. In general, though, the channel does a very good job and has little trouble with ``normal'' UUCP addresses. different kinds of mail connections. We decided to write a channel to interface to the UUCP mail world that would take care of the necessary formammdf/doc/review/p9 444 0 12 7337 3620510340 7275 .SH Using Domain Name Servers .PP The use of domain name servers will have some interesting effects on the address verification aspects of mail submission. In the current system, all the information necessary for verification of addresses is in a local data base. When we are using name servers, we can no longer be guaranteed that all the needed information will be locally cached. In addition, we are not guaranteed that we will be able to reach all the necessary name servers at submission time (although duplicate name servers will make this possibility small). The \fBsubmit\fR program will call the local domain resolver to verify each address, and there will be some time limit in which to complete this task. The resolver will be expected to first consult the local cache of domain data and, if the information is not found, contact as many servers as necessary to resolve the address. .PP The possible lack of information will force us to provide a contingent submission queue for those messages that cannot be verified at submission time. This does not imply that there will we be no verification. We will verify that we at least know the top level domain of each address and verify each sub-domain when possible. If some sub-domain of the full address is known to be bogus, the address can be flushed. Knowing that we have authoritative information that a domain does not exist is just as important as knowing that it does exist. .PP A new channel, much like the list channel, will be used to process the partially-accepted address for a message. This channel will continually try to verify the address until it is known to be good or bad. It will have the message returned to the sender, with an explanation, if one or more addresses is bad. Most systems will run with a fairly rich cache of host information. For those systems which cannot afford to keep this information around, the submission time verification might be a considerable delay which would be unacceptable for a user interface. On these systems it will possible to force all message to be accepted for background verification (via the "verification" channel). .SH Conclusion .PP MMDFII had some early problems and as a result may have gotten some initial bad press, but MMDFII has shown that it is a capable mail system which is both robust and able to handle very large mail loads. There now exist a growing number of tools to analyze and manage large flows of mail in a MMDF system. These tools include status programs, sophisticated logging, and log analysis programs. Because of the separation of mail into separate queues, multiprocessing of the mail queues is not only possible, but routinely used to both increase throughput and decrease delays. MMDF is also a flexible system. Runtime reconfiguration is simple, generally easy to understand, and can be done at any time. Since the MMDF core software is free of channel specific or network specific information, one can easily add additional channels for new networks or protocols without affecting the existing software. MMDFII represents a stable, production mail system, providing a strong base for the development of new network interconnections and mail handling environments which are essential in today's distributed computing environment. .sp 2 .PP The MMDFII software is available under license, free of charge (with the possible exception of a tape copy fee), for internal use only as follows: to U.S. Government agencies through the Ballistic Research Labs, to CSNET sites through the CSNET Coordination and Information Center at BBN, and to others through Prof. David Farber at the University of Delaware, Electrical Engineering and Computer Science Department. Commercial concerns interested in MMDF for other than internal use should contact Prof. Farber. he full address is known to be bogus, the address can be flushed. Knowing that we have authoritative information that a domain does not exist is just as important as knowing that it does exist. .PP A new channel, much like the list channel, will be used to process the partially-accepted ammdf/doc/review/fnmacro 444 0 12 75 3620510341 10323 .nr Fn 0 +1 .de FN .ps -2 \u\\n+(Fn\d\ .ps +2 .FS \\n(Fn .. ng of the mail queues is not only possible, but routinely used to both increase throughput and decrease delays. MMDF is also a flexible system. Runtime reconfiguration is simple, generally easy to understand, and can be done at any time. Since the MMDF core software is free of channel specific or network specific information, one can easily add additional channels for new networks or protocols without affecting the existing software. MMDFII represmmdf/doc/administrators/ 755 0 12 0 3671074607 10510 mmdf/doc/administrators/building 444 0 12 134447 3656410704 12363 .\".AU .\"Douglas P. Kingston III .\".AI .\"Ballistic Research Laboratory .\"USA AMCCOM .\"Aberdeen Proving Ground, Maryland. 21005 .\"(301) 278-6651 .NH 1 Building the \*M System .NH 2 Organization of Source .PP The \*M sources are arranged in a hierarchical directory structure under a directory such as /usr/src/local/mmdf. The major directories at the top level are: .IP \fBconf\fR 10 The \fBconf\fR directory contains subdirectories for each of several sample sites, for example, csnet-relay or okstate. Each of these subdirectories contains most of the files necessary for compile-time configuration of the \*M system. .IP \fBdoc\fR 10 The \fBdoc\fR directory contains the NROFF/TROFF source for all \*M documents except manual pages. .IP \fBh\fR 10 Include files used by MMDFII. .IP \fBlib\fR 10 The \fBlib\fR directory contains the source to the \*M library (libmmdf.a) which is used in compiling all the other \*M programs. There are five subdirectories in the \fBlib\fR directory: \fBaddr\fR, \fBdial\fR, \fBmmdf\fR, \fBtable\fR, and \fButil\fR. .IP \fBlib/addr\fR 15 contains the address parser. .IP \fBlib/dial\fR 15 contains the PhoneNet dial in/out package. .IP \fBlib/mmdf\fR 15 contains \*M specific shared routines. .IP \fBlib/table\fR 15 contains the table lookup functions. .IP \fBlib/util\fR 15 contains a variety of utility and system interface routines. .IP \fBlibndir\fR 10 The ``new'' directory access routines for non-Berkeley Unix systems. This directory may not be included on some distributions (e.g. 4.3BSD where it is standard). .IP \fBman\fR 10 The \fBman\fR directory contains the source for all the \*M manual pages. .IP \fBmh\fR 10 The sources for the MH system. (Not discussed here; see the online documentation. May not be included in all distributions.) .IP \fBsamples\fR 10 The \fRsamples\fR directory contains subdirectories for each of several sample sites. Each site typically contains the runtime-tailoring file (mmdftailor) and other files such as tables, dialling scripts and helpful shell scripts. Large tables will have been truncated to a representative set of entries. .IP \fBsrc\fR 10 The \fBsrc\fR directory contains the source to all \*M programs except for the mail posting and mail reading user agent programs (e.g. \fImsg\fR, \fIsend\fR, and \fIv6mail\fR). Within this directory is a subdirectory for each major program or program set, and one directory called \fBtools\fR for miscellaneous programs. .IP \fBtestmmdf\fR 10 The \fBtestmmdf\fR directory is a complete \*M operational subtree that can be used to install a test version of \*M in parallel with another mail system (even another \*M) on the same system. It also serves as a model of what the \*M subtree should look like. .IP \fBuip\fR 10 The \fBuip\fR directory contains user interface routines, primarily \fImsg\fR and \fIsend\fR. The subdirectory \fBother\fR contains a number of other user programs for dealing with mail. The subdirectory \fBucbmail\fR contains a recent version of Berkeley's Mail program. It has been modified to interface well to \*M. .PP The source directories are set up to use a hierarchical set of Makefiles. The directories \fBlib, src\fR, and \fBuip\fR contain master Makefiles. In each subdirectory is a file called Makefile.real and a program (shellfile) called .I gen. The .I gen program calls .I make with two parts, Makefile.real and a ``common makefile'' called Makefile.com that is contained in a parent directory. Makefile.com contains configuration specific information such as compiler flags, installation directory names, #ifdef flags, and the dependency generation code (for ``make depend''). .PP There is a strict convention for the format of #include lines in \*M source files. If you fail to follow the convention, you will break ``make depend'' and thereby your makefile dependencies. The format is as follows: .sp .ne 10 #include <file.h> .br .ti 10 For standard include files in /usr/include .br #include <subdir/file.h> .br .ti 10 For files in subdirectories of /usr/include. .br #include ``file.h'' .br .ti 10 For files in the \*M header file directory (typically ../../h). .br #include ``/fullpath/file.h'' .br .ti 10 For specifying a particular header file by full path name. .br #include ``./file.h'' .br .ti 10 For a file in the current directory. .PP There is a problem with the current ``make depend'' methodology on systems that do not have compiler support for it. Currently only 4.3BSD has this support through the ``\-M'' option to .I cc. As a result, there are two version of the ``depend'' actions in Makefile.com. On systems that do not have .I cc \-M, due to the way the dependencies are generated, there is no preliminary processing of the conditional compile lines. Files that conditionally include either one or another file appear to the ``make depend'' code as though they included both files. This problem does not show up until you are running the .I make program for the first time. .I Make will complain about not being able to make some header file that does not exist on your system. The remedy is simply to edit the makefile that broke and delete the line containing the file that doesn't exist. You can then run .I make again. .PP We regret that this process is necessary, but at least it only has to be done once per site. Luckily there are only a few files (maybe 5) that are affected. Look out for the following problem files: termio.h, ioctl.h, and fcntl.h. .NH 2 Getting Ready .PP \*M requires a significant amount of space for the sources, documentation, and binaries (about 8 to 10 megabytes for a full distribution). The 4.3BSD distribution will take about 3 megabytes for the basic sources, and documentation. The running system will consume much less, about 2 megabytes of installed binaries and tables, and you can remove your old mail programs. You will need superuser privileges. The distribution you receive may inadvertently contain old binaries or libraries despite the best efforts of those preparing the distribution. It is recommended that you do a ``make clean'' in the directories .B lib, .B src, and .B uip before starting. You can accomplish this by running ``make clean'' from the top directory of the MMDF source tree (the parent of \fBlib\fR, \fBsrc\fR, and \fBuip\fR). This will give you a clean slate upon which to base your compile. .XX Do a ``make clean'' in the MMDF root directory. .NH 2 System Compile-Time Configuration and Generation .PP The configuration directory, .B conf, contains one directory for each site being supported that has compiled-in differences. Many sites may share one compile-time configuration by using the run-time tailoring facility to specify site-specific information. There is also a shell program called .I sitesetup that is used to install a specified configuration. The test configuration is set up to run out of the \fBtestmmdf\fR subdirectory and should only be used for testing. You will base your running configuration on an existing system with possibly extensive changes. The following site configurations are available for copying: .sp .IP bbn 12 A 4.2BSD Vax Internet host using domain servers for table lookup. .IP 4.3vax 12 A 4.3BSD Vax running a minimal set of tables and using the badusers and badhosts channels to pass off unknown addresses to MCVAX.UUCP (haring.uucp). .IP csnet-relay 12 A very large PhoneNet relay host with Internet access. .IP okstate 12 A V7 Perkin-Elmer host on CSNET PhoneNet, UUCP and a local network. .IP uclvax2 12 An Internet host with links to various UK networks; illustrates authorization on a per-user basis. .IP vgr 12 A 4.2BSD Internet host. .PP These configurations are supplied as examples and templates for building your own configuration. Do not modify the contents of any of these configurations. You will make your own site's directory in the \fBconf\fR directory. .XX Make a directory named after your site in the \fBconf\fR directory. .XX Copy the contents of some other system's directory into your directory. .NH 2 Compile-Time Tailoring conf/\fIsite\fB/conf.h .XX Edit the file conf.h .PP The constants at the top of this file should not need tailoring. Possible exceptions might be NUMCHANS, NUMTABLES, and NUMDOMAINS. These limit the number of domains, channels, and tables (channel, domain, alias, and others), The numbers supplied should only need to be increased on a site acting as a major relay site with a large number of channels. The system will complain in the logfiles if the limits are reached. The ``DL_'' definitions control certain basic properties of the dial package used by phone and slave programs for PhoneNet. They should probably not be modified. If the dial package is not used, they are ignored. .PP The section in conf.h entitled ``JNTMAIL Tailoring'' contains #defines that are useful primarily in the United Kingdom and gateways to it, like UCL-CS. The JNTMAIL definition is used to enable code that is used on the JNT mail channel. The BOTHEND definition enables \*M to parse domain names in either ``little-endian'' or ``big-endian'' format. ``Little-endian'' is the standard format in the DARPA Internet and is the format defined in RFC-822. ``Big-endian'' is used in the UK community, and perhaps elsewhere. VIATRACE causes the relaying timestamp to conform to UK standards by starting with ``Via:'' instead of the Internet standard ``Received:''. Despite the best efforts of some British, this remnant remains from the days of RFC733. Non-JNT systems will leave JNTMAIL, BOTHEND and VIATRACE undefined. .PP The next section of conf.h contains the #include lines necessary to include the file time.h and dir.h (as is used with the libndir package). The former is typically /usr/include/time.h or /usr/include/sys/time.h (a.k.a. <time.h> or <sys/time.h>). The latter may be called ndir.h on some systems. This is typically /usr/include/dir.h or /usr/include/ndir.h (a.k.a. <dir.h> or <ndir.h>). .XX Check where your time.h and dir.h are and fix the #includes, if necessary. .PP The final section of conf.h is used to define a macro that will identify privileged UIDs that are likely to be submitting mail on behalf of other. We have defined it as a parameterized macro in the distribution, but you could undefine the macro and write a real function called PRIV_USER() if necessary. When used as a macro, it is assumed that the global variable .I effecid will contain the UID of submit. Currently only submit uses this macro/function. The uid for \*M (effecid) is determined at runtime. .XX Check that the #define has reasonable UID values (root, daemon?, and effecid). .NH 2 Compile-Time Tailoring conf/\fIsite\fB/conf.c .XX Edit the file conf.c .NH 3 Public Names .PP The first section is PUBLIC NAMES. The string .I mmtailor is the location of your runtime tailoring file. This is typically .I /usr/mmdf/mmdftailor. .PP The .I locname string should be set to your host name without the domain portion. For example if your host is SPOOK.BRL.MIL, then your locname should be ``SPOOK'', and your .I locdomain should be ``BRL.MIL''. Case is not important, but it will be preserved in some cases, so enter case as you would like to see it. We prefer to use all caps. .I Sitesignature should not be altered as it is an indication of which mail system you are running. .I Mmdflogin is the login id that you have chosen to give to the \*M mail system. This is usually ``mmdf''. The .I supportaddr string gives the address that the mailsystem will send problem notifications to, and this is also the address that will be placed in the from field of messages generated by the mail system. This address should reach the person or persons maintaining the mail system. Normally, it is defined as ``Postmaster@\fIlocname.locdomain\fR'', which is, in turn, an alias for the appropriate systems people. .XX Configure \fIlocname, locdomain, mmdflogin\fR, and \fIsupportaddr\fR. .NH 3 \*M Directories Locations .PP The next section, DEFAULT BASE DIRECTORIES, defines the location of certain \*M directories. The comments are fairly clear about the use of most of them. The locking directory will only be used if the link based locking mechanism is used. If it is, you should make sure this direcory is cleared at each reboot. .I Tbldbm is not actually a directory but is instead the basename of the DBM style database used by \*M to store tables for fast lookup. See the manual section \fIdbm(3)\fR for more information. Systems using System III or System V will have to obtain this code from another Unix site, preferably a 4.2BSD site. The original V7 version of the DBM library only provides for the files to be opened read/write. If you have this version you will need to modify it to try opening the files readonly if read/write access fails. The typical values are as follows: .sp .in +1i .KS .TS l l . \fIcmddfldir\fR /usr/mmdf \fIlogdfldir\fR /usr/mmdf/log \fIphsdfldir\fR /usr/mmdf/log/phase \fItbldfldir\fR /usr/mmdf/table \fItbldbm\fR /usr/mmdf/table/mmdfdbm \fIquedfldir\fR /usr/mmdf/lock/home \fIchndfldir\fR /usr/mmdf/chans \fIlckdfldir\fR /tmp/mmdf .TE .KE .sp .in -1i .XX Configure directories, if necessary (unlikely). .NH 3 Default Logging Parameters .PP The next section, DEFAULT LOG LOCATIONS & SETTINGS, controls the main logging facilities of the \*M system. There should not be any tailoring necessary here. The .I mlogloc and .I chlogloc variables are somewhat magic, in that if they are simple file names, then they are opened in the default logging directory (\fBlogdfldir\fR above). If they are full pathnames, then they will be opened as specified. They must exist for any logging to take place. If they do not exist, then no logging will take place. Another possible tailorable item is the default logging level, which is the third element of the ll_struct structure. See the explanation of the ll_struct structure in llog(3) before altering. This is normally tailored from the runtime configuration file. .XX Adjust \fImlogloc\fR and \fIchlogloc\fR, if necessary. .NH 3 Message Queue Parameters .PP The section MESSAGE QUEUE SUBSTRUCTURE has only two tailorable entries, .I warntime and .I failtime. .I Warntime is the minimum number of hours before \*M will send a warning letter indicating that a message has not yet been delivered. After .I failtime hours since submission, a message is eligible to be returned as undeliverable. Actual timing of warnings and returns will depend on how frequently the .I cleanque program is run. Neither of these actions will occur unless the program is run. It is suggested that .I warntime be at least 1/2 day and more like 2 or 3 if you are connected to a large network (e.g. the Internet) where there is a significant chance that a host will be down for a couple of days on occasion. .I Failtime should be at least 3 days, especially on large networks. You should be able to take a machine being down over a long weekend without returning mail. .XX Choose the \fIwarntime\fR and \fIfailtime\fR values (in hours). .NH 3 Command Names and Locations .PP The section COMMAND NAMES & LOCATIONS should only need tailoring if you do change the names of the programs mentioned (something you do at your own risk). .NH 3 Submit Parameters .PP The value .I maxhops in the section SUBMIT TAILORING is the maximum number of times a message may be relayed as indicated by the number of Received: lines before the message is considered dead due to looping. It is possible that this value may have to be made significantly larger in the future to handle mail that has passed to us through numerous RFC-822 UUCP hosts. For now, we have used the value 20. .PP The \fIlnk_listsize\fR value specifies the maximum number of addresses in a message before it is considered to have a ``big'' list. If there are more than the maximum number of addresses, then \*M will not send a warning message for waiting mail and will only return a ``citation'' for failed mail. A ``citation'' consists of the entire header plus the first few lines of the message body. .PP The \fImgt_addid\fR will cause \fIsubmit\fR to guarantee the existence of a Message-ID: line on every message entering the \*M system. A Message-ID will only be added if it is missing. .XX Set the value of \fImaxhops\fR, \fIlnk_listsize\fR, and \fImgt_addid\fR. .NH 3 Deliver Parameters .PP The section DELIVER TAILORING controls the deliver program. .I Maxqueue is the maximum number of messages that will be tolerated before deliver stops sorting the mail queue before delivering. If there are more than .I maxqueue messages in the channel's queue, deliver will process the queue in the order it is read which will probably not be the order in which messages were queued. A suggested value is 300. This value can be larger if you do not have any dial-in sites that might time out waiting for you to find a message in pickup mode. .I Mailsleep is the number of seconds that the daemon sleeps between sweeps on the mail queues looking for messages to deliver. The value is typically 600 (10 minutes). .XX Set the values of \fImaxqueue\fR and \fImailsleep\fR. .NH 3 Address Transformation .PP The section ADDRESS TRANSFORMATION should not be altered. This section was used to override the default header munging behavior. It has been superceded by a more general mechanism, but this table is kept in case the capability is needed again in some special situation. It cannot be runtime tailored. .NH 3 Local Delivery Options .PP The next section, LOCAL DELIVERY TAILORING, has several variables that can be tuned to local requirements. The .I dlvfile variable is the name of the maildelivery control file (see the manual section \fImaildelivery(5)\fR). This should be ``.maildelivery'' to be standard and avoid confusion with the documentation. .I Sysdlvfile is the full path of the name of a maildelivery file to be used if the user does not specify one. If this is set to `(char *)0', then there will not be a default maildelivery file. This file must be owned by root and generally unwritable. .I Mldfldir is used to control the location of default mailboxes. If the variable is set to 0, the mailbox will be created in the user's home directory. If it is non-zero, it will be treated as the name of a directory in which the mailbox name will be the same as the username (old V7 style mail boxes). .I Mldflfil is the name to call the mailbox if the mailbox is going to be in the user's home directory. .I Mldfldir and .I mldflfil have an inverse relationship. Only one should be defined at a time, the other should be zero. We recommended that .I mldfldir be zero and that .I mldflfil be ``mailbox''. Another common value for .I mldflfil is ``.mail''. The strings .I delim1 and .I delim2 define the message prefix and message suffix respectively for mailbox files. These should probably not be altered as some user programs may still have these values hardcoded to ``\\001\\001\\001\\001''. If you are introducing MMDF for the first time at your site you will not have this problem, but it is still unwise to change the delimiter unless you have a very good reason. .XX Set the values of \fImldflfil\fR and \fImldfldir\fR. .NH 3 UUCP Channel Parameters .PP The section UUCP CHANNEL TAILORING controls the behavior of the .I rmail and UUCP channel programs, the input side of the UUCP channel. The things to tailor here are the strings .I Uchan and .I Uuxstr. .I Uchan defines the default channel that rmail will indicate as the source channel for incoming UUCP mail. This should probably be ``uucp''. The string .I Uuxstr controls UUX invocation. One or both of the strings in this part may be removed in the future as the UUCP channel is modified to use more of the basic channel configuring facilities. .br .XX Adjust the strings \fIUuxstr\fR and \fIUchan\fR. .NH 3 NIFTP Channel Parameters .PP The section headed NIFTP CHANNEL TAILORING is only for UK sites running the JNTmail code. Tailoring is self explanatory (JNTmail spool location). .NH 3 Multiple Host Feature Parameters .PP The variable .I locmachine in the section MULTIPLE HOST TAILORING must be changed to contain your machine name. This is the simple identifier that distinguishs your host from others at your site. This is used by code that makes a collection of hosts look like one site. This string can also be set in the runtime configuration file for sites that want to run common binaries. This is normally the name of the host minus the domain suffix (e.g. VGR for VGR.BRL.MIL, or BRL-ORANGE for BRL-ORANGE.ARPA). .XX Set \fBlocmachine\fR to a string appropriate for your machine. .NH 3 Authorization Parameters .PP The last section, AUTHORIZATION TAILORING, is tailoring for the authorization code (`authorisation' for you folks in the UK). The .I authrequest string is the address of someone who should get notices of requests for authorization. The .I authfile string is the file which should contain a politely worded warning about usage of a restricted path. The paper on authorization in the \fIdoc\fR directory, \fIConfiguring MMDF Authorization\fR by Steve Kille, describes this facility in greater detail. Typically the address used for .I authrequest is simply the same as .I supportaddr in Section 2.5.1 of this paper. .XX Choose appropriate strings for \fIauthrequest\fR and \fIauthfile\fR. .NH 2 Compile-Time Tailoring conf/\fIsite\fB/chan.c .PP The file chan.c is used to tailor certain channel and table specific structures of \*M. If you plan on using runtime configuration you can simply skip this section since this need only be tailored by systems that plan to hardcode in all channel and table information for speed or size considerations. .XX Edit the file chan.c (if you are not using runtime configuration). .PP The variable .I ch_dflnam defined the name of the default channel to which mail is submitted. In any normal system, this should be ``local''. The definition for the head of the list of alias sources follows ( Alias *al_list = (Alias *)0 ). This should not be altered. The following section defines two more tables (\fItb_mailids\fR and \fItb_users\fR). These are only used if the flag variable \fImid_enable\fR is non-zero indicating that you wish to use the Mail-IDs feature of \*M. The Mail-IDs feature disassociates mail addresses from login names. If Mail-IDs are disabled, you should still leave the table definitions installed as they are referenced in other modules even when not used. .XX Set \fImid_enable\fR to either 0 or 1 (1 enables Mail-IDs). .PP In the next section you should define and initialize channel, table, and domain structures for all your channels if you are using a hardcoded configuration. This is not common and requires a reasonable knowledge of C and structure initialization. See the file h/ch.h for a definition of the channel structures, h/tb* for table structures, and h/dm* for domain table structures. Once these are defined and initialized, proceed to add all your tables to the arrays .I chsrch, .I exsrch, and .I tblist. You will also have to make sure you do not exceed the maximum array sizes (NUMTABLES, NUMCHANS, and NUMDOMAINS). (If necessary, increase these values). Finally count up the number of pointers in each array and update the variables .I tb_numtables, .I ch_numchans, and .I dm_numtables. .XX Add pointers to your structures to the appropriate arrays. .XX Adjust the xx_numwhatits variables to indicate number in use. .NH 2 Compile-Time Tailoring conf/\fIsite\fB/conf_dial.c .XX Edit the file conf_dial.c .PP This file is used to hardcode information for the dial-out package used by the PhoneNet channel (phone/slave). You can ignore this file if you do not intend to use the PhoneNet code. Although you can hardcode the information for ports (dial-out lines) and lines (directly connected lines, no separate dialer), the common practice is to use runtime configuration. If you want or need to hardcode, see the d_*.h files for definitions of the contents of these structures. As you add entries to the structures, be sure to update the .I d_num{prts,lines} variables and don't exceed the definitions for NUMPRTS and NUMLINES. These maximums must be set even if you do runtime configuration. If you expect to need more ports or lines than those #defines allow, even if doing runtime configuration, then increase the size of these definitions appropriately. .I D_calllog is the file in which the dialing software will record call attempts for billing or statistics purposes. Choose an appropriate place. Finally, the .I d_dialprog variable is the name of a program that will do dialing for your dial out ports if you have such a program. This variable is obsolete and will likely be removed in the near future. .XX Check NUMPRTS and NUMLINES; adjust \fId_calllog\fR if necessary. .PP The section PHONE RELAYING TAILORING is for controlling the behavior protocol layer of the dial package. The arrays d_lrill and d_lxill have a bit for each of the 128 ASCII characters. If the bit is set, then that character will never be sent directly, but will be sent using a two character encoding in printable ASCII. These should not need altering unless your system is quite non-standard and use of a certain character not already disabled causes your terminal driver to not pass the character directly to the user. .PP The section PHONE LOGGING is similar to the logging section in conf.c You may wish to adjust the path of \fIph_log\fR. The same is true of the string .I def_trn. .I def_trn is a transcript file which is not size limited, but is recreated at the beginning of each call. .NH 2 Compile-Time Tailoring conf/\fIsite\fB/Makefile.com .PP .XX Set HOST to match your configuration directory name .PP The SYSTEM definition is used to select the appropriate versions of several files that are system type dependent. Currently, we support SYSTEM types of `4.1', `4.2', `v7', and `5.2', corresponding to Version 7, 4.1BSD, 4.2BSD and System 5 Release 2 respectively. For 4.3BSD, use `4.2' (you will need to compile the inetd version of smtpd manually if you don't want the standalone daemon). Most (if not all) of the systems in current use can be fit into one of these four types. .XX Set SYSTEM to the type for your system. .PP MMPREF is simply the string to be appended to the front of every command when installed. This is a seldom-used kludge retained for system testing. It should be blank. The definitions of LIBDIR, CHANDIR, and TBLDIR must coincide with the variables \fIcmddfldir\fR, \fIchndfldir\fR, and \fItbldfldir\fR in conf.c (tailored above). Typically these are /usr/mmdf, /usr/mmdf/chans, and /usr/mmdf/table respectively. BINDIR is the directory into which you wish to install the user interface programs. Typically, this is /usr/bin or /usr/local/bin. On BRL Unix systems this is /usr/brl/bin. RCVDIR is the directory into which you wish to install the various ``rcvmail'' programs. These are automatic mail receiving programs that are activated by the user's installation of a .maildelivery file to control the delivery of mail to files and pipes. The directory should be generally accessible but write protected for security. A typical value is /usr/mmdf/rcvmail. .XX Set the definitions for MMDF directories. .PP The next section relates to access control. CHOWN should be set to the full pathname of the .I chown program on your system. Normally this is either /bin/chown or /etc/chown. MMDFLOGIN is the name of the account created for use by the mail system, and must agree with the variable \fImmdflogin\fR in the file conf.c (in Section 2.5.1 of this paper). ROOTLOGIN is the name of the superuser account on your system (first line in /etc/passwd), typically `root'. PGMPROT controls the default mode for executables. It should be no less restrictive than 511 and no more permissive than 775 if you have a systems group for maintenance activities. We recommend 755. .XX Tailor CHOWN, MMDFLOGIN, ROOTLOGIN, and PGMPROT if necessary. .PP The CONFIGDEFS line contains a wealth of tailoring, some of which may be peculiar to your system and not known to me. The line is used to define C preprocessor variables. To define a C preprocessor variable, prepend "-D" to the variable and list it on the CONFIGDEFS line. For example, to define DEBUG level one, you would add -DDEBUG=1 to CONFIGDEFS. CONFIGDEFS is used to build both the CFLAGS and LFLAGS option lines (these lines should end with $(CONFIGDEFS)). The available preprocessor variables are: .IP DOASSIGN 15 Enables the d_assign code in the dial package. This code calls the program /bin/assign to gain exclusive access to a file. It does not appear that the d_assign code is ever used so don't bother defining DOASSIGN. .IP SECURETTY 15 Many sites are running their systems with TTYs in more secure mode than generally writable. Usually these systems use the execute bit to indicate write permission and have a privileged program make the access. If you are such a site, you will want to investigate the effect of SECURETTY and modify it if necessary. Vanilla sites should not define SECURETTY. BRL VAX UNIX sites must define SECURETTY. .IP DEBUG 15 Most sites should enable DEBUG=1 unless there is a serious crunch for space. This will give you fairly detailed debugging of the system if you need it. Setting DEBUG=2 will include even more debugging for address parser and reformatting code. If you do not enable DEBUG=1, you will seriously affect your ability to trace problems later. Once you have your system up an running reliably you can recompile it without DEBUG of either kind if you want the space and the very minor performance increase. .IP D_LOG 15 The same caution applies for D_LOG as for DEBUG. This variable controls logging in the dial package. If you are tight on space or if you don't use the dial package (no phone or pobox channels) you may safely omit D_LOG. .IP D_DBGLOG 15 This controls more debug logging for the dial package. Again, if you have the space and if you use the dial package (PhoneNet), define it to allow extensive tracing if problems arise later. .IP RUNALONE 15 This makes the channel programs operate independently from \fIdeliver\fR. It hasn't been used for years and requires reading the code to understand its effect. Do NOT define RUNALONE! (it is used for debugging) .IP V4_2BSD 15 Enables code that is specific for 4.2 BSD . All 4.2 BSD sites must define this variable. This should be used by 4.3 BSD sites as well. 4.3 BSD sites will also want the new src/smtp/smtpd.4.3.c for use with INETD. They will have to compile this manually for now (or copy it to smtpd.4.2.c). .IP NODIAL 15 Prevents the dial package (PhoneNet protocol) from being compiled. This saves a fair amount of space and compile time. Define NODIAL if you do not intend to use the phone or pobox channels. (You will also need to take `dial' out of Makefile.lib, see below.) .IP SYS5 15 Enables code that does Bell System V tricks (probably also useful for System III installations). Note: if you are running System V, you must also append -Drindex=strrchr and -Dindex=strchr to CFLAGS. .IP ALTOS 15 Enables code that does ALTOS tricks. Define this variable if you have an ALTOS system. .IP NODUP2 15 Define this variable if you don't have a dup2() system call or subroutine. .IP NOFCNTL 15 If you defined NODUP2, then you should also define NOFCNTL if you don't have the fcntl(x, F_DUPFD) system call either. .IP NAMESERVER 15 Enables the nameserver lookup code for accessing domain servers. The NAMESERVER support is new. You will be required to include either tb_ns.fake.c or tb_ns.<sys>.c. Currently there is only support for 4.2BSD networking. See TB_NS below. .IP UUCPLOCK 15 Uses UUCP/tip/cu-style locking for PhoneNet dial-out lines. Define UUCPLOCK if you intend to run UUCP (or tip/cu) and PhoneNet on the same dial-out line. This causes \*M to use the LCK..tty?? locking of UUCP. .IP NODOMLIT 15 Prevents Domain Literals (such as [10.0.0.59]) from appearing in addresses. Since \*M doesn't currently handle Domain Literals properly, use of this option is strongly advised. .XX Tailor the CONFIGDEFS line to your site's requirements and system type. .PP The CFLAGS line is the combination of the CONFIGDEFS given above and any C compiler dependent options. The basic boilerplate content is ``-O -I../../h'' which asks for the object code optimizer and specifies the path to the header file directory. If you do not have an optimizer or it does not work properly, remove the ``-O''. .XX Tailor the CFLAGS line to your system's C compiler. .PP LINT is the pathname of your lint program, if you have one. If not, define LINT to be something innocuous like `echo'. LFLAGS are the flags to LINT; tailor as you like. This line should end with $(CONFIGDEFS) just like CFLAGS. LDFLAGS are the flags you want to give to the loader (ld). These may be site dependent; for example, a PDP-11 will require use of split instruction and data space by using `-i'. Do not include libraries in the LDFLAGS definition. Do not tailor MMDFLIBS. SYSLIBS is a list of libraries required from the system given either explicitly or using the -l\fIlibname\fR notation. Typically you will require at least -ldbm for the hashed database library if you are using it. Systems without the new Berkeley directory access routines will also need to include -lndir or ../../libndir/libndir.a. The ndir library is normally supplied in the sub-directory libndir in the \*M distribution. The AR definition is simply the path or name of the library archiver, typically just `ar'. .XX Tailor LDFLAGS and SYSLIBS (and perhaps LINT, LFLAGS, and AR). .PP CH_TB selects one of two types of table lookup. The two choices: use linear search tables (slow) or the hashed database mechanism (fast). In an independent survey, 99 out of 100 sites recommended fast over slow (the 100th was a masochist). If you don't have the dbm library, get it from some other site. It was distributed with V7 and all BSD systems. It is FAST. Set CH_TB to `ch_tbdbm' if you want to use a dbm hashed database (recommended) or `ch_tbseq' if you want to use linear searched tables. .PP The TB_NS variable indicates which nameserver module to use. There are currently only two choices, `fake' and `4.2'. `fake' is appropriate if you didn't specify -DNAMESERVER in CFLAGS above or if your system is a non-BSD4.2 Unix. .XX Tailor TB_NS. .PP LOCALUTIL is for listing special modules needed based on different configurations. .IP \(bu If you do not want runtime tailoring, include tai_fake.o. Otherwise you should include tai_file.o. The latter is highly recommended (tai_file.o). .IP \(bu You must include the name of one of the several locking code modules. There are many systems with home grown locking mechanisms that are much better than those provided with vanilla UNIX. 4.2BSD comes with a very good discretionary locking mechanism. Examine the various lk_lock.*.c files in lib/util and choose one that is appropriate for your system and include the name of its object module here (substitute .o for .c). If you have a type of locking available for which we do not have a module, choose one that is close to what you need and modify it accordingly. If you have no locking mechanism, you should specify lk_lock.o. This will include a locking package that uses links to lock files. .PP The last item to tailor here is which dependency generation script to use. The first one is for systems that do NOT have the .I cc \-M flag. It uses .I grep and .I sed to generate the dependencies. The second entry for ``depend:'' uses the \-M option to .I cc to generate dependencies more accurately. This is currently only usable with 4.3BSD systems. .XX Make sure the proper version for your system is uncommented and comment out the other version. .NH 2 Compile-Time Tailoring conf/\fIsite\fB/Makefile.lib .PP There are only three things to be modified here: all defines at the top of the file. First, tailor MAKE. If you need to use a special path for the make program, then define MAKE to be that path, otherwise MAKE should just be `make'. Second, tailor RANLIB. Some systems (primarily System III and System V sites) do not have the \fIranlib\fR archive table of contents program and so must always create a library in the proper order for searching. This is done with the .I lorder and .I tsort programs. If you do not have .I ranlib, define RANLIB to be `libfix'. Libfix is a shellfile in the directory .B lib that will do the proper things to build a sorted \*M library. If you are a site that has .I ranlib, then simply define RANLIB to be `ranlib'. Finally, if you do not plan to use the dial package (no PhoneNet) then remove dial from the list of SUBDIRS (in this case you should also have defined NODIAL in Makefile.com). Normally SUBDIRS will be ``addr dial mmdf table util''. .XX Set MAKE, RANLIB, and SUBDIRS to appropriate strings. .NH 2 Compile-Time Tailoring conf/\fIsite\fB/Makefile.src .PP There are a couple of experimental or new channels that you may want to use that are not on the line defining SUBDIR, or it may be commented out (preceded by a #). The standard entries are tools, submit, deliver, local, list, delay, smtp, phone, pobox, uucp, and prog. If you include the nameserver code you will need `delay', otherwise it can be left out. You may also want to remove references to directories you know you are not going to use. This will save compile time, since you will not build things you do not intend to use. If you have specified -DNODIAL, you can safely comment-out the phone and pobox directories. .XX Check the contents of SUBDIR and adjust if necessary. .NH 2 Compile-Time Tailoring conf/\fIsite\fB/msgtailor.c .PP There are a quite a few things to tailor for the .I msg program. The first is the number of messages supported in one mailbox. This is basically limited only by the host machine's address space. The .I msgtailor.c file already has some typical values for 16 bit and 32 bit systems. NMSGS is the maximum number of messages to be supported. For 16 bit systems, the maximum should be no more than 500, on 32 bit systems, the upper limit is somewhat arbitrary, but does affect storage space. We suggest 2000. .I Savmsgfn is the name of the place to save mail from the main mailbox (as tailored in Section 2.5.7). This is usually ``mbox''. The string .I resendprog is a string that is fed to a subroutine of .I msg to cause the resending program (supplied as uip/other/resend.c) to be invoked. It must begin with a `|' (pipe symbol) and the remainder of the string must be the path to the resending program, which normally gets installed in BINDIR (see Section 2.8). It is possible to omit the full path but it is not recommended. Note that a space character is required at the end of .I resendprog. The stand-alone section should be ignored. .XX Set up \fIsavemsgfn\fR and \fIresendprog\fR, and NMSGS if necessary. .PP The strings .I dflshell and .I dfleditor define the default shell for subprocesses and default editor for two window answer mode (respectively). Both can be overridden by the normal shell variables (``SHELL'' and ``EDITOR''). The default editor should be able to edit multiple files simultaneously if invoked with more than one file name. If you have no editors with this feature you can probably concoct a shell file to do it or make do with just invoking an editor (e.g., \fBex\fR or \fBvi\fR) on a copy of the original message. .XX Set up the \fIdflshell\fR and \fIdfleditor\fR. .PP The following variables are basically self explanatory and control the default behavior of such features as command completion, paging, informative messages, and timezone constants. If in doubt, leave the settings alone. Note that the time stuff need not be tailored for System V systems. For unusual time zones (perhaps all time zones), it is recommended that you use the offset notation from Greenwich Mean Time. For the US Eastern Time zone this would be `{ ``-0500'', ``-0600'' };', and Middle European Time would be `{ ``+0200'', ``+0100'' };'. The normal BRL settings are given below: .sp .TS l l l . int pagesize = 22; /* default line count */ int linelength = 80; /* default line size */ int paging = OFF; /* Internal paging of long messages */ int verbose = ON; /* Command completion (raw mode) */ int keystrip = ON; /* Strip header lines on display */ int bdots = ON; /* Binary box dots */ int mdots = ON; /* Mailbox dots */ int bprint = ON; /* Reading... messages */ int quicknflag = OFF; /* Turn off NEW before message prints */ int filoutflag = ON; /* Filter text (display controls as ^X) */ int binsavflag = ON; /* Save binary box on exec's & pauses */ #ifndef SYSV long timezone = 5*60*60L; /* Seconds West of GMT (Greenwich Mean Time) */ /* (-1)*60*60L is Middle European Time. */ /* 5*60*60L is U.S. Eastern Time. */ /* 8*60*60L is U.S. Pacific Time. */ int daylight = 1; /* Do you use daylight savings time? */ /* 0 = NO, 1 = YES */ char *tzname[] = {"EST", "EDT",}; /* Timezone strings, primary and daylight savings time if used, in that order */ #endif SYSV .TE .NH 2 Compile-Time Tailoring conf/\fIsite\fB/s_tailor.h .PP This file defines some of the operating characteristics of the \fIsend\fR program. There are three #defines you can tailor in this file. Your choice is to either #define or #undef each of the following. SENTFILE causes a copy of each message sent to be placed in a file called .sent in the user's home directory. This option is obsoleted by the new version of \fIsend\fR; the user can now tailor this feature by editing the .sendrc file in the user's home directory. FNCYPRNT enables paginated printing of the message by the review command. If this is not defined, listing will occur without pagination. This is normally left undefined. PWNAME, when defined, enables the code that will try to get the user's real name from the gecos field of /etc/passwd if no .signature file exists. The code stops at the first comma and knows about the '&' notation for abbreviating the login name. If your site has gecos fields which contain other information or the format is not compatible, do not #define PWNAME. .XX Check the settings of SENTFILE, FNCYPRNT, and PWNAME. .NH 2 Compile-Time Tailoring conf/\fIsite\fB/s_tailor.c .PP This is the main tailoring file for the \fIsend\fR program. There are three things to tailor here. Set the initialization of the strings for the default line and screen editors, and the default spelling checker. .I Dfleditor should be set to the local default line editor. .I Dflveditor should be set to the local default visual (screen) editor. .I Dflchecker should be set to the local default spelling checker. If the names are simple names (e.g. "ed") your users will be able to find versions in their search paths; if the names are full paths, \fIsend\fR will always run the versions you specify unless overridden by the user. Users can always override these settings in their .sendrc files. .XX Set the strings \fIdfleditor\fR, \fIdflveditor\fR and \fIdflchecker\fR for your site. .NH 2 Other Compile Time Problems .PP If you are a 4.3BSD site or have access to the Berkeley Inetd or another Internet super-server, you will probably want the version of smtpd designed to run under these super-servers. For the time being, you will need to move smtpd.4.2.c to smtpd.4.2.old and install smtpd.4.3.c as smtpd.4.2.c in the directory src/smtp so the makefiles can generate the module you need. In the future this difference will be more cleanly handled in the configuration procedure. .XX If you have a super-server, install smtpd.4.3.c. .NH 2 Compiling Your \*M System .PP You are now ready to generate your version of \*M. You must first go to the conf directory and install your site specific files by running the `sitesetup' shell program located there. Yoursitname is the name of the directory you chose and should also match the HOST variable in the Makefile.com you edited earlier. Please do not try to run this shellscript with C-shell or some other non-standard shell. .XX In conf, run `sh sitesetup yoursitename'. .PP If you have not run a ``make clean'' yet, you should do so now. .PP Next you should go to the top directory and run ``make depend''. This will go recursively through \fBlib\fR, \fBsrc\fR and \fBuip\fR generating the dependencies for your system. Remember the comments about this mechanism made at the beginning of this document. .XX In the top directory, run `make depend'. .PP The master makefile in the top of the \*M source tree will attempt to recompile everything in the subdirectories \fBlib\fR, \fBsrc\fR, and \fBuip\fR (in that order). You can also run make in each of these directories alone, although \fImake\fR must be run in \fBlib\fR first. .PP If you want to run each of the make's separately, change directory to the \fBlib\fR directory and run \fImake\fR. This should result in the file lib/libmmdf.a several minutes later (15-30 minutes, depending on machine speed). Assuming libmmdf.a was generated successfully, change directory to the \fBsrc\fR directory and run \fImake\fR to build the core \*M programs (burn another 15-30 minutes). Finally, go to the \fBuip\fR directory and run \fImake\fR there. In all cases, libmmdf.a must be built before any other directories are made or the objects will not load successfully (they need the library!). .PP Remember that make depend will insert some bogus dependencies if you do not have .I cc \-M, so make will stop a few times. When it does remove the offending bogus dependency and continue. You will only have to do this once. Make depend should not have to be run again unless significant source changes are made. .XX If you didn't run the make's separately above, run `make' at the top of the \*M source tree .NH 2 Handling Problems While Compiling .PP If you run into problems you can always interrupt the process, correct the problem, and continue by calling \fImake\fR again from the directories lib, src, or uip (or just running the master makefile again from the top directory). Remember that if you try to build any of the subdirectories of \fBlib\fR, \fBsrc\fR, or \fBuip\fR (e.g. \fBsrc/deliver\fR) while in that directory, you must invoke \fImake\fR via the shellfile \fIgen\fR in the subdirectory. \fIGen\fR takes all the same arguments as \fImake\fR but includes the file ../../Makefile.com along with ./Makefile.real by using the \-f option of \fImake\fR. .NH 2 Additional Makefile Features .PP The makefiles in \fBsrc\fR and \fBuip\fR also support the command ``make dist'' which makes a tar-format file with all the executable binaries and a shellfile called ``install.sh'' which, if run, will install all the binaries and chown/chmod them appropriately. A ``make dist'' should only be run after the system has been successfully built and (preferably) tested on the current system. This facility makes it much easier to maintain multiple systems from a master source machine and greatly simplifies distributing and installing new versions. (This facility is a bit flakey.) .PP As mentioned before, all the Makefiles support ``make clean'' and ``make depend'' (or ``gen clean'' and ``gen depend'' as appropriate to your current directory). to the Berkeley Inetd or another Internet super-server, you will probably want the version of smtpd designed to run under these super-servers. For the time being, you will need to move smtpd.4.2.c to smtpd.4.2.old anmmdf/doc/administrators/runtime 444 0 12 123510 3664425123 12236 .\".AU .\"Julian Onions .\".AI .\"Department of Computer Science, .\"The University, .\"NOTTINGHAM, .\"NG7 2RD. .\"ENGLAND. .\"(0602) 506101 ext 3595 .bp .NH Runtime Tailoring the \*M system .NH 2 Introduction .PP The \*M Mail Transport system can be run in one of two modes. It can either have all the site dependent information tailored at compile time, or else it can be asked to read the information from an ASCII human-readable file each time it starts up. Obviously the latter provides much more scope for tweaking various constants etc. but does slow down the start-up time of the programs using this information. Most sites will want to run somewhere in the middle, with most of the static information (bug address, directories, filenames, etc.) compiled in, and the remainder configured at runtime. If you later have a need to change some compiled in values, you can simply add an item to the runtime tailoring file to override the compiled-in value and thus avoid having to recompile the mail system. There are obvious efficiencies to be had from a smaller configuration file which makes it attractive to try to keep it small. Typically one would runtime configure the host name and domain, the support address, the tables, the channels, the domains, and the logging levels. .PP Many people may be worried about the cost of runtime tailoring. The method for reading in the tailoring file is very efficient, involving only a single read system call. The use of runtime tailoring adds very little overhead to the \*M mail system. The advantages of runtime tailoring generally far outweigh the minor extra cost at startup. .PP Occasionally there will be a need to compile in the configuration at a particular site (e.g. CSNET-RELAY) because the runtime tailoring file becomes far too large. There are two tools for helping to do this in src/tools. \fBFixtai\fR reads a specified runtime tailoring file and produces structure initializations for those structures suitable for being #included into chan.c. \fBRem_chans\fR comments out specified channel entries in a runtime tailoring file that you now expect to compile in. You may wish to do this by hand if you only have a few. .PP In general, keywords in the tailoring file are case independent, however such things as filenames and similar values, obviously are not. The full specification for the tailoring parsing can be found in manual section tai(3). Strings are generally quoted to prevent them from being parsed into separate words accidentally. Simple words consisting of only alpha-numeric characters can be safely left unquoted, but all others strings should be quoted, especially those containing spaces or punctuation. .PP This section describes tailoring through the external file, but much of this information is applicable when using the compiled-in tailoring. The subsections below describe the specific entries which can be placed in the tailoring file. .PP Examples are given for a number of sites including most of those with directories in the \fBconf\fR directory. See the directory \fBsamples\fR; subdirectories will contain actual tables and tailoring files for these systems. .PP This whole process is perhaps easiest if you copy a tailor file for some other system and change the bits you need. This example will show you common values that have been used for parameters; they are not necessarily what you want, but they should give you a guide. In some cases, there is more than one way to do something. For example, setting up the list channel tables was done differently at BRL and UCL. This does not mean that one is wrong, just different. One is perhaps more elegant, but both should work. .XX Choose a tailor file to copy. .XX Copy the chosen tailor file to the location you specified as .I mmtailor in the conf.c file (see section 2.5.1). .XX Edit your tailor file as described below. .NH 2 Local Names and Site-Specific Information .PP The MLNAME is the name of your machine or site as you wish it to be known throughout the network. This may be a generic host name used to hide a number of local hosts. If a generic host name, internal hosts will be differentiated by MLOCMACHINE (described below). .DS .TS l l . MLNAME ``Maths'' # UK site MLNAME ``Arpahost'' # ARPA site .TE .DE Case should not matter here. .PP The MLDOMAIN line gives your full local domain. This, combined with the MLNAME (and possibly the MLOCMACHINE) will give you the full network address. .DS .TS l l . MLDOMAIN ``Nott.AC.UK'' # UK site MLDOMAIN ``ARPA'' # ARPA site .TE .DE This will give as your address .DS .TS l l . Maths.Nott.AC.UK # UK site Arpahost.ARPA # ARPA site .TE .DE .PP The MLOCMACHINE line is often set to the null string, unless you are a large transparent site. The normal use of this is for a site which has several machines but wishes the machines themselves to appear as one address. .LP For example: .br Arpahost is, say, really a name that covers 5 machines, mach1 to mach5. Mail leaving any ``Arpahost'' machine is stamped as originating from Arpahost, and mail is addressed to username@Arpahost by outsiders. The alias table then maps the incoming address to .DS .TS l l . username username@mach1.Arpahost.ARPA .TE .DE Take a look at the ucl-cs setup for an example of how such things function in real life. Remember that the mail system will really believe its name is \fIlocname.locdomain\fR and any mail with that domain will be verified locally. If it finds a more specific domain such as \fIlocmachine.locname.locdomain\fR it will pass it on to the referenced host. If defined, the .I locmachine string will never be generated in addresses by the system, although it is recognized and can be supplied by a user if it is important to bypass the normal aliasing done by the system. The .I locmachine string will be used in id strings (Received: and Message-ID: header lines). .DS .TS l l . MLOCMACHINE maths70 .TE .DE The MSIG line is the signature that MMDF will use when notifying senders of mail delivery problems. This should be something that indicates a message agent was responsible. .DS .TS l l . MSIG ``MMDF Mail System'' .TE .DE MLOGIN is the user that owns the MMDF transport system. .DS .TS l l . MLOGIN mmdf .TE .DE MSUPPORT is the address to which to send mail that MMDF can't cope with (i.e. that MMDF can't deliver or return to sender). Be sure MSUPPORT is a legal address! .DS .TS l l l . MSUPPORT mmdf@Maths.Nott.AC.UK # UK site MSUPPORT mmdf@Arpahost.ARPA # ARPA site or perhaps MSUPPORT postmaster@Arpahost.ARPA .TE .DE .PP AUTHREQUEST is the address to which users should mail if they have questions about why a message was not authorized for delivery. It is also used as the sender of authorization warning messages. It is not used if authorization is not enabled on some channel. .DS .TS l l . AUTHREQUEST postmaster .TE .DE The MMAILID line is used to enable the use of the the Mail-IDs facility of \*M which disassociates mail addresses from login names. The line is of the form: .DS .TS l l . MMAILID \fInumber\fR .TE .DE where \fInumber\fR is either 0 to disable the feature, or 1 (non-zero) to enable it. If it is enabled, you should have the tables ``users'' and ``mailids'' with appropriate mapping from userids to mailids and vica-versa. See section 5.4.4 of this document and tables(5) for more information on setting up these tables. .NH 2 Directory Tailoring .PP Next, a whole heap of path names to directories can be tailored. The example that follows has the \*M file under both /usr/spool and /usr/lib/mmdf. This is the way UCL arranges their \*M directories. Also shown are the values used at BRL where everything is placed under under /usr/mmdf (see the samples directory for more examples). .br MLOGDIR is the default directory in which the log files are kept. .DS .TS l l . MLOGDIR ``/usr/spool/mmdflogs'' MLOGDIR ``/usr/mmdf/log'' .TE .DE MPHSDIR is the directory in which the timestamps for the channels are made, showing what state of activity they are in. .DS .TS l l . MPHSDIR ``/usr/spool/mmdflogs/phase'' MPHSDIR ``/usr/mmdf/log/phase'' .TE .DE MTBLDIR is the default directory for the table files. .DS .TS l l . MTBLDIR ``/usr/lib/mmdf/mmdftable'' MTBLDIR ``/usr/mmdf/table'' .TE .DE MQUEDIR is the parent directory for the various queues and address directories. .DS .TS l l . MQUEDIR ``/usr/spool/mmdflock/que'' MQUEDIR ``/usr/mmdf/lock/home'' .TE .DE MLCKDIR is the directory where the locking of files takes place, this is dependent on what style of locking you are doing. .DS .TS l l . MLCKDIR ``/usr/spool/mmdf/locks'' MLCKDIR ``/tmp/mmdf'' .TE .DE MTEMPT is the temporary files directory; if not a full pathname, it is taken relative to MQUEDIR. .DS .TS l l . MTEMPT ``temp/'' .TE .DE MADDRQ as above, but holds the address files. .DS .TS l l . MADDRQ ``addr/'' .TE .DE MMSGQ as above, used for the message texts. .DS .TS l l . MMSGQ ``msg/'' .TE .DE MCMDDIR is the default commands directory where the various \*M commands live; any command that does not have a full pathname is taken relative to this directory. .DS .TS l l . MCMDDIR ``/usr/lib/mmdf'' MCMDDIR ``/usr/mmdf'' .TE .DE MCHNDIR is where the channel programs are to be found. .DS .TS l l . MCHNDIR ``/usr/lib/mmdf/chans'' MCHNDIR ``/usr/mmdf/chans'' .TE .DE The MQUEPROT gives the protection mode in octal that the parent of the MQUEDIR directory will have. .DS .TS l l . MQUEPROT 0700 .TE .DE .NH 2 Database Files .PP The MDBM line tells MMDF where to find the database file containing the associative store. Dbm style databases get their speed and flexibility by dynamic hashing and can get quite large. By default, the file is located in the MTBLDIR dirctory, but it might need to be relocated due to its size. .DS .TS l l . MDBM ``/usr/spool/mmdfdbm'' .TE .DE This is not the actual form of the file name, as the dbm(3) package forms two files with the suffices .I \&.pag and .I \&.dir which are closely coupled by the dbm package. Another file may also appear with a .I \&.lck suffix. This is a file that is used in locking the database. .NH 2 The Log Files .PP Several parameters affecting the logging of information can be tailored. One parameter is the level of detail that should be logged. If this parameter is set for verbose logging, then enormous amounts of information can be gleaned, probably most of it indecipherable (but a good start towards your guru's badge), and most of the \*M programs become noticeably slower and I/O bound. To get the fullest logging information available, the programs have to be compiled with the \-DDEBUG flag. Setting this flag may cause the programs to get too large for some PDP-11 computers. .PP The MMSGLOG controls the logging information produced by \fIdeliver\fR and \fIsubmit\fR; MCHANLOG controls the logging by most other \*M programs with the following exceptions: PHLOG controls phone logging information and AUTHLOG controls authorisation information. An argument without an equal sign is taken as the new name of the log. Logging files and levels can also be specified in the channel descriptions. The logging file, if specified there, will override the MCHANLOG definition. The logging level for the channel will be set to the maximum of the MCHANLOG level and the channel description's level. The MCHANLOG level can therefore be used to increase logging on all channels at once. .DS .TS l l . MMSGLOG /tmp/mmdf.log, level=FST, size=40, stat=SOME MCHANLOG level=FST, size=40, stat=SOME MPHLOG level=FST, size=40, stat=SOME AUTHLOG level=FST, size=40, stat=SOME .TE .DE These examples set the logging level to FST (full statistics). Alternatives here are: .RS .TS l l . FAT \- Logging of fatal errors only. TMP \- Temporary errors and fatal errors. GEN \- Include the generally interesting diagnostics. BST \- Show some basic statistics. FST \- Full statistics. PTR \- Give a program trace listing of what is happening. BTR \- Give more detailed tracing. FTR \- Give every diagnostic you can. .TE .RE .PP The last two conditions (BTR and FTR) are enabled only if you have compiled the MMDF system with DEBUG #define'd. This amount of tracing can quickly fill up log files. During normal operation, the logging level should be set somewhere between GEN and FST. .PP Size is set to the number of 25-block lumps you wish your log file to grow to. When the log files reach that size, logging either stops or it may start to cycle around overwriting old data. .PP Stat sets up various status flags for logging. Options here are: .RS .IP close 10 Close the log after each entry; this allows other processes to write to it as well. .IP wait 10 If the log is busy, wait awhile for it to free. .IP cycle 10 If the log is at maximum length specified above, then cycle to the beginning (this feature doesn't work currently). .IP some 10 Sets the values close, cycle, and wait. It is the most common setting. .RE .PP The logfile tailoring is generally done at the end of the tailor file to prevent logging the tailoring action itself, thereby needlessly filling the logfiles when higher tracing levels are enabled. If you have bugs in the tailoring, you will obviously want to move the logfile tailoring closer to the top. .PP The DEFTRAN line gives the name of the default transcript file for use in phone logging. It is the only log that is not size limited, as it is created anew each time a connection is made with the phone channel or slave program. .DS .TS l l . DEFTRAN "ph.trn" .TE .DE .NH 2 Runtime Table Definitions .PP Table contents are discussed in section 4 of this document. Table definitions are discussed here. Tables fall into three categories: domain, channel, and others. Domain and channel tables are discussed below. The "other" category is discussed here. Regardless of category, table definitions all have the same format. They all consist of the following parts: .RS .IP name 10 A name by which the table is known. .IP file 10 A file from which the contents of the table are built up. .IP show 10 A display line, which is used for pretty printing purposes to explain what the table is all about. .IP flags 10 The flags line is optional. It is used to indicate additional properties about the table being defined. This can be specified more than once for a given table to set more than one flag. Currently this is used to specify various table options - source type, nameserver lookup and partial lookups. There are three source types defined: ``file'', ``dbm'', and ``ns''. The type ``file'' means that the table comes from a sequentially read file (the default). The type ``dbm'' indicates that the table is in the MMDF hashed database built with \fIdbmbuild\fR. The type ``ns'' indicates that the table data is obtained by making queries on a nameserver. In the case of an ns-type table, the ``flags'' field will also specify the type of nameserver lookup. Currently ``domain'', ``channel'', and ``root'' are supported. ``Domain'' indicates a lookup of the given address in the domain specified by the table name (e.g. FOO.ARPA in table ARPA). ``Channel'' indicates a lookup of the given fully-qualified domain name to determine the address(es) to use in delivering via SMTP. ``Root'' indicates a domain lookup of the given fully-qualified domain name without regard to the table name (e.g. FOO.OXBRIDGE.EDU in table ROOTDOMAIN). Normally, only fully-qualified names will be checked. The optional type ``partial'' indicates that, for ns-type domain tables, partially qualified names can be used (e.g. if ns-type domain tables ARPA and EDU have the ``partial'' flag set, an address of FOO will generate queries for FOO.ARPA and FOO.EDU). The ``partial'' flag is often used for the local domain table to allow users to omit the full domain specification when referring to local machines. .sp Note: In the current release there are only two modes of operation. If you use tb_io.c, you will get sequential table io ONLY. If you use tb_dbm.c you will get DBM and NS support (and the latter only if you define NAMESERVER). The source types ``file'' and ``dbm'' are therefore treated the same. .RE .LP A typical example might be: .DS MTBL name=aliases, file=aliases, show=``User & list aliases'' MTBL name=smtpchan, flags=channel, flags=ns, show=``SMTP Addrs via NS'' .DE If the first argument does not have an equal sign in it, it is taken as the value for the other parameters. For example: .DS MTBL aliases, show=``Alias table'' .DE sets the name, file and show parameters to the string ``aliases'', and then resets the show parameter to the string ``Alias table''. .PP The following tables are provided automatically via chan.c and the tailoring package when Mail-IDs are enabled (see section 3.2 above): .RS .IP users 10 A mapping of usernames to Mail-IDs .IP mailids 10 A mapping of Mail-IDs to usernames .RE .PP The aliases tables are sometimes compiled in from the configuration file chan.c, so they can be left out of a runtime configuration. If they are not in chan.c they must be specified in the runtime configuration. If this is your first time through, best go for runtime tailoring until you know what you want. The tables for mailids and users are also compiled into chan.c since other parts of MMDF need to know about these tables. Changes to the mailids or users table definitions are best made in chan.c. .XX Define or change the aliases tables (if necessary). .NH 2 Runtime Alias Definitions .PP Alias definitions are used to tell the system about the various sources of alias information. \*M can have more than one alias file. In addition, each alias source has the option of being trusted and the option of preventing bypassing (via the\ \~\ on the front of an address). .PP Making an alias file trusted is a serious decision. A trusted alias file can direct mail to be delivered to any file or process using the permissions of any user on the system. It should only be modifiable by the Superuser, or guarded as such. Typically there is only one or two trusted alias files per system. .PP The alias tables are searched in the order they are defined. When a hit is made, no further searching is done for additional hits in later tables. .RS .IP table 10 Specifies name of the table to be associated with this alias entry. .IP trusted 10 The entries in the table will be allowed to cause delivery to files and pipes. .IP nobypass 10 The `\~address' alias bypass mechanism will not work on this file. .RE .DS ALIAS table=sysaliases, trusted, nobypass ALIAS table=forward\ \ \ \ \ \ # modified by users (== .forward) ALIAS table=useraliases, trusted .DE .XX Define your alias tables and their attributes. .NH 2 Runtime Domain Definitions .PP See section 5.2 for a discussion of how to choose a set of domains to include in the tailor file. Domain definitions consist of the following: .RS .IP name 10 A name by which to refer to this domain. .IP show 10 A display line, which is used for pretty printing purposes to explain what the domain is all about. .IP dmn 10 The full domain name (x.y.z...) that specifies this domain. .IP table 10 The associated table entry of known sites in this domain. If the specified table has not been previously defined, it will be defined with the same name, file, and show as for this domain. .IP lname 10 An optional local name by which you are known in that domain. This defaults to the value of LOCNAME. .RE .PP There are many defaults to make life easier. As in the table case, if the first argument does not have an equal sign, the values of the name, domain and show parameters take on this value. .DS MDMN name=``nott.ac.uk'', dmn=``nott.ac.uk'', table=nott, show=``Nottingham Domain'' MDMN arpa, show=``ARPANET Domain'' MDMN ``'', show=``Root Domain'', table=rootdomain .DE .XX Define a set of domains in your tailor file. .NH 2 Runtime Channel Definitions .PP The first stage is to determine the set of channels to be used. Typically, a local and list channel will be required. Then, there will be one channel for each PhoneNet link, and one channel (at least) for each protocol network pair (e.g. smtp channel for SMTP/ARPANET and an NIFTP channel for JNT Mail/X.25). UUCP needs only one channel. In some cases it may be desirable to split what would normally be one channel so that some authorisation can be used on the basis of channel. For example, one may choose to have a separate channel for ARPANET and Hosts using SMTP/TCP on a local ethernet, even though they could use the same channel. Another case might be to have separate JNT Mail channels for JANET and PSS. This allows for logical authorisation functions to be applied on a per-channel basis. .XX Decide what channels you will need to use. .PP Next you must define these channels in the tailor file. The channel definitions are more complex than tables and domains. There are a large number of parameters available because of the diverse behavior of the different channel programs. Many of the parameters are not necessary for most of the channels. The specific parameters are as follows: .RS .IP name 10 The name of the channel. .IP show 10 A display line, which is used for pretty printing purposes to explain what the channel is used for. .IP que 10 The queue directory into which messages for this channel should be queued. .IP tbl 10 The associated table entry of hosts that are accessible on this channel. If the specified table has not been previously defined, it will be defined with the same name, file, and show as for this channel. .IP pgm 10 The channel program to be invoked for this channel. .IP mod 10 The mode in which this channel works. If several values are selected, they are cumulative. Normally, one of ``reg'', ``bak'', ``psv'', or ``imm'' is selected. ``Pick'' and ``send'' are usually both selected on two-way channels such as phone or pobox. The values are: .RS .IP "reg \-" 9 regular mode (the default). The channel may be explicitly invoked by running .I deliver. .IP "bak \-" 9 channel may be invoked only by the background deliver daemon. .IP "psv \-" 9 the channel is passive; it is a pickup type channel (e.g. pobox). .IP "imm \-" 9 this channel may be started up immediately; there is no need to batch up mail. .IP "pick \-" 9 This channel may pick up mail from the remote host. .IP "send \-" 9 This channel may send mail to the remote host. .RE .IP ap 10 The address-parser type to be used for reformatting headers on messages going out on this channel. The settings of ap control reformatting. If none is selected, the channel will not reformat. If several values are selected, they are cumulative: .RS .IP "822 \-" 9 selects RFC 822 style source routes (e.g. @A:B@C) .IP "733 \-" 9 selects % style\ (JNT) source routes (e.g. B%C@A) .IP "big \-" 9 selects NRS hierarchy ordering (for the UK) .IP "nodots \-" 9 selects output of leftmost part of domain names (e.g. A in A.B.C) for sites that can't handle domains (usually used in conjunction with `known=' (described below) to hide domain names behind a smart relay). .IP "jnt \-" 9 is equivalent to ap=733 + ap=big .RE .IP lname 10 This specifies a name overriding the default MLNAME value when used in this channel. This is the name by which the local host is known to hosts accessed by this channel. This is used when the channel should have non-standard values for the local domain. If \fIlocname\fR = `UCL-CS' and \fIlocdomain\fR = `AC.UK', then for an arpa smtp channel, one would set the parameters ldomain = `ARPA' (and, implicitly, lname = `UCL-CS'). All local references in this channel would be mapped to UCL-CS.ARPA if reformatting is used. .IP ldomain 10 As above, but overriding the value of MLDOMAIN. See the example above. .IP host 10 The name of the host that is being contacted by this channel, usually used in the phone and pobox channels. (Also the name of the relay host when all mail to hosts in this channel's table gets relayed to one host. This is required on the .I badusers and .I badhosts pseudo-channels. It is also required to be set to the local host for the .I list channel. .IP user 10 The login name of the account which uses the slave program to pick up mail (used for the pobox channel only). .IP scr 10 The name of the dialing script file to be used by the phone channel. .IP trn 10 The name of the transcript file to be used for this channel; this currently applies only to PhoneNet, and defaults to DEFTRAN (usually ph.trn). .IP poll 10 The frequency of polling the remote machine. A poll on a two-way channel (such as the phone channel) is used to see if any mail is waiting to be picked up from the remote host. A value of 0 disables polling; a value of -1 requests polling to be done every time the channel is started up. Any other value is the number of 15-minute intervals to wait between polls. Note that the value of poll is ignored if any mail is waiting to be sent; in this case, a connection is always attempted. .IP insrc 10 Table of hosts controlling message flow. See the paper .B "Configuring MMDF Authorisation" by Steve Kille for details. .IP outsrc 10 See insrc, above. .IP indest 10 See insrc, above. .IP outdest 10 See insrc, above. .IP known 10 This is a table of hosts that are known on this channel. If a known table is specified and a given host is not in the table, then a relay form of the address is used, specifying that the return address goes through this host. Only the LHS of this table is used; the RHS is ignored. The LHS should contains hosts, not domains. Typically, channel tables double as ``known tables''. Be sure that the table also contains your own fully specified name (localhost.locdomain). Note that The ``known table'' must exist. A tailor entry will not define a ``known table'' that has not been predefined. .IP confstr 10 This is a string that is used by some channels for specifying the invocation of another program. There is a form of macro expansion in this string, so that it can include the target address, the return address, etc. Refer to the man pages describing specific channels for more information about using the confstr. Most channels don't use this string currently. .IP auth 10 This specifies the authorisation tests that are made on this channel. It takes any combination of the following modes: .RS .IP free 10 default, no checking takes place. .IP inlog 10 log information regarding incoming messages. .IP outlog 10 log information regarding outgoing messages. .IP inwarn 10 warn sender of incoming message if his/her authorisation is inadequate (the message still goes through). .IP outwarn 10 as above, but for outgoing messages. .IP inblock 10 reject incoming messages that have inadequate authorisation. .IP outblock 10 as above, but for outgoing messages. .IP hau 10 Host And User control. Requires both host and user authorisations to be acceptable. .IP dho 10 Direct Host Only. When applying host controls, the message must be associated with a user local to that host (i.e. no source routes). .RE .IP ttl 10 The \fIdeliver\fR program keeps track of connection failures to specific hosts. When a connection fails, the host is marked ``dead'' for a certain period of time. This period is called the ``time-to-live'' of the dead-host entry. After a connection failure to a given host, retries are blocked for ttl minutes. This value may be overridden on the deliver command line. If the ttl is not given here or given to deliver, a default of 2 hours is used. .IP log 10 This specifies the name of the log file to be used instead of the default channel log file (see CHANLOG above). Typically this is something like ``log=smtplog'' which will use a logfile called smptlog if it exists in the default logging directory. .IP level 10 This specifies the logging level to use for this channel. The form is the same as that used to specify the default logging level (e.g. ``level=FST'') as described in section 3.5 of this document. The actual logging level used is the maximum of this level (if present) and the default MCHANLOG level. .RE .PP As mentioned above, the majority of these options are not used for most channels. .RS .TS center; l l. MCHN name=local, que=local, tbl=local, show=``Local (maths)'', pgm=local, poll=0, mod=imm, ap=822, level=BST MCHN uucp, name=uucp, que=uucp, tbl=uucp, show=``UUCP'', pgm=uucp, poll=0, mod=reg, ap=822, auth=inlog, auth=outlog MCHN smtp, show=``SMTP/TCP'', que=smtp, tbl=smtp, pgm=smtp, mod=reg, ap=822, log=smtp.log, level=FST, confstr=``mysite.mydomain'' MCHN list, show=``Mailing List Processor'', que=list, tbl=list, pgm=list, mod=reg MCHN bboards, show=``BBoards Delivery'', que=bboards, tbl=bboards, pgm=bboards, mod=reg, ap=822 MCHN dial-out, show=``Dial-out channel to dial-site'', que=dial-site, tbl=dial-site, trn="dial-out.trn", ap=822, pgm=phone, mod=reg, mod=send, mod=pick, poll=8, host=dial-site, scr="dial-site.script" MCHN dial-in, show=``Dial-in channel from dial-site'', que=dial-site, tbl=dial-site, trn="dial-in.trn", ap=822, pgm=pobox, mod=psv, mod=send, mod=pick, host=dial-site, user=dislave .TE .RE In the ``uucp'' example, the name, que, tbl and pgm could have been omitted; they would have defaulted to ``uucp'' since it was the first argument. It is common, however, to include these explicit settings for clarity. The order of parameters is unimportant. .XX Define the channels needed at your site. .NH 2 Some Runtime Constants for Messages .PP Now there are a collection of constants defined: .LP MADDID controls whether .I submit will add Message-ID: header lines if they are missing from messages. It takes a number as an argument. If the number is 0, no action is taken. If the number is non-zero, then .I submit will add Message-ID: header lines if they are missing from messages. .DS MADDID 1 .DE .LP MLISTSIZE specifies the maximum number of addresses in a message before it is considered to have a ``big'' list. If there are more than the maximum number of addresses, then \*M will not send a warning message for waiting mail and will only return a ``citation'' for failed mail. A ``citation'' consists of the entire header plus the first few lines of the message body. .DS MLISTSIZE 20 .DE .LP MMAXHOPS specifies the maximum number of Received: or Via: lines in a message before it is considered to be looping, and is rejected. .DS MMAXHOPS 20 .DE .LP MWARNTIME specifies the time in hours that a message may remain in a queue before a warning message about delayed delivery is sent to the sender. .DS MWARNTIME 72 .DE .LP MFAILTIME is the time a message may remain in a queue before a failed mail message is sent to the sender and the message is purged from the queue. .DS MFAILTIME 144 .DE If the number of items in the queue is less than MMAXSORT, the messages will be sorted by arrival time and dispatched in that order; otherwise a message will be dispatched as it is found in the directory search. .DS MMAXSORT 150 .DE MSLEEP is the length of time in seconds that the background delivery daemon sleeps between queue scans. The following causes the daemon to sleep 5 minutes between passes. .DS MSLEEP 300 .DE .NH 2 Runtime UUCP Settings .PP The only uucp string worth bothering with is UUXSTR, which is the command used to invoke the UUCP software. In general you may want to tailor the grade level if supported and the disabling of immediate attempts (via \-r). If you have special per-message arguments, then you will need to modify the UUCP channel itself (e.g. \-a\fIsender\fR). .DS UUXSTR ``uux -'' \tor UUXSTR ``uux - -r'' \tor perhaps UUXSTR ``uux - -r -gA'' .DE .NH 2 Runtime General Variables .PP Now some more general variables. .DS .TS l l . MSUBMIT path=submit MDELIVER deliver deliver MPKUP name=pickup, path=deliver MV6MAIL mmdfmail ``/bin/v6mail'' .TE .DE MSUBMIT tells MMDF where the \fIsubmit\fR program lives. MDELIVER is where the \fIdeliver\fR program lives, and MPKUP is the post box pickup program. MV6MAIL is the mail program MMDF should use to return undeliverable messages and to send diagnostics. It should behave rather like the original Bell \fImail\fR program. It is not typically necessary to include these tailoring entries. .NH 2 Runtime Delivery Tailoring .PP MDLVRDIR is the directory in which to deliver mail. If this is null, then the user's home directory is used. MMBXNAME is the name of the mailbox. If this is null, then the user's login name is used. The following would set \*M to deliver mail to /usr/spool/mail with each mailbox named after the user's login name: .DS MDLVRDIR ``/usr/spool/mail'' MMBXNAME ``'' .DE The following causes mailbox files to be created in the user's home directory: .DS MDLVRDIR ``'' MMBXNAME ``mailbox'' .DE MMBXPROT is the protection mode that should be used when creating the user's mailbox. Its value is an octal number. See manual section \fIchmod(1)\fR. .DS MMBXPROT 0600 .DE MMBXPREF and MMBXSUFF are two strings that are written before and after the message is written into the mailbox. These are usually set to a sequence of control-A's. .DS MMBXPREF ``\e001\e001\e001\e001\en'' MMBXSUFF ``\e001\e001\e001\e001\en'' .DE MDLV is the name of the maildelivery file, used for tailoring the delivery for each user. See manual section \fImaildelivery\fR(5). .DS MDLV ``.maildelivery'' .DE The preceding parameters are also good contenders for being compiled-in since they very seldom change once established at a given site. .NH 2 Runtime PhoneNet Tailoring .PP If you do not use the phone or pobox channels, you may skip this section of the tailoring document. .PP DBADOUT and DBADIN are eight octal integers specifying the ASCII codes that can be used for host-to-host communications over PhoneNet. If there is a bit set in the ASCII position, then that ASCII code is not allowed to be used in the transmissions and therefore is escaped. .DS .TS l l . DBADOUT 0177777,0177777,0000010,0000000, 0000001,0010000,0000000,0100000 DBADIN 0177777,0177777,0000010,0000000, 0000001,0010000,0000000,0100000 .TE .DE In this example, the following characters are declared ``illegal'' to send or receive: all control characters (0x000 - 0x037), '#' (0x043), '@' (0x100), '\\' (0x134), and del (0x177). If the sending and receiving character sets are the same (as in this example), the DBAD tailoring item may be used to set both character sets at once: .DS .TS l l . DBAD 0177777,0177777,0000010,0000000, 0000001,0010000,0000000,0100000 .TE .DE .PP When installing the dial protocol package on a Unix system, there are a few things that have to be changed to reflect the environment such as the names, types, and speeds of the various dial-out ports and direct connect lines to other hosts. Each DPORT entry sets the parameters for a given dial-out port. A dial-out port is a modem with a separate autodialer associated with it. Each DPORT entry contains five positional arguments and then, optionally, any of three named arguments. The positional arguments are: .IP 1. 10 The path name of the port itself. .IP 2. 10 The path name of the lock file which is used to insure that no more than one process tries to use the same port at a time. If the UUCPLOCK feature was specified on the CONFIGDEFS line in Makefile.com, then the path name must be of the form "/usr/spool/uucp/LCK..tty??" where ?? is the number of the tty line being used. .IP 3. 10 The path name of the automatic calling unit (acu) that is associated with the dial-out port. .IP 4. 10 The speeds that the modem can support. This is an octal number whose bits represent supported speeds. The relationship is as follows: .DS .sp .TS box; c c l l . \fIBit\fR \fIBaud Rate\fR 0 50 1 75 2 110 3 134.5 4 150 5 200 6 300 7 600 8 1200 9 1800 10 2400 11 4800 12 9600 .TE .DE For example, 0177 is used for modems that can operate at speeds from 0-300 baud and 0400 is used for modems that only operate at 1200 baud. .IP 5. 10 The line type of this port. Line types such as "local", "wats", or "instate" are referenced in the dialing script (see script(5)) and allow selection of ports based on the capability of the associated phone line. .IP pref= 10 The prefix string to send to the acu before sending the phone number contained in the dialing script. This is typically a command string that tells the acu to get ready for a phone number and to select the appropriate outgoing modem. .IP suff= 10 The suffix string to send to the acu after sending the phone number contained in the dialing script. This is typically a command string that tells the acu to actually start dialing. .IP access= 10 This is a filename which contains a list of usernames (one per line) of the people authorized to use the associated dial-out port. If this argument is omitted, everyone will be given access. .PP For example, the following entry sets up a dial-out port for a Vadic MACS where the modem is on tty14, the dialer is on dn0, the speed is 1200-only. The prefix string gives control information to the dialer such as connection-type and modem slot address. The suffix string signals the end of the phone number. .DS DPORT ``/dev/tty14'', ``/usr/spool/uucp/LCK..tty14'', ``/dev/dn0'', 0400, pref=08, suff=``<'' .DE .PP The DLINE entry does a similar job for directly connected lines. Directly connected lines can be of three types: a hard-wired connection between two machines, an autodialing modem, and a pseudo-tty. These three cases are discussed below and the format of the DLINE entry for each is presented. .PP The first type, a hard-wired connection between 2 machines, uses a single tty port on each system. In this configuration, one machine is designated the master. The master machine has logins disabled on its tty port. The other machine, the slave, has logins enabled at a known and fixed baud rate. The master machine's port is defined with a DLINE entry which tells the name of the line (this gets used in the ``dial'' command in the dialing script (see script(5)), the path name of the port itself, the path name of a lock file to use to insure exclusive access to the port, and the speed to use for the connection. Thus, for a 9600 baud hard-wired connection, the following DLINE might be used: .DS DLINE ``udeleehp'', ``/dev/tty05'', ``/usr/spool/uucp/LCK..tty05'', 9600 .DE .PP The second type, an autodialing modem, uses a single tty port to both tell the modem what number to dial and to exchange data with the remote machine. The dialing script must contain the necessary commands to tell the modem what number to call. The DLINE entry looks just like that for a hard-wired connection. More than one DLINE entry may be included with the same name; the phone channel will try each one in succession until it finds one available. .DS DLINE ``hayes'', ``/dev/tty05'', ``/usr/spool/uucp/LCK..tty05'', 1200 DLINE ``hayes'', ``/dev/tty06'', ``/usr/spool/uucp/LCK..tty06'', 1200 .DE .PP The third type, a pseudo-tty, allows you to enter commands in the dialing script which log back into your own host and which run an auxiliary program to connect to the remote machine. This program might be an X.25 pad-out program or it might be ``cu'' or ``tip''. You must first allocate a pseudo-tty pair (e.g. /dev/ptypf and /dev/ttypf) for this application by renaming them (e.g. to /dev/ptymmdf and /dev/ttymmdf, respectively). Next, enable logins on the latter device (e.g. by placing a line such as ``12ttymmdf'' in /etc/ttys). The DLINE entry that may then be used to establish a connection back to your own machine is: .DS DLINE ``pty'', ``/dev/ptymmdf'', ``/usr/spool/uucp/LCK..ptymmdf'', 9600 .DE Note that the speed designator must be valid but that it is ignored. .TP DACCT specifies a file to use for logging phone calls for accounting purposes. Only calls made on a DPORT are logged. If the specified file does not exist, then logging of calls is disabled. .DS DACCT ``/usr/mmdf/log/dial_log'' .DE .NH 2 Some Miscellaneous Bits .PP MDFLCHAN is used to set the default channel to something other than local. .PP NIQUEDIR is set to a directory used for NIFTP temporary files hence is only used on NIFTP channels. .NH 2 Installation .PP Now that you have a runtime tailor file in place, you are ready to create the required directory structure and install the binaries in it. Run the program src/tools/xsetup (see \fIsetup\fR(8)) to generate the needed directories. You will have to create the RCVDIR and BINDIR directories by hand if they don't already exist; \fIsetup\fR has no notion of these directories (tailored in Makefile.com). .XX Run src/tools/xsetup (it will ask for confirmation before changing anything). .XX Create the directories RCVDIR and BINDIR if necessary. .PP The programs \fIsubmit\fR and \fIdeliver\fR are unable to automatically create queue directories (q.xxx). If you later add new channel definitions to your runtime tailoring file, you will need to create the queue directories for those channels. The program \fIsetup\fR can be used to do this. Note that this program will sometimes complain unnecessarily about permission problems if you have played with the file modes to allow group access to the lock directory. .PP Now that the directories have been created, you are ready to install the binaries. Running `make install' from the top of the \*M source tree will install everything in the subdirectories \fIsrc\fR and \fIuip\fR (in that order). You can also run `make install' in each of these directories alone. Note that all \*M programs such as background \fIdeliver\fR's and smtp daemons must be killed off before running `make install'. .XX Run `make install' from the top of the \*M source tree. .sp 2 .ne 10 .NH 1 Miscellaneous Tasks .PP There remain a number of things to be done either now or sometime later that are not critical but should eventually be attended to. You will need to setup your tables and run dbmbuild if you are using the hashed database (DBM library). Setting up tables is described in the next section of this document. You should arrange for the \fIcleanque\fR program to be called from \fIcron\fR at least twice a day, and perhaps more often at your choice. You will need to install a program called `setlogs' in the log directory to do periodic cleanup on the logging files. This should also be called from \fIcron\fR at some reasonable interval. The \fIcheckup\fR program will complain if the \fIsetup\fR program is missing, but this is not a critical error. Eventually you will need to edit /etc/rc to have it start up at least one background .I deliver daemon. Be sure that there is an ampersand (`&') on the end of the line invoking .I deliver since it does not automatically detach itself. In some situations you may wish to have more than one. See the section on tuning for more information. each one in succession until it finds one available. .DS DLINE ``hayes'', ``/dev/tty05'', ``/usr/spool/uucp/LCK..tty05'', 1200 DLINE ``hayes'', ``/dev/tty06'', ``/usr/spool/uucp/LCK..mmdf/doc/administrators/tuning 444 0 12 23025 3631170262 12032 .\".AU .\"Douglas P. Kingston III .\".AI .\"Ballistic Research Laboratory .\"USA AMCCOM .\"Aberdeen Proving Ground, Maryland. 21005 .\"(301) 278-6651 .bp .NH Tuning \*M .NH 2 Introduction .PP Mail systems can vary greatly in the amount of traffic they carry and the communities they serve. This variety of environments and loads cannot be properly served without appropriate tuning of the mailsystem to the environment it must serve. Tuning can take many forms, but the most common is adjusting the number and type of .I deliver daemons you have servicing the mail queues. Another common technique is to create additional mail queues which may serve a special subset of a larger queue to enable processing of that subset at a higher or lower priority. .NH 2 Multiple Queues and Channels .PP One common method to improve the performance of \*M is to partition the mail queue into multiple directories. Generally this is done such that each channel has its own queue. The advantage is that when the daemon is working on a given channel, it need only examine messages which it knows are for that channel (by virtue of being in the queue directory for that channel). It is possible to have multiple channels operating from a single queue, and this might be advantageous if you had a very large number of channels each with a very small amount of traffic, but this is unlikely and probably only worth thinking about when the number of channels approaches 100 or more. .PP Adding channels which cover a subset of hosts accessible by another channel is a good technique for increasing throughput and reduce delivery latency. .PP A good example of this is the BRLNET channel used at BRL. All BRL hosts are on the ARPANET (MILNET), and as such could be queued into the smtp channel for delivery. If this were done, the BRLNET messages would be mixed up with the hundreds of messages pending for tardy ARPANET sites. In addition, the delivery latency would rise dramatically since you would have to wade through all the tardy messages on each pass over the queue. To provide better service to the directly connected hosts, we defined a BRLNET channel, with its own queue, and re-used the smtp program. This channel is defined before the SMTP channel so when a message is queued for a BRL host, the \fIsubmit\fR program finds the BRL host name first in the BRLNET channel table and therefore queues it into the BRLNET channel. The \fIdeliver\fR servicing the BRLNET channel services only the BRLNET channel and a couple of lightly loaded channels like the local and list channels. This means the the BRLNET channel gets a high level of service and because of our high-speed local area networks, the delivery latency is quite small. .PP Another use for separate channels is for tardy hosts. At times there have been several hosts tardy over a long period of time (weeks). Rather than have them fill up the regular mail queues, we created a special channel with only these ``turkey'' hosts and placed this channel ahead of the smtp channel in the configuration file. This caused mail for the turkey hosts to be queued into this channel rather than the smtp channel. The turkey channel had its \fIdeliver\fR run less often than normal. .NH 2 Multiple Daemons .PP It is possible to run multiple invocations of the \fIdeliver\fR daemon. There are three advantages to multiple daemons. The first is increased throughput. Although the mail processes do use a considerable amount of cpu time, they spend a large amount of time waiting while actually delivering mail so it is possible for two \fIdeliver\fR daemons to deliver close to twice as much mail in the same period as one daemon. Second, with more than one deliver, the delivery latency will be reduced since with more daemons, the chances that one will find your message in the same amount of time are twice as good. Third, multiple daemons servicing the same channel provide redundant service. If one daemon should die off, the remaining delivers on that channel will continue to deliver, although providing a reduced level of service. .NH 2 Tailoring Delivery Characteristics .PP There are numerous options that can be used to control the behavior of deliver daemons. The most common is `\-c\fIchannel,channel,channel\fR' which sets which channels the deliver will attempt to process. One might have one daemon processing local, uucp, and localnet mail and two other daemons just processing smtp mail if you were a large site relaying a lot of mail to the Arpanet. The configuration used on the main mail host at BRL is as follows: .sp .nf .RS .nf deliver \-L/usr/mmdf/log/deliver1 \-b \-l15 \-clocal,brlnet,list,uucp,bb deliver \-L/usr/mmdf/log/deliver2 \-b \-t24 \-csmtp deliver \-L/usr/mmdf/log/deliver3 \-b \-t48 \-csmtp deliver \-L/usr/mmdf/log/deliver4 \-b \-csmtp deliver \-b \-l15 \-s \-T300 \-cucl .fi .RE .fi .PP This is a very good example because it demonstrates the use of almost all the different delivery tuning mechanisms. There are seven different queues. There are five separate \fIdeliver\fR daemons. The queues can be characterized as follows: .sp .nf .DS .TS l l . local \- Local mail delivery, light volume, fast brlnet \- Mail to other BRL hosts via LANs, medium volume, some backup list \- List processor, light volume, fast uucp \- UUCP channel, light volume, fast bb \- BBoards channel, light volume, fast smtp \- SMTP to Arpanet, heavy volume, large queue backups ucl \- SMTP to UCL-CS only, heavy volume, occasional large backups .TE .DE .sp .fi The first five are grouped together under one deliver daemon, with the only major user being the BRLNET channel. Since the other channels create an insignificant load, the BRLNET hosts get an essentially dedicated \fIdeliver\fR daemon. The smtp channel has so much volume, and spends so much time waiting for read and writes across the country, that we have three daemons processing this channel. The ucl channel was created to handle all the mail headed to one host, UCL-CS, since we send them a considerable volume of mail, and occasionally the satellite has a bad day, and the mail backs up. We have had over a thousand messages backed up for this host on more than one occasion. The first time this happened, it really slowed down the smtp delivery. Now when UCL-CS is tardy, they hurt no one. .NH 2 Dead Host Cache Time-to-live .PP When a host is discovered to be dead or not responding, it is marked ``dead'' by the \fIdeliver\fR daemon. All further addresses for that host in the current message or subsequent messages are skipped until the time-to-live of the ``dead host'' record has been reached. The time-to-live is usually defaulted to 2 hours. The time-to-live can be changed by using the \-l\fIminutes\fR flag on the \fIdeliver\fR command line. For example, you will note that on the first \fIdeliver\fR daemon in the example, the time-to-live is set to 15 minutes since we do not expect BRLNET hosts to spend much time down and we want the \fIdeliver\fR daemon to quickly notice when a BRLNET host comes back on line. .NH 2 Recent Message Selection .PP One of the properties of sending to hundreds of hosts is that if a host is down for more than a day or two, it is likely to be down considerably longer. It is wasteful to have all the daemons constantly working over all the old messages when the chance of a machine reappearing are relatively small compared to the chances that a machine down only 4 hours will reappear. The \-t\fIhours\fR switch allows us to force the daemon to consider only messages that were queued in the past \fIhours\fR hours. In other words, we force the daemon to concentrate on delivering the recent messages, which have a higher likelihood of delivery. One daemon looking after the dregs is enough. You should only use this if you have at least one daemon that does not have the \-t flag, processing the same queue. In the example above you can see the \-t flag is being used on both the second and third \fIdeliver\fR daemons which both service the largest channel (smtp). The third daemon is not restricted with a \-t. .NH 2 Daemon Sleep Time .PP There are certain channels that you wish to run in the background but for which you do not wish to attempt delivery as often as the default settings would try. The \-T\fIseconds\fR switch changes the amount of time in seconds the daemon will sleep between attempts to deliver messages. In our example above, we have used this switch on the ucl channel. This is generally a low-priority mail queue and we are not concerned that we wait an extra 10 or 20 minutes to discover that the host has been down, since when it goes down, it is usually down for hours or days. The time when this really pays off is when the host is down and mail has backed up. When this happens, each time the daemon wakes up, the daemon examines every address and then decides to skip it because the host is down. Occasionally the daemon will retry the connection (if the time-to-live has expired) but it spends most of its time not delivering mail. We simply elect to not attempt as often, therefore wasting less time. .NH 2 Queue Sort Override .PP In normal operation, \fIdeliver\fR will attempt to sort the queue of messages for each channel in the order they were submitted. Sorting a large mail queue can be quite expensive and time consuming. On some channels you don't care in what order the messages are delivered. In these cases it is advantageous from an efficiency standpoint to be able to disable the queue sorting. The \-s option does just that. If this option is specified, the daemon will read the queue and process the messages in the order they were found. This will often not be the order in which they were submitted. her channels create an insignificant load, the BRLNET hosts get an essentially dedicated \fIdeliver\fR daemon. The smtp channel has so much volume, and spends so much time waiting for read and writes across the country, that we have three daemons processing this channel. The ucl channel was created to handle all the mail headed to one host, UCL-CS, since we send them a considerable volume of mail, and occasionally the satellite has a bad day, and the mail backs up. We have had over a mmdf/doc/administrators/Makefile 444 0 12 260 3620510343 12173 SOURCE = title overview building runtime tables tuning troff: $(SOURCE) tbl $(SOURCE) | troff -t -ms > admin.troff nroff: $(SOURCE) tbl $(SOURCE) | nroff -ms > admin.nroff the chance of a machine reappearing are relatively small compared to the chances that a machine down only 4 hours will reappear. The \-t\fIhours\fR switch allows us to force the daemon to consider only messages that were queued in the past \fIhours\fR hours. In other words, we force the daemon to concentrate on delivering the recentmmdf/doc/administrators/overview 444 0 12 12635 3631170252 12400 .\".AU .\"Douglas P. Kingston III .\".AI .\"Ballistic Research Laboratory .\"USA AMCCOM .\"Aberdeen Proving Ground, Maryland. 21005 .\"(301) 278-6651 .NH 1 Overview .NH 2 Introduction .PP The \*M mail system has seen a great deal of development over the past several years and has involved a large number of people. This guide is an attempt to gather together the information which until now was only passed along as folklore or learned by reading the code. The guide is divided into five parts. The Overview will attempt to give a summary of the contents of the \*M distribution and where to look for more information. The next section explains, step by step, how to compile your own tailored \*M. The following sections cover runtime tailoring from the configuration file, building host, domain, and alias tables, and finally tuning \*M for best performance on your system. The \*M programs are all linked with a library that contains a default compiled-in configuration (which may be very scant). The compiled-in values can be overridden or augmented by the runtime tailoring file and tables. .NH 2 Available Channels .NH 3 The Local Channel .PP The local channel is responsible for delivering mail to mailboxes and processes on the local machine. It normally delivers directly to a mailbox file in the user's home directory or /usr/spool/mail. It is also capable of delivering to processes or alternate files under the control of the alias file and/or the user's own "maildelivery" file, as described in manual section \fImaildelivery(5)\fR. There is also provision for a system default maildelivery file if the user does not supply one. .NH 3 The List Channel .PP The list channel is a special channel that remails messages. The channel simply invokes \fBsubmit\fR and feeds the addresses and text back into the \*M mail system. This is often useful to avoid long address verification procedures at posting time or to force the verification take place in the background. The list channel also knows how to replace the return address of a message with the list-request address when appropriate. .NH 3 The SMTP Channel .PP This channel implements the Simple Mail Transfer Protocol as described in RFC-821. There is currently code to support the 4.1aBSD, 4.2BSD, and 4.3BSD network semantics, BBN TCP network semantics, and 2.9BSD semantics. .NH 3 The Delay Channel .PP Generally used in conjunction with nameserver support. If there is a nameserver failure for whatever reason, and this channel is configured, the mail will be conditionally accepted and queued to the delay channel. The delay channel will re-submit the mail at a later date until a definitive reply is received from the nameserver. .NH 3 The BadUsers Channel .PP There is no specific code associated with this channel. Submit knows if it finds a username it does not know, it can queue it on this channel for forwarding to another host that has a better user database. The channel name is hardcoded as ``badusers''. .NH 3 The BadHosts Channel .PP Like the BadUsers channel, except this is for mail to hosts that submit does not know. The channel name is hardcoded as ``badhosts''. .NH 3 The Phone Channel .PP The PhoneNet dialup network protocol is supported by this channel. PhoneNet is a dialup package developed at UDEL and used extensively by the CSNET and the Army as a link layer for \*M for its hosts not directly connected to a Local or Wide-Area network. .NH 3 The Pobox Channel .PP This is a do-nothing channel that allows queued mail to be picked up by another process. The Pobox channel is typically used in a PhoneNet slave site, but is by no means limited to this. In the PhoneNet application, the Pobox channel passes mail from the queue (via deliver) to the phone (via the PhoneNet Slave program). .NH 3 The UUCP Channel .PP UUCP mailing is supported by this channel. Addresses are converted as necessary. Inbound mail is converted into RFC822 format, preserving existing 822 format header lines. Outbound, ``From<space>'' lines are added and rmail arguments are generated in UUCP bang route format. The channel will make use of pathalias output paths to simplify incoming addresses and to route outgoing addresses. .NH 3 The NIFTP Channel .PP NIFTP is a batched file transfer facility used in the UK. This channel uses NIFTP to send and receive mail by creating batched requests that send mail as files. .PP The NIFTP channel produces message files formatted according to the JNT Mail Protocol. These are transferred by a file transfer protocol, typically the UK NIFTP (Network Independent FTP), to the remote site. .NH 3 The BBoards+POP Channel .PP This channel is used to interface into the BBoards system that is part of MH.{4,5,&6} as developed at UCI. The channel is also compiled to a slightly different version for supporting Post Office box Protocol (POP). (Provided by Marshall Rose) .NH 3 The EAN Channel .PP This channel is used to access the EAN X.400 system produced by the University of British Columbia. .NH 3 The Program Channel .PP This channel is used to interface well behaved programs to the \*M mail system. It does no more than try to reliably pass mail in or out of \*M. Systems that require special reformatting of the message (such as UUCP) cannot use this channel. For example this channel could be used to interface the ACSNET network or a Batch SMTP implementation both of which prefer to deal with RFC822 format mail. This is a new channel and may change somewhat in the future to make it more powerful. n take place in the background. The list channel also knows how to replace the return address of ammdf/doc/administrators/tables 444 0 12 32727 3656410700 12012 .\".AU .\"Steve Kille .\".AI .\"Department of Computer Science .\"University College London .\".AB .\"The \*M mail system provides a flexible table structure to .\"allow (user) transparent connectivity to a large number of sites, .\"using multiple mail protocols and a mixture of direct and indirect .\"connections. This document explains how set up these tables. .\".AE .bp .NH Configuring Tables for \*M .NH 2 Introduction .PP This section explains how to construct the tables which are defined in the runtime tailoring file. The exact syntax of the tables is documented in tables(5). It is assumed that most sites will be in one of a fairly small number of standard situations regarding mail connectivity. It is suggested that the simplest approach for most sites will be to base the configuration on that of a site in a similar situation. For this reason, we supply a number of sample configurations. For a given sample site \fIfoo\fR, the runtime tailoring file and its associated tables are contained in the directory \fBsamples/\fIfoo\fR. .PP The current sample configurations are: .RS .IP bbn 12 A 4.2BSD Internet host using domain servers. .IP vgr 12 A 4.2BSD Internet host with heavy mail traffic. .IP uclvax2 12 An Internet host with links to various UK networks; illustrates authorization on a per-user basis. .IP 4.3vax 12 A 4.3BSD workstation on an ethernet. Offloads remote traffic to another host. .IP csnet-relay 12 A very large PhoneNet relay host with Internet access. .IP okstate 12 A V7 Perkin-Elmer host on CSNET PhoneNet, UUCP and a local network. .IP laser 12 A System V Release 0 based 68000 workstation. One UUCP link to a gateway host. It pretends its name is that of the gateway to hide the name laser from the rest of the world. Unknown hosts and users are passed to the gateway. .IP scripts Various samples of PhoneNet dialout scripts. .RE .PP A basic word about the tables for domains and channels is in order. The tables may appear similar, but they serve very different functions. The host lookup procedure is a two-tier process. A destination is first looked up in the domain tables. \*M need not find an exact match, a match on a partial domain reference will also succeed, although the full domain match will be attempted first. When a match is found in the domain tables, the RHS (right hand side) will have the full domain specification of a host to which the message should be passed to reach the destination (in many cases this IS the destination). That host is then looked up in the channel tables, searching in the order the channels were defined. The channel tables contain host names (full domain form) on the LHS and typically addressing data on the RHS. Sometimes addressing data does not make sense (like in the local channel) and some fixed string is substituted. (See the addressing document for more information about the domain matching process). .PP In the examples here, the RHS and the LHS are separated by a colon and some white space. The colon is in fact optional. You may find the colon missing from many of the sample files. .NH 2 Setting Up Domain Tables .PP Domain tables are themselves specified by a domain. When the domain of a table is concatenated with the LHS of any entry in the table, a domain must result. On the RHS are HOST values. This two level hierarchy is intended as a compromise between efficiency and flexibility. Domain tables are matched longest first. Take the `AC.UK' domain for example (United Kingdom, Academic community). Thus when matching `CS.CAMFORD.AC.UK', if a table "CAMFORD.AC.UK" is present this will be examined before "AC.UK". If an entry `CAMFORD' was found in "AC.UK" (thus corresponding to domain `CAMFORD.AC.UK'), then it would be identified as a relay point. If no domain table is identified, then the domain tables are searched in order. In this case, if `CS.CAMFORD' were presented, tables would first be searched for CS.CAMFORD and then (if no match) for CAMFORD. If the latter were found in "AC.UK", then `CAMFORD.AC.UK' would be identified as a relay domain for `CS.CAMFORD.AC.UK'. .PP Note that if #define BOTHEND is set, then this will work for both RFC 819 and NRS ordering (for those lucky enough to live in the UK). .PP The best place to start is with the "TOP" table which matches rooted domains. It should contain the domain on the LHS and an appropriate gateway on the RHS (host name, using full domain notation). For example, on an ARPANET site it might be: .ne 8 .nf .RS .TS l l . mailnet: mit-multics.arpa bitnet: wiscvm.arpa csnet: csnet-relay.arpa uucp: harvard.arpa ac.uk: ucl-cs.arpa .TE .RE .fi .ne 7 In the UK it might be: .nf .RS .TS l l . arpa: ucl-cs.ac.uk csnet: ucl-cs.ac.uk dec: ucl-cs.ac.uk .TE .RE .fi Other domains will be given explicit tables, derived from various sources. There is a domain extension which can be useful. If domains are not directly connected, but require further verification (i.e. beyond the top level or two) the entry should be of the form: .sp partial-domain:\ \ \ \ host.domain\ \ \.\.\.\ \ \ host.domain .sp As with other entries, alternative partial domain names should follow the first one, with the same RHS value. The semantics of the RHS is a source route from RIGHT TO LEFT. The connect site is specified by the right-hand host. The left-hand host value will be visible only when ap=host reformatting is used (otherwise, the concatenation of the preferred partial domain and the domain associated with the table is used). For example, if EECS.SALBRIDGE.AC.UK is reached through CAMFORD.AC.UK, the entry in the ``AC.UK'' table would be: .sp salbridge:\ \ \ \ salbridge.ac.uk\ \ \ camford.ac.uk .sp and would be suitable to route all mail destined for machines at Salbridge via Camford. .NH 2 Setting up Channel Tables .PP In section 3.8, a set of channels was determined. Now the channel tables should be built. The LHS should contain the domain of the host served by that channel and the RHS should contain information used by the channel to connect to the host. The local channel requires the RHS to be the string specified by MLNAME (or .I locname from conf.c if it is not runtime tailored). The smtp channel, for example, requires the Internet address on the RHS of its channel table (e.g. "129.11.5.3"). The phone and pobox channels don't use the RHS (but the RHS is typically set to the host name). .NH 2 How to Modify a Standard Configuration .PP This section assumes that you have identified a suitable configuration on which to base your tables. .NH 3 Modifying Channel Tables .PP Each of the channels will have a set of hosts associated with it. All channel tables associated with external hosts should be usable as they stand. The local channel table (used by both the local and list channels) should have at least the following two entries: .ne 12 .nf .RS .TS l l . \fIlocname\fR.\fIlocdomain\fR: \fIlocname\fR \fIlocname\fR: \fIlocname\fR .TE .RE You may also need the entries with LHS \fIlocmachine.locname.locdomain\fR and \fIlocmachine\fR if you are going to hide a number of sites behind a single name. The RHS should still be \fIlocname\fR. For example for UFOO.CSNET with locmachine set to "VAX1": .RS .TS l l . ufoo.csnet: ufoo ufoo: ufoo vax1.ufoo.csnet: ufoo vax1: ufoo .TE .RE .fi .NH 3 Modifying Domain Tables .PP Domain tables associated with a number of domains will be provided. All can be taken as given, except the table for the local domain. To this should be prepended all alternative names of \fIlocname\fR. The RHS of all these entries should be \fIlocname\fR. The first entry in this table should be \fIlocname\fR:\fIlocname\fR\ . Thus if UFOO is also know as UBAR and UPIG, the first entries in the CSNET domain table should be: .ne 6 .nf .RS .TS l l . ufoo: ufoo ubar: ufoo upig: ufoo .TE .RE .fi .NH 3 Alias Tables .PP The next step is to set up a small alias file. Assuming your own login on UNIX to be ``bill'', the following initial file is suggested as a example: .ne 6 .nf .RS .TS l l . root: bill mmdf: bill postmaster: bill .TE .RE .fi In particular, the \*M \fIcheckup\fR program will check to see that an alias exists for ``postmaster'' and the \*M login you specified in the configuration (herein assumed to be ``mmdf''). It is suggested that one of the sample alias files be used as a model for the alias file organization. To handle the expansion of a mailing-list ``foo'' from a file, the entries for the list ``foo'' should be of the form: .ne 12 .nf .RS .TS l l . foo: foo-outbound@\fIlocname\fR@list foo-outbound: :include:filename foo-request: list-maintainer .TE .RE For example: .RS .TS l l . staff: staff-outbound@ufoo@list staff-outbound: :include:/etc/alias/staff staff-request: bill .TE .RE .fi .NH 3 User and Mail-ID Tables .PP The final step is to set up the User and Mail-ID tables. This step is only necessary if you have enabled use of Mail-IDs. The ``user'' table maps users to Mail-IDs. For example: .RS .TS l l . wiz: unix-wizards-request wiz2: unix-wizards-request joe: joe bob: bob .TE .RE The ``mailids'' table maps Mail-IDs to users. For example: .RS .TS l l . unix-wizards-request: wiz joe: joe bob: bob .TE .RE In this example, mail addressed to ``unix-wizards-request'' would be delivered to user ``wiz''. However, both users ``wiz'' and ``wiz2'' could send mail and the mail would appear to come from ``unix-wizards-request''. .NH 2 How to Design Your Own Tables .PP This section is written for those who have the misfortune not to be able to base their design on one of the standard configurations, or who wish to make radical extensions to a standard configuration. Rather than attempting to describe many possibilities, it tries to explain what is being aimed at. .PP The terms DOMAIN and HOST are used here in a quite specific manner. The domain name space is assumed to be a global hierarchy with the most significant component on the RHS (with apologies to the long suffering UK Mailpeople who will have to live with back to front names). Domains are implicitly fully qualified (i.e. extending to the root). For example: UK, AC.UK, CAMFORD.AC.UK, and CS.CAMFORD.AC.UK are all domains. CAMFORD is not a domain. Domains are administrative entities and may be synonymous with a host. A host is a computer to which your computer makes a direct connection. The entries on the LHS of channel tables and RHS of domain tables are hosts. All host names must be unique within a given system (to allow host -> channel lookup). A useful convention to ensure host uniqueness is to have the host name the same as the fully qualified domain. This convention is also likely to facilitate the introduction of nameservers. .PP The key to remember is that the validation of an address is a two-fold operation. First, the domain or a parent of it must be found in the domain tables (CS.CAMFORD.AC.UK, or CAMFORD.AC.UK, or AC.UK, or UK). Once it is found, the RHS will contain a host or route to a host. The second step is to try and find/verify the route to the host by looking up the host or route elements in the various channel tables. The basic premise is that you should be able to find hosts specified in the RHS of a domain table by looking in the LHS of channel tables. .NH 2 Hashing Your Tables .PP Once you have created the necessary tables to support the domains and channels defined in your runtime tailoring file, you must combine these tables into a hashed database. If you defined CH_TB to be 'ch_tbseq', then you can skip this section. The dbmbuild program uses the definitions in the runtime tailoring file to build the hashed database. It will report if it finds any tables missing. See \fIdbmbuild(8)\fR for details. .XX Run \fIdbmbuild\fR (no switches are required -- the default -n is correct). .NH 2 Testing Your Configuration .PP Now test your configuration by running the \fIcheckup\fR program (see \fIcheckup\fR(8)). This program will report any inconsistencies in your installation. Items marked with asterisks are worthy of note. Items in brackets are advisory only. .XX Run \fIcheckup\fR. .PP Now test your tables by seeing if various addresses are accepted by \fIcheckaddr\fR(8) or \fIsend\fR(1). You may want to give the -W option to submit via send or the -w option to checkaddr. This will tell you on what channel \*M chose to queue the message. .XX Run \fIcheckaddr\fR or \fIsend\fR to check your tables and configuration. .PP If you find a problem with an address (or more likely, a particular class of addresses), you should make the following checks. Assume as an example that CAMFORD.AC.UK is in question: .IP (1) If there is a domain table for AC.UK, the LHS `CAMFORD' should exist. If there is no domain table for AC.UK, but there is one for UK, then LHS `CAMFORD.AC' or `AC' should exist. Finally if there is no UK or AC.UK domain table, then on the LHS of the ``top'' table, `UK', `AC.UK', or `CAMFORD.AC.UK' should exist. It is noted that the required domain should be equal to or a subdomain of the concatenation of a table key (LHS) and the domain associated with the table (I hope that the pattern is clear from this). .IP (2) Then consider the RHS of the first entry identified above. This should be found on the LHS of the appropriate channel and (given that all domain table RHSs must be unique) not found on the LHS of any inappropriate channel. This entry should identify either CAMFORD.AC.UK or the preferred relay. (If there are multiple matches, the first one is generally selected according to the order of channel definitions in the tailoring file). .IP (3) These first two criteria should be sufficient to cause the message to be queued, and the RHS of the channel table entry may be used by the channel to deliver the message. e many possibilities, it tries to explainmmdf/doc/administrators/title 444 0 12 2576 3671073136 11645 .ds M MMDFII .de XX .br .ft R .IP "\(rh\ \ \ [\ ]\ \ " 1i .. .TL Installing and Operating MMDF II\u*\d .AU Douglas P. Kingston III .AI Ballistic Research Laboratory Aberdeen Proving Ground, Maryland, USA. 21005 (301) 278-6651 .AU Steve Kille .AI Department of Computer Science University College London Gower Street, London, England. WC1E 6BT (44) 1-387-7050 ext 733 .AU Julian Onions .AI Department of Computer Science, The University, Nottingham, England. NG7 2RD. (0602) 506101 ext 3595 .AU Daniel B. Long .AI CSNET Coordination and Information Center BBN Laboratories 10 Moulton Street Cambridge, Massachusetts. 02238 (617) 497-2777 .AB The \*M mail system is a powerful but complex mail system that requires a certain degree of care in its setup and maintenance. This paper will try to give a step-by-step installation procedure and will recommend how to maintain the \*M system on a day-to-day basis. Topics covered include system generation, runtime configuration, generation of channel and domain tables, and tuning of the \*M system. .AE .FS This document describes the second release of MMDF II, generally known as MMDF IIb. Credit and thanks are due to several people (besides the authors) who contributed much time and effort to the MMDF IIb project, in particular Phil Cockcroft of UCL, Jim Galvin of UDEL, Craig Partridge and Dennis Rockwell of CSNET, and Howard Walter of BRL. .FE .bp mise is that you should be able to find hosts specified in the RHS of a domain table by looking in the LHS of channel tables. .NH mmdf/doc/table.sample.kille 444 0 12 32144 3631170245 11134 Host and Domain Table examples Steve Kille Febraury 1984 This text is meant to illustrate by example, the mechanism for setting up host and domain tables. It is assumed that the manual pages tailor(5.mmdf) tables(5.mmdf) have been read if not understood. This sheet contains three example: 1) A simple Arpa host with minimal tables 2) A UK host, illustrating handling of local domain with more than two components, and basic use of the list channel 3) A complex example, illustrating use of locmachine, and various other subtle points 1. Basic ARPA host The following tailor values are assumed in these examples: LOCDOMAIN = ARPA LOCNAME = Ivy For reference in the following tables, the structure is based on the following table, channel, and domain specifications. # Only one domain entry needed. The table maps to a file, and # then MDMN specifies the entry. Note that the flags entry is # probably redundant, but must always be set to 01. MTBL "ARPA",file="arpa",show="ARPA Domain" MDMN "ARPA",show="ARPA Domain",table="ARPA" # Channel parameters, with fields not pertinent to address lookup removed MCHN local show="Local Channel" MCHN smtp show="SMTP channel" First the channel tables will be looked at. When domains are reformatted, either host or domain values are output. The selection of host names is related to current DCA and JNT domain policy. Domains that are 'allowed' are also host names. In other cases, abbreviated names are used. # Local channel table # note - RHS = LOCNAME in all cases # values for LHS include LOCNAME, and all full domain names # Northing else should ever be needed ivy.arpa:ivy ivy:ivy # TCP/SMTP hosts channel table # Only official HOST names in table alta-coma.arpa:3.1.0.50 yuma.arpa:6.1.0.1 bbncca.arpa:8.0.0.2 bbnccb.arpa:8.1.0.2 bbnccc.arpa:8.2.0.2 bbnccd.arpa:8.3.0.2 bbn-labs-admin.arpa:8.4.0.2 bbncck.arpa:8.5.0.2 ..... # ARPA Domain table # # Here are all of the names from hosts.txt # Official name first. HOST name (as used in channel table) on RHS alta-coma:alta-coma.arpa yuma:yuma.arpa bbncca:bbncca.arpa bbnccb:bbnccb.arpa bbnccc:bbnccc.arpa bbnccd:bbnccd.arpa bbn-labs-admin:bbn-labs-admin.arpa bbn-labs:bbn-labs-admin.arpa bbnl:bbn-labs-admin.arpa bbncck:bbncck.arpa ...... 2. Baisc UK Host The following tailor values are assumed in these examples: LOCDOMAIN = Camford.AC.UK LOCNAME = CS For reference in the following tables, the structure is based on the following table, channel, and domain specifications. # Camford.AC.UK - always the most local domain first. The table # maps to a file, and the domin spec points to the table # This domain is for other hosts within the camford domain # (e.g physics.camford.ac.uk) MTBL "Camford",file="camford",show="camford.ac.uk domain table" MDMN "Camford.AC.UK",show="Camford.AC.UK domain",table="Camford" # Then The AC.UK domain for other .AC.UK domains MTBL "AC-UK",file="ac-uk",show="AC.UK Domain" MDMN "AC.UK",show="AC.UK Domain",table="AC-UK" # then AC.UK domain, which uses the same table as AC.UK # The settings of name=AC and dmn=AC.UK will let # foo.ac be mapped to foo.ac.uk MDMN "AC",show="AC.UK Domain (AC)",table="AC-UK",name="AC",dmn="AC.UK" # Channel parameters, with fields not pertinent to address lookup removed MCHN local show="Local Channel" MCHN list show="via List-Channel" # in this case, the list channel will use the local channel # table, rather than needing its own table. If foo is a list, # The alis file would have entries: # foo:foo-outbound@camford@list # foo-outbound: :include:/etc/alias/foo # This would force foo to be deliverd to the list channel. # The list is then expanded in background MCHN niserc show="via Sercnet with NIFTP" MCHN uucp show="with UUCP" First the channel tables will be looked at. When domains are reformatted, either host or domain values are output. The selection of host names is related to current JNT domain policy. Domains that are 'allowed' are also host names. In other cases, abbreviated names are used. (e.g. until the NRS is introduced, hostnames are foo rather than foo.ac.uk). NOTE on NRS ordering (Americans ignore this): Internal tables all use RFC 819 ordering. Mapping for reformatting output should be selected. # Local channel table # note - RHS = LOCNAME in all cases # values for LHS include LOCNAME, and all full domain names cs.camford.ac.uk:cs cs:cs # Sercnet Table # Note that RHS is mapped to a Transport address by the lower # level (NIFTP) # # As neither DCA nor JNT would like to see names with .AC.UK # in, UK host names are not full domains. abxa:abxa/mail apca:apca/mail baca:baca/mail # Some other camford hosts are reached over sercnet bio.camford:bio.camford/mail # UUCP table # This might be used for both remote and camford hosts eagle:eagle physics.camford:camphys The rest of this sample description shows the structuring of the domain tables. # Camford.AC.UK domain table # This contains all *.camford.ac.uk # with alternative anmes on LHS, and HOST names on RHS # Official names first # Ourselves - note lowercase in tables cs:cs comp-sci:cs # And the rest bio:bio.camford biology:bio.camford physics:physics.camford # AC.UK Domain table # # List of *.AC.UK # Note that some are in different channels abxa:abxa apca:apca baca:baca eagle:eagle 3. Complex Example A real extract is given, as this is more straightforeward to check! The following tailor values are assumed in these examples: LOCDOMAIN = AC.UK LOCNAME = Ucl-Cs LOCMACHINE = 44a This mechanism should still work if LOCMACHINE is not set. For reference in the following tables, the structure is based on the following table, channel, and domain specifications. # Ucl-Cs.AC.UK - always the most local domain first. The table # maps to a file, and the domin spec points to the table MTBL "Ucl-Cs",file="ucl-cs",show="Ucl-Cs domain table" MDMN "Ucl-Cs.AC.UK",show="Ucl-Cs.AC.UK domain",table="Ucl-Cs" # Then AC.UK MTBL "AC-UK",file="ac-uk",show="AC.UK Domain" MDMN "AC.UK",show="AC.UK Domain",table="AC-UK" # then AC domain, which uses the same table as AC.UK # The settings of name=AC and dmn=AC.UK will let # foo.ac be mapped to foo.ac.uk MDMN "AC",show="AC.UK Domain (AC)",table="AC-UK",name="AC",dmn="AC.UK" MTBL "ARPA",file="arpa",show="ARPA Domain" MDMN "ARPA",show="ARPA Domain",table="ARPA" MTBL "MAILNET",file="mailnet",show="Mailnet Domain" MDMN "MAILNET",show="Mailnet Domain",table="MAILNET" # The top level domain is entered by setting Name and dmn to "" MTBL "Top-Level",file="top",show="Top Level Domain" MDMN "Top-Level",show="Top level Domain",table="Top-Level",name="",dmn="" # Channel parameters, with fields not pertinent to address lookup removed MCHN local show="Local (44a)" MCHN list show="via List-Channel" MCHN sring show="with SMTP" (around local ring) MCHN satnet show="via Satnet with SMTP",ldomain="ARPA" MCHN niserc show="via Sercnet with NIFTP" First the channel tables will be looked at. When domains are reformatted, either host or domain values are output. The selection of host names is related to current DCA and JNT domain policy. Domains that are 'allowed' are also host names. In other cases, abbreviated names are used. # Local channel table # note - RHS = LOCNAME in all cases # values for LHS include LOCNAME, and all full domain names ucl-cs:ucl-cs ucl-cs.ac.uk:ucl-cs ucl-cs.arpa:ucl-cs # These two ar only needed if LOCMACHINE is set # The LHS should be LOCMACHINE and LOC-MACHINE+NAME+DOMAIN 44a:ucl-cs 44a.ucl-cs.ac.uk:ucl-cs # List channel table # This has only one entry, and is only needed if LOCMACHINE is # set to give transparent routing between a number of machines # This enables lists on other machines to be referred to iin # the same manner # in this case, an alis file entry might be # foo:foo-outbound@44c-list # foo-outbound: :include: /etc/alias/foo@44c # The @44c in the second line ensures that mail sent to foo-outbound # works correctly 44a-list:44a-list # Ring channel table (abbreviated) # This is the table associated with the local ring # The local references need NOT be removed, and so all machines # can use the same table. These names are not seen by # users unless typed explicity. RHS is transport address. ucl-cs.arpa:128.16.9.3 44a:smtp44a 44b:smtp44b 44c:smtp44c ... # References to other list channels are included so that non-local # lists will be sent around the local net to the next machine 44a-list:smtp44a 44b-list:smtp44b 44c-list:smtp44c ... # TCP/SMTP hosts channel table # Note special reference to ourselves at the top of the table # To protect against table update errors # NOte that multiples of the RHS are included to do the # reverse mapping correctly from all registered IP addresses ucl-cs.arpa:128.16.9.3 ucl-cs.arpa:14.0.0.9 # This is needed for convenient loopback testing # (e.g to use steve@ucl-cs@satnet rather than steve@ucl-cs.arpa@satnet ucl-cs:128.16.9.3 # # Arpa hosts are all full domain names. RHS is familiar numbers # Note that no abbreviated names are in this table alta-coma.arpa:3.1.0.50 yuma.arpa:6.1.0.1 bbncca.arpa:8.0.0.2 bbnccb.arpa:8.1.0.2 bbnccc.arpa:8.2.0.2 bbnccd.arpa:8.3.0.2 bbn-labs-admin.arpa:8.4.0.2 bbncck.arpa:8.5.0.2 ..... # Sercnet Table # Note that RHS is mapped to a Transport address by the lower # level (NIFTP) # # As neither DCA nor JNT would like to see names with .AC.UK # in, UK host names are not full domains. abxa:abxa/mail apca:apca/mail baca:baca/mail bdga:bdga/mail bdgb:bdgb/mail bedford:bedford/mail bgxa:bgxa/mail bhga:bhga/mail bhgb:bhgb/mail bhia:bhia/mail bhvs:bhvs/mail bihh1:bihh1/mail bihh2:bihh2/mail bihh3:bihh3/mail # UK users - note domain ordering!! # US users - ignore this entry hcig.nott:nott.hcig/mail nsga:nsga/mail The rest of this sample description shows the structuring of the domain tables. # UCL-CS.AC.UK domain table # Map all known names below the (transparent) UCL-CS.AC.UK # Thus string 44b maps into DOMAIN 44b.UCL-CS.AC.UK # which maps into HOST 44b # which is found in the ring channel table # (or in the case of 44a in the local channel table or 44a-list # in the list channel table) 44a:44a 44b:44b 44c:44c ..... 44a-list:44a-list 44b-list:44b-list 44c-list:44c-list .... # ARPA Domain table # idis is at top of file as it is used as a relay host # A relays entry must come before any hosts realying # through it to get the official name correct usc-isid:usc-isid.arpa # # The following hosts won't talk TCP to UCL # (or used not to at least). Therefore we palm them off # on someone else (boo hiss) ada-vax:ada-vax.arpa,usc-isid.arpa isi-vaxb:ada-vax.arpa,usc-isid.arpa ajpo:ada-vax.arpa,usc-isid.arpa vaxb:ada-vax.arpa,usc-isid.arpa almsa-1:almsa-1.arpa,usc-isid.arpa alta-coma:alta-coma.arpa,usc-isid.arpa ames-tss:ames-tss.arpa,usc-isid.arpa ames-67:ames-tss.arpa,usc-isid.arpa # # Here are all of the names from hosts.txt # Official name first. Host name on RHS alta-coma:alta-coma.arpa yuma:yuma.arpa bbncca:bbncca.arpa bbnccb:bbnccb.arpa bbnccc:bbnccc.arpa bbnccd:bbnccd.arpa bbn-labs-admin:bbn-labs-admin.arpa bbn-labs:bbn-labs-admin.arpa bbnl:bbn-labs-admin.arpa bbncck:bbncck.arpa # Mailnet Domain table # All domains are reached through MIT relay # the official name of mit-multics si found in # the Arpa domain # There is no need for a mailnet channel carnegie:carnegie.mailnet,mit-multics.arpa cornella:cornella.mailnet,mit-multics.arpa dickinson:dickinson.mailnet,mit-multics.arpa durham:durham.mailnet,mit-multics.arpa educom:educom.mailnet,mit-multics.arpa # AC.UK Domain table # Lots of amazing names for UCL at the top ucl-cs:ucl-cs ucl:ucl-cs zuxa:ucl-cs zuxa80:ucl-cs research:ucl-cs nss:ucl-cs unknown-host:ucl-cs zuxc:ucl-cs ucl-niftp:ucl-cs ucl-tg:ucl-cs src-44a:44c # # Some odinary domain names abxa:abxa apca:apca baca:baca bdga:bdga bdgb:bdgb bedford:bedford bgxa:bgxa # These entries will map HCIG.NOTT, HCIG.NOTTINGHAM # NOTT, NOTTINGHAM, HCIG (and all the permutations with .AC or # .AC.UK appended) into HCIG.NOTT.AC.UK hcig.nott:hcig.nott hcig.nottingham:hcig.nott nottingham:hcig.nott nott:hcig.nott hcig:hcig.nott # # Some examples of Relayed connections. # Note that the rightmost component should be the host # directly connected to. The leftmost component should be # The Name required for 'Host' format output dlia:dlia,rlgb rlia:rlia,rlgb rlib:rlib,rlgb camphx:camphx,caga caxa:camphx,caga # Camring is routed first ot caga, then camjenny, finally to camring camring:camring,camjenny.ac.uk,caga camjenny:camjenny,caga camsteve:camsteve,caga ecsvax:ecsvax,edxa rco:rco,edxa edxd:rco,edxa eagle:eagle,ukc.ac.uk,rco.ac.uk,edxa ktda:eagle,ukc.ac.uk,rco.ac.uk,edxa kent:eagle,ukc.ac.uk,rco.ac.uk,edxa # # Hosts connected to over the PSS Network aucc:aucc cardiff:cardiff cardf:cardiff essex:essex # Top level domain # UUCP and Bitnet through berkeley gateway # Note that the HOST name of the relay must be given uucp:ucb-vax.arpa bitnet:ucb-vax.arpa # Mailnet relay through MIT. Domains in the mailnet table can # be abbreviated. Others will only work if specified in full mailnet:mit-multics.arpa lta-coma.arpa:3.1.0.50 yuma.arpa:6.1.0.1 bbncca.arpa:8.0.0.2 bbnccb.arpa:8.1.0.2 bbnccc.arpa:8.2.0.2 bbnccd.arpa:8.3.0.2 bbn-labs-admin.arpa:8.4.0.2 bbncck.arpa:8.5.0.2 ..... # Sercnet Table # Note that RHS is mapped to a Transport address by the lower # level (NIFTP) # # As neither DCA nor JNT would like to see names with .AC.UK # in, UK host names are not full domains. abxa:abxa/mail apca:apca/mail bacmmdf/doc/dbmdatabase 444 0 12 55302 3620510345 7713 .TL The MMDF Database .br (somewhat obsolete) .AU Steve Kille .AI Department of Computer Science University College London .NH Overview .PP This design specifies modifications to the database access procedures of the MMDF mail system. The changes are motivated by the expansion of the names database for the SRI environment. The major changes involve the reorganization of the database for more efficient random access methods that would reduce the processing inefficiencies of the sequential access of large databases. The changes take advantage of the database management dbm(3)[*] .FS [*] The notation dbm(3) refers to the documentation as found in the Unix Programmer's Manual, Vol. 1, e.g., the database documentation is in Section 3 under the title .B dbm. .FE libraries of V7 Unix. .NH 1 Scope of Changes .PP All changes are restricted to two modules of MMDF. One module is part of the .B submit process and the other module is shared by the .B submit, .B deliver, and .B channel processes. There are potential efficiencies that could be gained in other modules due to the use of the new design but these changes are left for a future effort. The include file .B ch.h is compacted by eliminating obsolete fields in the channel structures. .NH 2 Affected Modules .PP The two modules that are affected are .B ch_table.c and .B adr_submit.c. .B ch_table is completely rewritten to use the dbm(3) library. Two procedures are respecified because their functions have been altered. The remaining procedures are reimplemented. The procedure .B loc_alsrch in the module .B adr_submit.c is modified to remove references to and logic for the obsolete procedure .B ch_tseek and to add logic to use the procedure .B ch_tsetnum. .NH 2 Interfaces .PP The interfaces to the public procedures of ch_table.c are preserved. The re-implemented procedures have the same names, accept the same arguments, and return the same results as the procedures they replace. The respecified procedures have different names and arguments to ensure that incompatible calls to the old procedures would be caught and prevented from improperly linking to the new procedures. Some of the re-implemented procedures have a boolean argument that indicates whether the search should start at the beginning of the file or where the pointer is currently located. The argument now indicates whether the partial ordering information stored in the database should be used to detect possible name circuits. .NH 2 Affected Procedures .PP Only one procedure, .B loc_alsrch, in the module .B adr_submit.c is modified. The modification is described above. All other procedures in this section are in the module .B ch_table.c. .SH ch_tblopen .IP This procedure is removed completely and its replacement, .B ch_tdbminit, is made completely internal to the module. It calls the .B dbminit procedure for the first database access to initialize the database. .SH ch_tseek .IP This procedure is removed completely. It is replaced by the procedure .B ch_tsetseq which sets the minimum acceptable point for an entry to be in the partial ordering of the database. .SH ch_ttell .IP This is really a macro defined in the file .B ch.h. It is removed and the procedure .B ch_tgetseq is defined in its place to return the partial ordering of the most recently accessed entry in the database. .SH ch_h2adr .IP This procedure is rewritten internally to use the dbm(3) library. The argument .B first is redefined to be a flag to indicate whether partial ordering of the entries is to be enforced. All other aspects of the procedure remain the same. .SH ch_ad2first .IP This procedure is rewritten internally to use the dbm(3) library. The external interface for this procedure remains the same. .SH ch_h2first .IP This procedure is rewritten internally to use the dbm(3) library. The argument .B frst is redefined to be a flag to indicate whether partial ordering of the entries is to be enforced. All other aspects of the procedure remain the same. .SH ch_h2chan .IP This procedure is rewritten internally to use the dbm(3) library. The external interface for this procedure remains the same. .PP The procedures no longer access each other internally. They instead set up their own calls to the dbm(3) procedure .B fetch and process the returned data themselves. The linkages between data records is defined in the logic of the access procedures. .NH 1 Addressing Database Design .PP The support provided by the dbm(3) library consists of procedures to store and retrieve data items using arbitrary ascii strings as keys. Since no provision is made for accessing the internal representation of keys or data pointers, all linkages are maintained as ascii keys. The interpretation of a data item as a key for further searches of the database is therefore embedded in the MMDF access procedures. The subsections below define these relationships between data items and other keys. .NH 2 Database Internals .PP The sequential database is composed of lists of key and value pairs. Each list is a separate text file that is appropriate only in certain contexts. An address or name is either a mailbox or a host depending on whether it is in the file used for aliases or the file used for channel host addresses. The formats however are identical. .PP A data entry is composed of three components; the keyword that is used for access, a partial ordering number, and a list of one or more values separated by FS ('\\034') characters. See Figure 2 and Section 5.2.1 for the details and format of the entry. .PP A value is composed of a channel code character followed by a printable ascii string. There are three basic types of values used in the data entries. They provide single, multiple, and indirect values for the keys. All of the types are transparent to the database procedures and are returned unmodified to the calling procedures for processing. .IP Single 10 This is the simplest form of database value. It is a single ascii string that is passed back to the address processor. The value can be the real name associated with an alias, a host name associated with a channel, or a host address associated with a host name. .IP Multiple 10 A multiple value is a comma ',' separated list of single values. All of the single values are associated simultaneously with the key. Such a value is used for small distribution lists or for aliasing a name to mailboxes for a user on different machines. .IP Indirect 10 The indirect value is an extension of the multiple value. A value that starts with a left angle bracket '<' character is treated as a filename (not including the '<') where a list of values may be found. Distribution lists are implemented with indirect values. The referred to file is not searched but is read sequentially for values that are then recursively processed against the database. .PP Only one file is used in the dbm(3) library[*]. .FS [*] There are really two files, one data file and one key hash file, but they are considered as one in this context since they are intimately connected in the dbm(3) library. .FE Identical keys that are in different lists in the sequential files are stored under one key in the random access database. The storing of identical keys is described in the next section. .NH 3 Partial Ordering Numbers .PP The ordering of entries in the sequential database is used by the sequential database to detect name circuits. The first two bytes of an entry datum are an unsigned 16 bit ordering number that indicates the partial ordering between any two entries in the database. The numbers are assigned by the database management program. Section 3.2 Name Circuit Prevention describes its use. .NH 3 Channel Encoding .PP The context information implied in the multiple files used for the sequential database must be preserved in the random access database. This context is preserved by prepending a code character to the value. This code is either the official name code or the code character assigned to a channel. The code is used both to check whether the value is valid for a particular channel and to map a name(host) to a channel. .PP Identical keys may be stored in more than one file in the sequential database. This feature is implemented in the random access database by concatenating the entries from the multiple files and separating them by the FS ('\\034') character. The first character of each entry in such a list is the code character for that entry. Note that the first character is a code only for these multiple entries and not for the multiple comma ',' separated values described above. The code character is stripped by the procedures and not returned. The sequential order of the entries implies the partial ordering imposed in the sequential database. .NH 3 Official Names .PP An official name is the primary identifier for a host. Other names are allowed but the official name is the only name that is universally known by all other hosts. Alternate or alias names are available but MMDF converts these names to the official name before the address is used in the system. .PP By convention, the key associated with the first occurrence of a value (address) in the sequential file is the official name. It may be found from an address by searching for the first occurrence of the address in the value field and returning the key as the official name. The official name is found from an alternate by first finding the alternate and then by taking its value (address) and searching for the its first occurrence. .PP There are two entries stored in the database for an official name. One is a normal name-key with address-entry and the other is an address-key and name-entry. The name-entry has a code character of '='. An official name is recognized when the database is being built and the inverse of the entry is generated by exchanging key and entry and storing the result. The official name is also stored as the first entry of all other (alias) entries that have the same address as the official name. The official name is retrieved from a value (address) by retrieving the entry under the address key. It is retrieved from an alternate name by retrieving the entry with the alternate name keyword and then selecting the official name entry. .NH 3 Name List Redirection .PP Name list redirection is not part of the sequential search database procedures. The procedure .B alst_proc parses the value returned from reading the current address. The address line may either come from an input line or a database value due to the recursive logic of the address parsing procedures. The redirection is done on all addresses that start with the '<' character. This character is transparent to the database system and is considered part of the returned value. The partial ordering numbers are used by the database procedures to detect and reject name circuits generated when keywords in the indirect files do not map to out of order entries in the database. .NH 3 Reserved Code Characters .PP The only reserved code character is '='. This character may not be used for channel codes because it is used to identify an official name. It should be considered to be the official name channel code character. The FS ('\\034') character is also reserved for use as the entry separator. All other characters are acceptable. .NH 2 Name Circuit Prevention .PP A name circuit exists when a name key is self referencing though an arbitrary number of name translations. Such a name would put the address checking procedures in an infinite loop. The sequential database defeats some cases of self reference by forcing the search of any one list at any one level to be one pass. This algorithm forces a partial ordering of all addresses; but it is only enforced for one file at a time. .PP The random access database contains an ordering number for each entry in the database that identifies the order in which the entries were stored in the database. This partial ordering of the entries prevents name circuits by forcing all translations to be forward references that would eventually terminate with the end of the list. The check for partial ordering is under the control of the argument to the the applicable procedures that controlled whether the sequential search started from the beginning of the file. The partial ordering is not enforced if the argument is TRUE to be compatible with the old procedures. .NH 1 Database Access Procedures .PP This section describes the algorithms used to implement the database with the dbm(3) package. These procedures replace those procedures of the same name in the file .B ch_table.c .NH 2 Access Initialization .PP The procedure .B ch_tblopen is removed and it is replaced by the procedure .B ch_tdbminit which is callable only by the procedures within the module. There is no argument list. An internal static flag is maintained by the procedure to record if the database file is initialized. During the course of this procedure it will call the dbm(3) procedure .B dbminit and set the flag so further calls to the procedure will not attempt to initialize the database again. .NH 2 Partial Ordering Control .PP The old procedure .B ch_tseek and the macro .B ch_ttell are deleted from the file along with all references to them. They are replaced by the procedures .B ch_tsetseq and .B ch_tgetseq which control the partial ordering in the database. .B ch_tgetseq returns the sequence number of the most recently accessed entry and .B ch_tsetseq changes the internal sequence number value to its argument. A sequence number is a value of type unsigned short. .NH 2 Database Record Access .PP All records in the database are accessed by the internal procedure .B ch_fetch. It correctly forms its string argument into a key for the dbm(3) procedure .B fetch and decomposes the returned datum into the partial ordering number and a vector of char pointers to the individual values. The number and the vector are stored in the data structure passed to the procedure. Success return is indicated by returning the pointer to the data structure and failure is indicated by a NULL pointer. .NH 2 Key to Value Access .PP This function is done by the procedure .B ch_h2adr. The 'name' parameter is passed to the internal procedure .B ch_fetch. The returned structure may have multiple entries because the same key may be used for more than one channel or alias list. The code character of each entry is checked against the code character stored in the channel structure and the entry that matches is returned in the pointer parameter 'buf' with its code character stripped off. TRUE is returned if a match was found and FALSE is returned if .B ch_fetch can not find the key or if the code character for the channel does not match an entry in the datum. .NH 2 Value to Primary Key Access .PP This function is performed by the procedure .B ch_ad2first. The parameter 'adrstr' is passed to the procedure .B ch_fetch as described above and the returned structure is scanned for an entry whose code character matches the official name code character '='. The result is returned via the pointer parameter 'buf'. TRUE is returned if a match was found and FALSE is returned if .B ch_fetch can not find the key or if there is no official name. .NH 2 Key to Primary Key Access .PP This function is performed by the procedure .B ch_h2first. The parameter 'str' is passed to the procedure .B ch_fetch and the returned structure is searched for an official name entry. The official name is returned via the pointer parameter 'buf'. TRUE is returned if a match was found and FALSE is returned if .B ch_fetch can not find the key or if there is no official name entry. The parameter 'str' is returned if no official name is found. .NH 2 Key to Any Channel Access .PP This function is performed by the procedure .B ch_h2chan. The parameter 'hostr' is passed to the procedure .B ch_fetch. The pointer parameter 'adrstr' is used to return either the official name if it is found or the parameter 'adrstr' if none is found. The procedure returns NOTOK if there is no official name. Each value except the official name in the entry is used in turn to find the first channel available for the key. The code character of the value is passed to the procedure .B ch_c2struct which will either return a channel pointer or the value NOTOK. If there is no channel to match any of the code characters then .B ch_h2chan also returns NOTOK. If the channel found by .B ch_c2struct is the local channel and local delivery is ok then the procedure returns OK. Otherwise, it returns the channel pointer. .NH 1 Database Management Program .PP The program .B dbmbuild generates the database from ascii files of the same format as the sequential database. It does not attempt to do updates to an existing database; rather, it regenerates the entire database each time and the resultant files are moved into place with other commands. .NH 2 Inputs .PP The input to the program is in the current sequential file format. The code character is passed to the program as a command line argument. A command line argument selects official name processing; the default action is no processing. .KS .DS line ::= text eol text ::= comment | entry comment ::= '#' <any printable ascii string> entry ::= key sep value key ::= <lower case printable ascii string> sep ::= ':' | <space> | <tab> value ::= <printable ascii string with optional spaces or tabs> eol ::= '\e012' .ce Fig . 1, Input Line format .DE .KE .NH 2 Outputs .PP The output file is generated into two files. The file\fI.dir\fP file contains a bitmap directory of the actual keys and values located in the file\fI.pag\fP file. The file is generated by the .B store procedure of the dbm(3) library. See the Unix Programmer's Manual, Vol 1, dbm(3x), for further details on the database library. .NH 3 Record Format .PP A record consists of a key and a datum. The key is an arbitrary ascii text string but for matching purposes it is all lower case with all extraneous white space compressed out. The datum is an arbitrary variable length byte string. The length is controlled by a count field. The subfields of the datum are defined below. There is no white space separating any of the fields. They are either defined by position and count or by the field separator (FS) character. .KS .DS datum ::= serial-number entry-list serial-number ::= <two bytes used as unsigned short> entry-list ::= entry | entry-list FS entry entry ::= code-char value code-char ::= <printable ascii char matching channel char> value ::= <arbitrary compressed printable ascii string> FS ::= '\e034' .ce Fig. 2, Internal Record Format .DE .KE .NH 2 Algorithms .PP These algorithms convert a sequential database into the random access database suitable for the online MMDF. .NH 3 Partial Ordering Control .PP At the start of processing an input file the entry under the key "Local-Name" is retrieved and the serial number field is retained for further processing. If the entry is not found then the serial number is initialized to 1 and the key "Local-Name" is stored. The name is stored again at the end of processing with a datum containing the updated serial number. Any values stored under the key are preserved so that the key may be used to store other information in addition to the serial number. In future, this entry can be used to store the host's local name instead of the separate file currently used. .NH 3 Channel Encoding .PP Channel code characters are passed to the program with the sequential data file. The code characters are retained by the program and prepended to values as they are stored in the database. There is currently one code character assigned for each channel in the system as well as the '=' character which identifies an official name. .NH 3 Identical Keys .PP Identical keys are allowed in the sequential database. They are implemented in the random database by concatenating the entries according to the record format in Fig 2. The key part of an input line is retrieved before it is stored into the database. The retrieved datum is used instead of an initialized empty datum whenever the retrieval returns a valid datum. The new entry is appended to the end along with its code character and separated from the rest of the retrieved entry by the FS character. .NH 3 Official Name Processing .PP An official name in the sequential database is the first name associated with a particular address. Official names are detected and marked in the random access database in a similar manner. Official names are entered into the database under the control of a command line argument; the default action is to not process official names. .PP As the sequential input file is read for entries to store in the database, the value, i.e., address, part is \fIfetch\fPed to see if it is present. If the .B fetch returns a valid pointer to an official name then it will be used to build the datum for the current input line. If the pointer is valid but the value is not an official name then the official name is appended using the format from Fig 2 and datum is stored back into the database. A NULL pointer indicates that the current entry is an official name. The value from the address part (from above) is used as a key and the key part of the current input line is used as the value for the new entry. The entry has a '=' code character and the value is 'canonicalized', i.e., the first character and the first character after a '-' are capitalized. This official name entry is always stored as the first value in the datum for all names, including itself, that refer to it. The string is stored in every datum already canonicalized in order to save processing time for MMDF. .NH 3 Name Circuit Detection .PP A name circuit is exists if there is a valid key to the database that is equal to the value in the current input line that is not the inverse key of an official name. This can occur only if there was a previous entry in the database that had a key equal to the value of the current entry. The input line is stored as an additional value in the entry. (See Section 5.3.3). There is a change in the way name circuits are rejected by the database because the partial ordering for the entry remains the same. Any identical keys which would have been valid in the sequential database because they were positionally correct are no longer valid because they are, in effect, moved up to be just below (actually part of) the first, out of order key. This restriction prohibits certain obscure or clever alias list manipulations. .PP Forward references, i.e., the value field of a previous entry is equal to the key of the current entry, are allowed in the sequential database since the linkages will terminate at the end of file. These are allowed in the random access database too because the database procedures will check sequence numbers when requested by the calling procedure and reject all fetches that are not forward references thereby preventing the name circuit. .NH 2 Error Processing .PP All system and database inconsistency errors are reported to the operator on the standard error output. All improperly formed entries are rejected. again at the end of processing with a datum containing the updated serial number. Any values stored under the key are preserved so that the key may be used to store other information in addition to the serial number. In future, this entry can be used to store the host's local name instead of the separate file currentmmdf/doc/addressing 444 0 12 106316 3631167646 7647 .nr Fn 0 +1 .de FN .ps -2 \u\\n+(Fn\d .ps +2 .FS \\n(Fn \n(Fn. .. .TL Addressing in MMDF II .AU Steve Kille .sp <Steve@CS.UCL.AC.UK> .AI Department of Computer Science University College London .AB The Multi-channel Memorandum Distribution Facility (MMDF) is a powerful and flexible Message Handling System for the .UX operating system. It is a message transport system designed to handle large numbers of messages in a robust and efficient manner. MMDF's modular design allows flexible choice of User Interfaces, and direct connection to several different message transfer protocols. .PP This paper is intended as a sequel to the paper presented by Doug Kingston at the 1984 Usenix meeting in Salt Lake City \*([.Kingston84\*(.]. A brief technical overview of MMDF is given, and then two aspects are considered in more detail. First, the table-driven approach taken by MMDF to handling a structured address space is described, and then the extension of this approach to use with distributed nameservers is considered. Several distributed message systems using similar, but distinct, message and address formats have emerged. The message reformatting approach taken by MMDF to allow interworking is described. Finally, a comparison with other systems is made. .AE .SH Introduction .PP The Multi-channel Memorandum Distribution Facility (MMDF) is a powerful and flexible Message Handling System for the UNIX operating system \*([.Crocker79,\|Kingston84\*(.]. It was originally developed for use on Csnet \*([.Comer83\*(.], to provide message relaying services. A version of MMDF stabilised in 1982 is the production system used by most Csnet sites. MMDF\ II, which is described here, has many changes relative to the earlier system. MMDF\ II is on beta test at several sites both in Europe and the US, and is expected to be fully available before this paper is presented. Although it is not currently a production system, it has been used to provide message relaying services for about two years at the Ballistic Research Laboratories, Maryland and at University College London, and more recently on the Csnet hub machine. Each of these sites has a variety of particularly demanding requirements, and are regarded as good testbeds. .PP The main requirements and features of MMDF\ II are as follows: .IP (1) Robust, reliable, and efficient message relaying. Particular emphasis is placed on reducing message loss to an absolute minimum. A two phase warning is used to handle message transfer delays. .IP (2) Support for multiple Message Transfer Protocols, and general interworking between them. Currently supported are: Phonenet \*([.Crocker79\*(.], the Arpanet Simple Mail Transfer Protocol (SMTP) \*([.Postel82\*(.], the UK JNT Mail Protocol (Grey Book) \*([.Kille84\*(.], UUCP Mail \*([.Nowitz78\*(.], and indirectly CCITT X.400 protocols by use of the EAN system developed at University of British Columbia \*([.CCITT83,\|Neufeld83\*(.]. .IP (3) Support for a wide range of User Interfaces. At present, v6mail, Shoens Mail, msg/send \*([.Vittal81\*(.], and MH \*([.Borden79\*(.], can all be used with MMDF\ II. .IP (4) Authentication \- that is submission time verification that a message conforms to RFC 822 (the standard on which the generic message format for all MMDF\ II messages is based) \*([.Crocker82\*(.], and that the source is correctly specified. .IP (5) Authorisation \- that is the restriction of message flow for policy reasons, or assigning responsibility for a given message transfer (possibly for billing purposes) on the basis of the networks, users, and hosts involved. This is discussed more fully in \*([.Brink85\*(.]. .IP (6) Submission time address checking. This allows for maximum address checking within the User Interface, and for more efficient network use by protocols such as SMTP and Phonenet. .IP (7) Flexible handling of local addresses, including aliasing, delivery to pipes and files, and enhanced support for distribution lists by use of a list channel. This is described in \*([.Kingston84\*(.]. .IP (8) User configurable delivery time options. This allows messages to be sorted according to destination and message header, and then processed accordingly. Processing can include: filing; redistribution; standard delivery; delivery to a pipe; user notification; destruction. This is described in more detail in \*([.Kingston84\*(.]. .IP (9) Straightforward runtime configuration (by use of a .B "tailor file" ). .IP (10) Flexible handling of remote addresses. .IP (11) Message reformatting to support several environments using protocols similar to, but different from, RFC 822. .PP These last two points are the main subject of this paper. .SH MMDF System Structure .PP To describe the address handling, it is first necessary to understand the basic system structure. .DS B ----------- ----------- | User | |Protocol | |Interface| | Server | ----------- ----------- | / ___________________ | / | \\\\\\\\ | / | ----------- | | Submit | | | | | ----------- . . . . . . . .-> | . Message | . | . Queues | ----------- <- . . . . . . . . | | Deliver | | | | | ----------- | / | \\\\\\\\ | / | \\\\\\\\ | / | \\\\\\\\ | ----------- ----------- ----------- | | List | | Local | |Protocol | | | Channel | | Channel | | Channel | | ----------- ----------- ----------- |______| MMDF Process Structure .DE .PP The key to the operation of MMDF\ II is the process .B submit . Submit accepts messages in one of two modes: .IP (i) \'Message' mode, where a message is accepted on the standard input and addresses extracted from specified message header fields. .IP (ii) \'Protocol' mode, where addresses are first checked in an SMTP like negotiation \*([.Postel82\*(.], and then the message is accepted. This mode is usually invoked by another process interacting with submit by use of a pair of pipes. .FN See .B "pipe(2)" in any UNIX reference manual. .FE .PP Submit is invoked both by User Interface processes to send local messages, and by Message Transfer Protocol servers (e.g. an SMTP server, rmail, .FN rmail is the process invoked by UUCP to transfer mail. .FE or a JNT Mail Protocol server). Submit verifies the addresses, and then checks conformance to authorisation policies. It also authenticates the source and format of the message. The most important part of this authentication is that locally generated messages have a correct originator specification (i.e. that of the invoker of submit), and that messages originated remotely are only submitted by a process with the appropriate privileges. Finally, the message is queued, with the text of the message (both body and header) in one file, and the associated addresses and control information in another file. Each address is associated with a .B channel , and each channel has an associated queue, managed independently as a directory containing files which are links to the appropriate address file. The address to channel binding is discussed later. .PP Each channel has a process for delivering messages associated with it. More than one channel may use the same channel process (and thus protocol), as channels may have: different (sets of) hosts associated with them; different routes to the same hosts; or a different quality of service to the same hosts. .FN This flexible mapping becomes particularly important when applying authorisation on the basis of multiple criteria. A fuller discussion is given in \*([.Brink85\*(.]. .FE There are two special channels: .IP (1) Local channel. This delivers messages to local mailboxes, including any user specified processing. .IP (2) List channel. This allows a list to be expanded in two phases, by passing the message back to submit with a modified return address. This approach gives several advantages, particularly when handling large lists. .PP Both of these channels are discussed in more detail in \*([.Kingston84\*(.]. .B Deliver is a process which reads the queue associated with one or more channels, and invokes a channel process with which it interacts through a pair of pipes. Deliver will read addresses from the queue, and pass them in turn to the channel process. When an address is fully processed (which may not be immediately if multiple addresses are being sent to a given host before any text is transferred) this information is passed back to deliver. Deliver then sends any necessary error messages, and the queue is adjusted accordingly. Caching and time control parameters allow for a variety of backoff strategies to handle hosts which do not respond. Deliver may also be invoked in pickup mode to allow messages to be pulled as well as pushed, which is particularly important for dialup links. This is discussed in \*([.Crocker79\*(.]. .SH Address handling .PP The mechanism for verifying addresses is now considered. First, some terminology is defined: .IP Address Address is used to mean the text that the user supplies to a typical message sending interface in order to direct a message to a desired destination. This loose usage has been selected, because it is familiar to most message system users. .IP Domain .br The DARPA domain scheme \*([.Mockapetris84\*(.], and the UK Name Registration Scheme (NRS) \*([.Larmouth83\*(.], allow an untyped global namespace to be allocated in an hierarchical fashion. Examples of domains are: UK, Salford.AC.UK, CompSci.Salford.AC.UK, USC-ISID.ARPA, ARPA, UUCP. If a domain is sufficiently specified .FN To identify a specific service in the context of domain UK is unlikely to make sense, whereas it might for Salford.AC.UK. .FE it is possible to identify a direct connection to a Host, which may be associated with the domain or be a relay to the domain. .IP Mailbox A mailbox (User Agent) is the source or destination of a message, often associated with a human user. A mailbox can be globally specified in RFC 822 as local-part@domain. .IP Host A host is a computer connected to directly by the local computer. A domain associated with a host not directly connected to by the local computer should be regarded only as a domain: the domain to host binding is immaterial if there is no direct connection. .IP Route .br Routes are considered to be implicitly at the message level. A Route is an explicit sequence of domains over which a message should traverse. Two types of route are considered: .IP (i) 8 A Formal Route consists of a sequence of globally valid domains, recognised as a source route by the originating system. In RFC 822, this is specified by a syntax of the style <@domain1:local-part@domain2>. A Formal Route is used to reach domains which cannot be accessed directly, or where indirect access is preferred. .IP (ii) 8 An Informal Route is one that is not recognised as a route by the sending system, but is interpreted as such by a receiving system. In the RFC 822 world, a common Informal Route syntax, making use of a special form of local-part is: user%domain2@domain1. Interestingly, this syntax is a Formal Route in the JNT Mail world. An Informal Route is used when different domains have a different interpretation of the global namespace (e.g. user%domain2@domain1 is used where the message originator's local system does not interpret domain2 as intended, but domain1 does). .PP A number of domain specifications have been adopted informally by different groups (e.g. .ARPA .AC.UK .MAILNET .UUCP .CSNET .CDN .EDU), and it seems likely that many networks will be able to agree on a global namespace. All Message Routing in MMDF\ II is currently controlled by tables. This is likely to continue for hosts where all external communication is by use of dialup links, and the NRS (at least) will distribute information as tables.. The approach taken by MMDF\ II to table based domain handling is now described. .PP The MMDF\ II process .B submit performs the address checking function. There are two orthogonal operations. The first is to parse the address, to extract a Formal Route. The second phase is to interpret the 'end' domain of the route. If the domain being verified is identified as the local domain, the next domain component is considered. If the local-part is reached, it is then interpreted as an Informal Route. MMDF\ II supports a number of Informal Route specifications, including the UUCP "!" syntax. If the local-part under consideration is not an Informal Route, it is looked up as an alias. If it is identified as an alias, the alias value is then parsed as a sequence of addresses, and the whole procedure recurses. Finally, if it is not an alias, it will be checked as a local account and, if valid, queued for delivery by the local channel. .PP Each channel is associated with a set of hosts, which are directly connected to the local system. .FN The directness is conceptual rather than physical. This is illustrated later. .FE A host may be associated with more than one channel. Each channel has a channel table .FN Tables are considered here as if they were physical files. In practice, they are implemented by hashed database lookup. Many of the operations described are optimised further by taking advantage of the structure of this database. .FE which maps its associated hosts onto the necessary connection information (e.g. transport addresses). The host namespace is arbitrary, but given that all hostnames must be unique (to allow for host to channel binding), the convention that the hostname is that of the associated domain is convenient. A reason for sometimes violating this convention is discussed later. .PP Domains are handled in a two level manner. The top part of the domain is specified in the tailor file, and indicates an associated table. This top part is referred to as the .B "domain table specification" The rest of the domain is specified as the LHS of the table, with the RHS of the table specifying the host associated with the domain. The split can occur in more than one way. For example, CS.SALFORD.AC.UK could be split as: .DS B domain table specification table entry SALFORD.AC.UK CS AC.UK SALFORD.CS UK CS.SALFORD.AC "" CS.SALFORD.AC.UK .DE The last case has a null domain table specification, known informally as the 'top' table. .PP The method of domain interpretation is now considered. When a potential domain is examined by submit, it is first assumed to be a full domain specification. .FN The procedure is substantially optimised for domains in this form, as this is the most common case. .FE Components are repeatedly stripped from the LHS and matched against the list of domain table specifications. Thus, if TREES.BIO.STANFORD.EDU is considered, domain table specifications are searched for in the order: BIO.STANFORD.EDU, STANFORD.EDU, EDU, "". For each match, the remainder is looked up in the associated domain table. If a domain is identified, it is then mapped into its official value by looking for the first component in the table with the same RHS. This allows for domain aliases. The RHS (host name) is then looked up in the channel tables, and the address is bound to the first channel satisfying management requirements. Some extensions to this basic procedure are now described. .IP "Source Routes" 12 An alternative specification for the RHS of a domain table is as a series of components; the first being the host directly connected to, and the rest being a sequence of domains traversed in order. These domains will be added to the source route passed to the channel (message transport protocol). This source route information can be added in an ad hoc manner, or can be systematically obtained (e.g. from the NRS). .IP Subdomains 12 If CS.SALFORD.AC.UK is specified, and has no corresponding domain table entries, but there is one for SALFORD.AC.UK, CS.SALFORD.AC.UK is accessed as a source route through SALFORD.AC.UK. This approach means that not all leaves of the tree need be stored. In particular, the 'top' table (i.e. the one with null domain table specification) may have entries such as: .DS CSNET:CSNET-RELAY.ARPA CA.UUCP:UCB-VAX.ARPA .DE This would route all subdomains of CSNET (e.g. UPENN.CSNET) through CSNET-RELAY.ARPA, and all subdomains of CA.UUCP through UCB-VAX.ARPA. This is useful in (the currently common) cases where the logical domain structure has a physical mapping. .IP "Partially specified domains" 12 When all these checks have been made on the assumption of fully specified domains, these checks are repeated in each domain table on the assumption that the domain table specification was omitted. For example, if there is a table ARPA, and BRL is being tested, BRL will be looked up in table ARPA (and matched) on the assumption that BRL.ARPA was intended. .IP "NRS domain ordering" 12 It has been assumed until now that the UK NRS (Name Registration Scheme) and DARPA Domain scheme specify domains in the same manner. An unfortunate difference is, that although the semantics are similar and syntax the same, the ordering of the hierarchy is reversed. Thus, SALFORD.AC.UK in the DARPA form is UK.AC.SALFORD in NRS form. This difference has caused much confusion to (UK) users communicating with systems using both of these schemes, and so it is not very satisfactory to assume consistent specification (within the UK). Therefore, MMDF\ II may be configured to check for domains in either order. It does this in an interleaved fashion (i.e. for each check done performing it first in one order, and then the other). This minimises the weight given to the order a domain happens to be specified in, and maximises weight on the degree of specification (e.g. preferring to interpret "UK.AC.AVON.MULTICS" as MULTICS.AVON.AC.UK rather than UK.AC.AVON.MIT-MULTICS.ARPA). In practice, the few problems which have occurred, have been caused by palindromic partial domain specifications. Except where explicitly noted otherwise, this paper assumes DARPA ordering, as NRS ordering is mostly confined to the UK Academic Community. .IP "Routing by the channel" 12 There are currently two cases of routing by channel. In the first case, which can be used for any channel, the channel connects only to one host acting as relay for all the hosts in the channel table. The second is the UUCP channel. Here, hosts are considered to be any UUCP host reachable through UUCP, and the RHS of the channel table is the UUCP route (expressed in the form host!host!host!%s). Use of this style of channel routing should be minimised, as it is preferable both to route incrementally and to bind addresses to channels as late as possible. .IP "Support for multiple machines" 12 It is common for UNIX sites to have many machines, and to have an allocation of users between machines with no clear meaning external to the site. If the machine name must be specified to send mail to a user, this leads to names which are hard to memorise/guess, and makes the movement of mailboxes between machines visible external to the site. MMDF\ II allows for multiple machines to share a common namespace, common binaries and common tables, and for each user to have a default machine for mail delivery. Further, many machines can appear externally to be the same domain. This is achieved by treating each machine as a (different) subdomain of the visible domain (e.g. a message apparently from Steve@CS.UCL.AC.UK might be originated on machine VAX2.CS.UCL.AC.UK). This approach is clearly more user-friendly. In some circumstances it will be more efficient, and in others, less. .PP It is interesting to note how the MMDF\ II addressing approach fits into the transition of various networks to a domain based addressing scheme. There is currently a draft proposal for the UUCP transition \*([.Horton85\*(.]. The author's interpretation of this suggests, assuming that this proposal is followed, that MMDF\ II will provide all of the needed functionality for users to get the full advantage of a domain based scheme. In the UK, the information supplied by the NRS can be used in a straightforward fashion. .PP Perhaps the most interesting case is the DARPA Domain Nameserver scheme \*([.Mockapetris84\*(.]. In general, domain information will not be available in table form, but is obtained dynamically by querying a sequence of nameservers. MMDF\ II will be extended to support this within the timescale of the DARPA transition. A brief description of a .I possible approach is described, the aim being to illustrate that the DARPA scheme can be integrated, rather than to describe how it will be integrated. In general, a final solution will have a mixture of table and nameserver determined bindings. It seems likely that the domain namespaces controlled by tables and nameservers will overlap substantially in many cases. A channel will be either table driven (as at present), or nameserver driven. A nameserver channel will be given a domain, which will be mapped into a connection (and possibly a route) when the domain in question is processed. At message submission time, the domain namespace would first be checked for fully specified domains contained in domain tables, which would be dealt with as before. .FN Note that a domain determined from a table could be bound to a nameserver channel. In general, this would be undesirable. .FE A table (most likely the 'top' table) could specify a domain as being associated with one or more (nameserver) channels (e.g. ARPA maps to the SMTP channel). This domain would then be bound to one of those channels on the basis of management considerations. This gives submission time checking of only the higher level domains, with the full nameserver checking occurring in background when the channel is invoked. Alternatively, the domain could simply indicate use of a nameserver. In this case, the domain being checked would be passed to the nameserver. .FN Detailed interaction with a DARPA nameserver will be provided by a resolver interface, which is likely to be implemented as a library of functions on UNIX. This is certainly the case for two implementations currently in progress \*([.Karp84,\|Terry84\*(.]. .FE If matched, any specification of a 'Mail Forwarder' could be used to specify a source route. The 'class' of address returned would be looked up (in a table) to determine a set of channels. This fuller submission time checking is desirable, but would only be acceptable if the mean nameserver response time was not too large. Then, table checks for partial domains would be made. Assuming that completion services are provided by a local nameserver, the potential partial domain could be checked by the nameserver, if desired. This scheme would extend naturally if there was a need to use either different types of nameserver, or disjoint systems of the same type of nameserver. This is however, only one of several potential approaches. .SH Message Reformatting .PP The need to reformat messages is an unfortunate reality. It is caused by the requirement of interworking between functionally similar, but differently encoded, end to end message protocols. MMDF\ II provides two basic types of message reformatting: the first, applicable to all message transfer protocols; and the second protocol specific. MMDF\ II messages are expected (by submit) to be in a generic format, and are queued directly. This format is flexible, so that User Interface Processes do not need to have overly stringent requirements in order to generate legal format messages. In particular, the following aspects are flexible: .IP \- Formal Routes may be specified either in RFC 822 format or as \'local-part@domain@domain'. .IP \- Domains do not have to be .B normalised , that is to say they may be partially specified or fully specified aliases (e.g. ISID, USC-ISID, ISID.ARPA, USC-ISID.ARPA are all acceptable). .IP \- Local domain specifications may be omitted. .PP In a system configured for the UK Academic Community, the following are also flexible: .IP \- Formal routes may also be specified in 'local-part%domain@domain' form, as used by the JNT Mail Protocol. .IP \- Domains may be specified in either NRS or DARPA order. .PP The general reformatting provided by MMDF\ II may be selected optionally on a per channel basis. This reformatting should be regarded conceptually, as being provided by deliver. It is really performed within the standard routines for interaction with deliver, called by each channel. If reformatting is selected, before each message is processed, a reformatted header will be generated into a temporary file. This is then used (transparently) by the channel instead of the real message header. The reformatting functions provided are now described. It is interesting that they all relate to address format, which in practice is the only interesting message transfer protocol independent form of reformatting. If reformatting is selected for a given channel, an alternative for each of the functions must be specified. .IP (1) Domain normalisation (the conversion of all domain specifications to fully qualified preferred forms) is a basic function of message reformatting. An alternative form of normalisation makes use of the host namespace maintained by MMDF\ II and, in cases where a domain has an associated host, normalises to the host name. This is useful when connected to two worlds with differing views of the global namespace, or in transition situations. A common host specification is the leftmost component of the domain (e.g. UPENN is the host associated with UPENN.CSNET). .IP (2) Formal Route specifications may be reformatted to either RFC 822 form (@domain1:local-part@domain2) or to percent form (user%domain2@domain1). This is useful for mapping between JNT Mail formal source routes and RFC 822 formal source routes. It is also useful where domains are used internally, and are not known globally. This mechanism allows formal routes to be mapped into informal routes, thus making the domain interpretation private. .IP (3) Domain/Host specifications may be output in either DARPA or NRS ordering. This is protocol independent, as the NRS ordering is associated with the UK Academic Community and not with any specific message transfer protocol. .IP (4) The local domain may be mapped into another specified form. This is used when one system has a different name in different environments. .IP (5) A treatment known as 'exorcising' against a table of domains. It is assumed that only the domains specified are known by domains accessed through the channel, and addresses relative to all other domains will be mapped to an informal source route behind the local system. .PP Message reformatting is also applied on a protocol specific basis. This occurs in two places. .IP "Channel Reformatting" 12 Channels may perform reformatting in addition to that provided as standard. The JNT Mail channel performs some simple reformatting, to ensure that the return path, calculated according to the protocol, is correct. MMDF\ II currently supports two UUCP channels. The first assumes that the remote site is 'modern' and does no reformatting on the basis that the remote site expects a standard RFC 822 message. The second UUCP channel assumes that the remote site is 'old fashioned', and maps all addresses into UUCP route syntax (e.g. user@CS.UCL.AC.UK is changed to ucl-cs!user). Both channels may be used simultaneously by one system. .IP "Server Reformatting" 12 Where an incoming message does not conform to the generic MMDF\ II format, the protocol server must perform reformatting. In practice, this is only done for UUCP and EAN X.400. The protocol server for UUCP (the rmail process), will map incoming addresses, which (by their syntax) appear to be from 'old fashioned' systems into RFC 822 format addresses. Where possible, UUCP routes are shortened. This reformatting will not change the message format if the remote UUCP site is 'modern'. .SH Comparison with other Systems .PP The only system considered in comparison is Sendmail \*([.Allman83\*(.]. A comparison with various non-UNIX systems would be interesting, but not appropriate here. No other UNIX systems with comparable functionality are known to the author. Only addressing and message reformatting aspects are considered here. .PP Sendmail's address parsing and message reformatting is controlled by a .B "configuration file" . Sendmail .B mailers are analogous to MMDF\ II channels, but are less tightly coupled. Sendmail's address parsing and domain interpretation are controlled by the configuration file, and are not specifically decoupled. Whilst allowing for an amazingly general syntax, it can lead to cases where domain interpretation is syntax dependent. .FN Careful design of Sendmail configuration files should minimise this dependency, although generation of configuration files to handle complex cases is not straightforward. .FE The decoupling of address parsing and domain interpretation, as provided by MMDF\ II, is seen as both correct and desirable. .PP Current domain handling by Sendmail uses explicit recognition of domains listed in the configuration file to bind to a given mailer. After this partial address checking, the message is then queued for a specific mailer, where further checking is done. If configuration files are kept to a reasonable size, this approach must rely on the fact that the higher level domains can be used to determine the message transfer protocol in most cases. The MMDF\ II approach of maximising address checking at submission time is seen as desirable in view of the trend towards logical domain specifications which do not imply specific message transfer protocols. A domain handling package is expected to be added to Sendmail in the near future, and this may well allow for a more flexible approach. .PP Sendmail reformatting, is highly general and flexible, and has been used to deal with a variety of unusual formats. Some difficulty has been encountered when trying to handle NRS domain ordering, but this is probably not insuperable. MMDF\ II's more basic facilities cover a wide range of commonly used formats and, where appropriate, are more straightforward to configure. It seems likely that more systems will be moving to standard formats. The simpler approach also tends towards greater robustness, and protects against protocol violations. .SH Afterword .PP MMDF\ II is expected to be available as a production system by the time this paper is presented. It is available in the US through University of Delaware, and in the UK through University College London. There is a (currently nominal) licence fee for commercial sites, and a tape handling charge. .sp .]< .\"Allman.E.-1983-2 .ds [F Allman83 .]- .ds [T SENDMAIL - An Internetwork Mail Router .ds [A E. Allman .ds [R Paper .ds [D 1983 .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Borden.B.S.-1979-3 .ds [F Borden79 .]- .ds [A B.S. Borden .as [A " and others .ds [T The MH Message Handling System .ds [D November 1979 .ds [J Project Airforce document, obtainable from RAND, Santa Monica, Ca 90406 .ds [K MH .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Brink.D.-1985-4 .ds [F Brink85 .]- .ds [A D. Brink .as [A " and S.E. Kille .ds [T Authorisation and Accounting in Store and Forward Messaging systems .ds [D June 1985 .ds [J Submitted to Networks 85, Wembley .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"1983-1 .ds [F CCITT83 .]- .ds [Q CCITT SG 5/VII .ds [T Recommendations X.400 .ds [D November 1983 .ds [R Message Handling Systems: System Model - Service Elements .ds [K x400 .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Comer.D.A.-1983-16 .ds [F Comer83 .]- .ds [A D.A. Comer .ds [T A Computer Science Research Network: CSNET .ds [J Communications of the ACM .ds [V 26 .ds [N 10 .ds [P 747-753 .nr [P 1 .ds [D October 1983 .ds [K csnet overview .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Crocker.D.-1979-5 .ds [F Crocker79 .]- .ds [A D. Crocker .as [A ", E. Szwkowski .as [A ", and D. Farber .ds [T An Internetwork Memo Distribution Capability - MMDF .ds [D November 1979 .ds [J Proc. 6th. Data Comm Symp, IEEE/ACM .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Crocker.D.H.-1982-6 .ds [F Crocker82 .]- .ds [A D.H. Crocker .ds [T Standard of the Format of ARPA Internet Text Messages .ds [D August 1982 .ds [R RFC 822 .ds [K RFC822 .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Horton.M.R.-1985-7 .ds [F Horton85 .]- .ds [T UUCP Mail Transmission Format Standard .ds [A M.R. Horton .ds [R Draft received as message .ds [D January 1985 .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Karp.P.D.-1984-18 .ds [F Karp84 .]- .ds [T DRUID: A Distributed Name Server .ds [A P.D. Karp .ds [R Stanford Internal Report .ds [D June 1984 .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Kille.S.E.-1984-8 .ds [F Kille84 .]- .ds [A S.E. Kille, (editor) .ds [T JNT Mail Protocol (revision 1.0) .ds [D March 1984 .ds [I Joint Network Team .ds [C Rutherford Appleton Laboratory .ds [K greybook .ds [K jntmail .nr [T 0 .nr [A 0 .nr [O 0 .][ 2 book .\"Kingston.D.P.-1984-9 .ds [F Kingston84 .]- .ds [A D.P. Kingston .ds [T MMDFII: A Technical Review .ds [J Usenix Conference .ds [C Salt Lake City .ds [D August 1984 .ds [K MMDF .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Larmouth.J.-1983-10 .ds [F Larmouth83 .]- .ds [A J. Larmouth .ds [T JNT Name Registration Technical Guide .ds [D April 1983 .ds [J Salford University Computer Centre .ds [K NRS .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Mockapetris.P.V.-1984-17 .ds [F Mockapetris84 .]- .ds [A P.V. Mockapetris .ds [T A Domain Nameserver Scheme .ds [D May 1984 .ds [J IFIP WG 6.5 Conference, Nottingham .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .ds [F Mockapetris84 .\"Neufeld.G.W.-1983-11 .ds [F Neufeld83 .]- .ds [A G.W. Neufeld .ds [T EAN: A distributed message system .ds [J Proceedings CIPS National Meeting, Ottowa .ds [P 144-149 .nr [P 1 .ds [D May 1983 .ds [K ean .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Nowitz.D.A.-1978-12 .ds [F Nowitz78 .]- .ds [A D.A. Nowitz .as [A " and M.E. Lesk .ds [T A Dial-up Network of UNIX systems .ds [D August 1978 .ds [R UNIX Programmer's Manual, 7th edition, Bell Labs. .ds [K UUCP .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Postel.J.B.-1982-15 .ds [F Postel82 .]- .ds [A J.B. Postel .ds [T SIMPLE MAIL TRANSFER PROTOCOL .ds [D August 1982 .ds [R RFC 821 .ds [K RFC821 .ds [K SMTP .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Terry.D.B.-1984-13 .ds [F Terry84 .]- .ds [T The Berkeley Internet Name Domain Server .ds [A D.B. Terry .as [A " and others .ds [J Usenix, Salt Lake City .ds [D August 1984 .ds [K bind .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Vittal..J.-1981-14 .ds [F Vittal81 .]- .ds [A J. Vittal .ds [T MSG: A simple message system .ds [J Proc. Int. Symp. Computer Message Systems, Ottowa .ds [I North Holland .ds [D April 1981 .ds [K msg .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .]> .sp RFC indicates a DARPA Request For Comments, which may be obtained from: USC Information Sciences Institute, Marina del Rey, Ca. USA. .SH Acknowledgements .PP Many people have worked on MMDF\ II, including Phil Cockroft, Bernie Cosell, Dave Farber, Dan Long, Lee McLoughlin, Mike Muus, Julian Onions, Brendan Reilly, Dennis Rockwell, and Marshall Rose. Particular credit to Dave Crocker who designed the bulk of MMDF\ II, and to Doug Kingston who bore the brunt of making it work as a production system. .nr [O 0 .][ 1 journal-article .\"1983-1 .ds [F CCITT83 .]- .ds [Q CCITT SG 5/VII .ds [T Recommendations X.400 .ds [D November 1983 .ds [R Message Handling Systems: System Model - Service Elements .ds [K x400 .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Comer.D.A.-1983-16 .ds [F Comer83 .]- .ds [A D.Ammdf/doc/auth.guide 444 0 12 44121 3631167650 7527 .\" *************************************************** .\" indra.me - Steve Kille - Jan 84 .\" .\" /vax2/staff/steve/doc/indra/in.me .\" .\" based on .\" .\" tmac.pl.in - internal/departmental-specific macros .\" .\" *************************************************** .\" .\" .\" .\" INTERNAL NOTE HEADER .\" .\" called as .IN "Internal Note number" "date" "other id" .\" .de IN .ec .ls 1P \" will be reset at top of next page to \n(LS .sp 4 .in +5 .tl ~Internal Note \\$1~~Internal~ .ie \\w~\\$3~ .tl ~\\$3~~Working \&~ .el .tl ~~~Working \&~ .ie \\w~\\$3~ \\{ .tl ~~~Paper \&~ . tl ~\\$2~~~ \\} .el .tl ~\\$2~~Paper \&~ .. .\" .\" *************************************************** .\" .\" TECHNICAL REPORT HEADER .\" called as .TR "Technical Report number" "date" "other id" .\" .de TR .ec .ls 1P \" will be reset at top of next page to \n(LS .sp 4 .tl ~TECHNICAL REPORT \\$1~~UCL-CS TR \\$1 \&~ .ie \\w~\\$3~ .tl ~\\$3~~~ .el .tl ~~~~ .ie \\w~\\$3~ \\{ .tl ~~~~ . tl ~\\$2~~~ \\} .el .tl ~\\$2~~~ .. .\" .\" *************************************************** .\" .\" TITLE & AUTHOR(S) .\" .\" called as .TA "title" "subtitle" "author(s)" "co-author(s)" .\" .de TA .sp |3i .rb .tl ~~\\$1~~ .if \\w~\\$2~ \\{\ . sp . tl ~~\\$2~~ \\} .sp 3 .tl ~~\\$3~~ .sp .if \\w~\\$4~ .tl ~~\\$4~~ .r .. .\" .\" *************************************************** .\" .\" ABSTRACT START .\" .de AB .sp |5..5i .ds AS ABSTRACT: .ll -(\\w~\\*(AS~u-1m) .in +(\\w~\\*(AS~u+4m) .ti -(\\w~\\*(AS~u+1m) \\*(AS \\c .. .\" .\" *************************************************** .\" .\" ABSTRACT END .\" .de AE .wh -6 UC .ls .ll .bp 1 .. .\" .\" *************************************************** .\" .\" UCL LOGO .\" .de UC .tl ~~Department of Computer Science~~ .sp .tl ~~University College London \&~~ .wh -6 .. .de AP .nr $A +1 .sh 1 _ \\n($A .af $A A .af $1 A .bp .ce 2 .uh "Appendix \\n($A" .rb "\\$1" .. .IN 1724 "April 1985" .TA "Configuring MMDF Authorisation" \ "Steve Kille" .he '[MMDF Authorisation]''[Indra Note 1724]' .fo '[Kille]''[Page %]' .AB This note describes how access control is applied in MMDF. It explains how to configure host based controls and user based control, and gives examples. .AE .ae .sh 1 "Introduction" .lp This is intended as a pragmatic document, explaining how to set up MMDF to apply these controls. A more general description of the aims, and a discussion of the UCL usage in conjunction with a full management system is given in \*([.Brink85a\*(.]. A more theoretical discussion of the considerations involved is given in \*([.Kille85a\*(.]. .pp A few concepts are needed to understand this document: .np User: A mailbox. Senders and recipients are two specific types of user. .np Normalised address. An address / source route in RFC 822 syntax with all domains fully qualified. Local addresses will simply be a local login name or alias. .np Channel: A grouping of hosts, all talking the same protocol. Channels are also divided on a network basis at UCL. They might also be divided on a logical/administrative basis. For authorisation purposes, many authorisation constraints are applied to channels, rather than to individual hosts. .np Host: Domains directly connected to (or connected to by a channel with a specific relay host). Distinguish from a general domain, which may be accessed indirectly. .np Message transfer: A message coming from a sender on a channel, and being transferred to one recipient on a channel. Multiple recipients are not considered from the standpoint of authorisation. .np Configuration file: the file which dynamically tailors the channel and authorisation configuration of a local system. This is constructed manually. .sh 1 General .lp All table formats are considered to be as follows. The LHS is terminated with tab or space or colon. Backslash is used as a single character quote. The RHS is a sequence, as defined in mm_tai(3.mmdf). .pp A message is considered to arrive over an inbound channel, and to depart over an outbound channel. The outbound channel may be selected from several possibilities. Each channel has a parameter "auth" which can be set to provide the following controls in each direction for a given channel. The following modes are identified for user access in each direction, with the appropriate values for "auth" in braces: .np FREE: (default) no controls. .np LOG. (inlog/outlog) Just note (and log) authorised / unauthorised access. .np WARN. (inwarn/outwarn) Like log, but send a warning note (from a standard file - see tailoring section of the installatin guide) to the sender of the message. This is intended to allow for gradual introduction of authorisation. .np BLOCK. (inblock/outblock) Reject the message. .pp This mechanism may be used in a straight forward manner to provide control over routes used to a given destination. .sh 1 "Host based Controls" .lp Some controls are applied on the basis of just the channels and hosts involved. This mechanism may be used to control bilateral relaying agreements, or to apply specific relaying policy controls in conjunction with user authorisation. If a host based criterion is identified, this is used to validate both inbound and outbound channel. There are four tables associated with each channel, which are now described. Each table is defined with the appropriate channel parameter (e.g. to use table "insrc", define and MTBL entry, say MTBL foo, and then use channel parameter insrc=foo). The items on the LHS and RHS of the table are then described. If a match is found, the message is deemed to be authorised. It is noted that host and not domain values are used in all of these tables. .ip outdest Used when the associated channel is a (potential) outbound channel. .sp LHS: outbound host. .sp RHS: NULL (LHS valid for all inbound hosts/channels), or list of valid inbound channels and inbound hosts. .ip insrc Used when the associated channel is the inbound channel. Will be the same as outdest if symmetrical controls are applied. .sp LHS: inbound host. .sp RHS: NULL (LHS valid for all outbound hosts/channels), or list of valid outbund channels and outbound hosts. .ip outsrc Used when the associated channel is a (potential) outbound channel. .sp LHS: inbound host or channel .sp RHS: NULL (LHS valid for all outbound hosts/channels), or list of valid outbound hosts. .ip indest Used when the associated channel is the inbound channel. Will be the same as outsrc if symmetrical controls are applied. .sp LHS: outbound host or channel .sp RHS: NULL (LHS valid for all inbound hosts/channels), or list of valid inbound hosts. .lp Some examples of this are given in the appendices. .pp Instead of using the connect host, it is also possible to use the full route to/from the originator's host. This has intrinsically more security holes than the other approaches. However, it should be satisfactory in most cases. .pp This is done by setting "auth=dho" \** .(f \n($f. DHO used to mean Direct Host Only, a relic from when this function was less general. .)f in the channel concerned. Then, all of the controls above apply, except that the host is replaced by a route specification. The route specification \&'knows' about "!" and "%" formats. Routes are specified by use of mailboxes of the form that are associted with the route, with the username replaces by the string "username". For example: "username@rlgb" "wcwvax!username@ucl" "xx!username%b@c". The string, including the sequence "username" is used in the tables. An example is given in the appendices. .sh 1 "User based controls" .pp A message may be authorised in terms of its sender and recipient. This is done by having a file (the authorisation file), to look up authorised mailboxes. The file has RFC 822 route format mailboxes on the LHS, and control information on the RHS. These controls are applied both on the incoming and outgoing channels. .pp Controls are applied in terms of a user database. An example of this is given in an appendix. Essentially, a user, with a given mode of access, is given a number of allowed channels. The following user modes of access are identified (one per user): .np SEND: send only. .np RECV: receive only (for aliases). .np BOTH: send and receive (normal mode). .np LIST: special access for UCL expanded mailing lists. .np EXPIRE. Note that authorisation has lapsed in any error message sent. .np Any other value will be treated as EXPIRE, and the text of the reason passed back in any failure message. .pp In most cases, a messages is authorised by EITHER host controls OR user controls. For some channels, it may be desirable to have both host AND user controls (e.g. to perform policy control on top of user based controls). This is done by setting auth=hau (Host And User) in the channel. .sh 1 "Logging" .pp Logging information relative to these controls is logged in a special log. This will be interpreted and loaded into the database. Attempted illegal accesses are also noted, with similar format. .pp All logging entries contain the message number and the date. There is a special entry containing the message size, and length, logged when the message is accepted by the system. Other logging entries contain: the incoming channel, outgoing channel, receiver address, authorisation reason, and any relevant host names. The following reasons are identified. It is expected to define specific encoding for the log: with the following set, only one reason is given, covering both inbound and outbound authorisation. The inbound and outbound host/route value is given. .np OH - outbound host/route .np HC - outbound host/route + inbound channel .np HH - inbound host/route + outbound host/route .np CC - inbound channel + outbound channel .np CH - inbound channel + outbound host/route .np IH - inbound host/route .lp In the second set, there are two reasons, one for the inbound channel, and one for the outbound. If there is no authorisation required for one of the channels, the reason is left NULL. .np IL - inbound channel, outbound = LIST .np OL - outbound channel, dest = LIST .np IS - inbound channel by sender .np OS - outbound channel by sender .np IR - inbound channel by receiver .np OR - outbound channel by receiver .np *I - inbound channel, logged unauthorised access .np *O - outbound channel, logged unauthorised access .sp .]< .\"Brink.D.H.-1985-1 .ds [F Brink85a .]- .ds [A D.H. Brink .as [A " and S.E. Kille .ds [T Authorisation and Accounting in Store and Forward Messaging systems .ds [D June 1985 .ds [J Networks 85, Wembley .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Kille.S.E.-1985-2 .ds [F Kille85a .]- .ds [A S.E. Kille .as [A " and D.H. Brink .ds [T A Model of Message Flow Control .ds [D Sepetmber 1985 .ds [J Submitted to IFIP WG 6.5 Congress, Washington .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .]> .AP "Tailor file parameter summary" .pp The basic configuration file opertaion is described in the manual page New.tailor (5.mmdf). This appendix details specific values associated with authorisation. The following channel parms and values are identified: .nf /* Various tables for channels */ insrc outsrc indest outdest auth /* channel auth parms */ = free /* default */ = inlog /* log inbound */ = inwarn /* warn inbound */ = inblock /* block inbound */ = outlog = outwarn = outblock = hau /* host AND user checks */ /* host OR user is default */ = dho /* use routes instead of hosts */ .fi .AP "Sample Log" .lp This is a small section of real authorisation log. It should be self explanatory if the information given so far has been digested. Logging entries will usually be in one line, but have been split here for readability. .nf 4/29 9:02:15 AU-0000: msg.a000427: i='local' o='niserc' a='cpuc08@interactive-computing-facility.engineering.cambridge.ac.uk' r='' r='OS' 4/29 9:02:19 AU-0000: msg.a000427: END size='2573', sender='steve' 4/29 9:16:53 AU-0000: msg.a000493: i='local' o='satnet' a='nsc!idi!kiessig@seismo.arpa' r='' r='OS' 4/29 9:16:54 AU-0000: msg.a000493: END size='1654', sender='steve' 4/29 9:18:13 AU-0000: msg.a000507: i='local' o='niserc' a='sjl@ukc.ac.uk' r='' r='OS' 4/29 9:18:14 AU-0000: msg.a000507: END size='640', sender='steve' 4/29 9:44:54 AU-0000: msg.a000561: i='local' o='nipss' a='neil@rsre.AC.UK' r='CH' hi='' ho='username@rsre.ac.uk' 4/29 9:44:54 AU-0000: msg.a000561: i='local' o='nipss' a='laws@rsre.AC.UK' r='CH' hi='' ho='username@rsre.ac.uk' 4/29 9:44:55 AU-0000: msg.a000561: END size='2102', sender='robert' 4/29 9:50:25 AU-0000: msg.a000629: i='local' o='satnet' a='robinson@dmc-crc.arpa' r='' r='OS' 4/29 9:50:26 AU-0000: msg.a000629: END size='559', sender='robert' 4/29 9:53:09 AU-0000: msg.a000653: i='local' o='niserc' a='map@edxa.ac.uk' r='' r='OS' 4/29 9:53:10 AU-0000: msg.a000653: END size='197', sender='phil' 4/29 9:56:12 AU-0000: msg.a000658: i='local' o='satnet' a='zsu@sri-tsc.arpa' r='' r='OS' 4/29 9:56:13 AU-0000: msg.a000658: i='local' o='satnet' a='mathis@sri-tsc.arpa' r='' r='OS' 4/29 9:56:13 AU-0000: msg.a000658: i='local' o='satnet' a='zsu@sri-tsc.arpa' r='' r='OS' 4/29 9:56:14 AU-0000: msg.a000658: END size='570', sender='robert' 4/29 10:46:11 AU-0000: msg.a001076: BAD AUTH i='niserc' o='tunnel' s='@gec-m.rutherford.ac.uk:nhpq03@alvey.ac.uk' r='@csnet-relay.arpa:posty@upenn.csnet' msg='(%s) no authorization for address '@csnet-relay.arpa:posty@upenn.csnet'' 4/29 9:58:27 AU-0000: msg.a000669: i='local' o='niserc' a='cpuc08@interactive-computing-facility.engineering.cambridge.ac.uk' r='' r='OS' 4/29 9:58:28 AU-0000: msg.a000669: END size='2180', sender='phil' .fi .AP "Sample Authorisation file" .pp This is a sample (dummy) auth file. Note that local users should have no doamin reference. Others should be in normalised 822 form. .nf # # Authorisation file # # Mailing lists # foo-outbound should have acceptable source channels # foo-request should have acceptable dest channels academic-outbound:list list,niserc,local,sring academic-request:send niserc,local,sring mailgroup-outbound:list list,isatnet,niserc,nipss mailgroup-request:send satnet,niserc,nipss,niipss # # Users brink:both satnet,niserc,nipss,niipss colin:both satnet,niserc,nipss,niipss eileen:both satnet,niserc,nipss,niipss eliza:both satnet,niserc,nipss,niipss francis:both satnet,niserc,nipss,niipss # # routed over tunnel robert:both tunnel,niserc,nipss,niipss steve:both tunnel,niserc,nipss,niipss tom:both tunnel,niserc,nipss,niipss # # Expired kirstein:"Text justifying reason for lapsed authorisation" # # remote users pfl@gec-d.rutherford.ac.uk:both niserc,tunnel @gec-m.rutherford.ac.uk:joe@alvey.uk:both niserc,tunnel .fi .AP "Configuration for local User Control" .lp To have basic control for local users, all remote channels might have: .nf AUTHLOG level=FST MCHN local auth=inlog,auth=outlog MCHN uucp auth=inblock,auth=outblock MCHN smtp auth=inblock,auth=outblock MTBL auth file="auth" The file auth might have: # # SMTP only users joe:both smtp # # SMTP and UUCP users fred:both smtp,uucp .fi Non-priviliged users (e.g. students) would not be in the auth file. Local usage is logged in the auth log. .AP "Basic channel control" .lp The following is designed to allow free local access to all channels, but to prevent relaying between phone and uucp, or uucp/uucp or phone/phone. .nf AUTHLOG level=FST MTBL control file="control" MCHN local auth=inlog,auth=outlog MCHN uucp auth=inblock,auth=outblock,indest=control,outsrc=control MCHN phone auth=inblock,auth=outblock,indest=control,outsrc=control The file "control" would be simply: local: .fi It can be seen, that if this is the entire set of channels, this is slightly redundant. The following explaination of indest for the uucp channel may be of help: if UUCP is the INbound (INdest) channel, valid DESTination channels (inDEST) are given by the control file. In this case, the channel local is valid for all hosts. The authorization on the phone channel is the same as that on the UUCP channel. .AP "Basic route control" .lp This example is where a number of routes are authorised through one channel (xxx). The basic tailor file would be: .nf MTBL yyy file="yyy" MTBL zzz file="zzz" MCHN xxx auth=inblock,auth=outblock,auth=dho,outsrc=yyy,indest=yyy outdest=zzz,insrc=zzz File yyy: # Routes not on channel xxx # # Local is all valid local: # JNT sites - direct username@cs.ucl.ac.uk: username@gec-b.rutherford.ac.uk: @gec-m.rutherford.ac.uk:username@alvey.uk: # UUCP sites username@kcl-cs.uucp mxpoly!username@kcl-cs.uucp wizzo!yuck!username%yerq@vile.uucp File zzz: # Routes on channel xxx # # all traffic between unido and channel niserc is valid username@unido.uucp:niserc telebox!username@unido.uucp:niserc .fi The routes are assumed to be symmetrical. If not, all four tables would need to be different. .AP "Complex case" .lp An extraction from the UCL tailor file is given. .nf MTBL name=auth, file=auth, display="Authorisation file" MTBL name=mod-auth, file=mod.auth, display="Mod host control" ; SMTP channel stuff (two routes to USA) MCHN tunnel show="via IPSS-Tunnel (outbound) with SMTP" auth=outblock MCHN satnet show="via Satnet (outbound) with SMTP", auth=outblock ; No blockin (US -> UK) ..... YET MCHN isatnet show="via Satnet with SMTP", auth=inlog,auth=outblock MCHN itunnel show="via IPSS-Tunnel with SMTP" auth=inlog,auth=outblock MCHN niserc show="via Janet with NIFTP" auth=inlog,auth=outlog ; Host AND user control on PSS MCHN nipss show="via PSS with NIFTP" auth=inlog,auth=outlog,insrc=mod-auth,outdest=mod-auth MCHN niipss show="via IPSS with NIFTP" auth=inlog,auth=outblock MCHN uucp show="with UUCP" auth=inlog,auth=outlog The file "mod.auth": rsre:isatnet,satnet,local,sring,list are-pn:isatnet,satnet,local,sring,list If nipss had been auth=dho (as it is on one machine at UCL), the file would be: username@rsre.ac.uk:isatnet,satnet,local,sring,list username@are-pn.ac.uk:isatnet,satnet,local,sring,list .fi ng academic-request:send niserc,local,sring mailgroup-outbound:list list,isatnet,niserc,nipss mailgroup-request:send satnet,niserc,nipss,niipss # # Users brink:both satnet,niserc,nipss,niipss colin:both satnet,niserc,nipss,niipss eileen:both satnet,niserc,nipss,niipss eliza:both satnet,niserc,nipss,niipss francis:both satnet,niserc,nipss,niipss # # routed over tunnel robert:both tunnel,niserc,nipss,niipss steve:both tunnel,nisemmdf/doc/uk.overview 444 0 12 6476 3620510346 7741 .sp 3 .ce 3 \fBUK MMDF OVERVIEW Steve Kille February 1985\fP .sp This document is a supplement to the MMDF Overview for sites in the UK Academic Community. It summarizes those features and changes peculiar to, or of particular relevance to such sites. Aspects peculiar to the JNT Mail Protocol are described, and a summary of the JNT Mail modules is given. The UK Academic community uses a domain hierarchy ordering reversed from that in all other RFC 822 based networks. UK ordering, as specified by the JNT Name registration Scheme is referred to as NRS ordering (e.g. UK.AC.Salbridge.CS). Standard RFC 822 ordering is described as RFC 819 ordering (e.g. CS.Salbridge.AC.UK). MMDF uses RFC 819 ordering internally, including in all tables, as this ordering is implicitly assumed in a large number of places in the code. The implications of this are discussed below. .sp When editing conf/sitename/conf.h to set site specific information, the following parameters are of particular relevance: .in +5 .sp .ti -5 #define JNTMAIL .br This should be defined for all sites running the JNT Mail channel. Its major function is to ensure that "%" is treated as lexically equivalent to "@", which is needed for correct handling of JNT Mail source routes. .sp .ti -5 #define BOTHEND .br This should be defined by all sites following NRS domain ordering. It causes domains to be interpreted in both NRS and RFC 819 ordering. As described below, the internal RFC 819 ordering can be made completely transparent to the user. .sp .ti -5 #define VIATRACE .br If this is defined, trace will be inserted in the "Via:" form with the domain in NRS ordering. This conforms to the JNT Mail protocol. The RFC 822 header reformatting discussed in \fBjntmail(8.mmdf)\fP is also turned off. If this is not defined, standard RFC 822 "Received:" trace will be used. VIATRACE should be defined by all UK Academic Community sites which neither intend to perform relaying from RFC 822 protocols other than JNT Mail to JNT Mail nor intend to make extensive external use of the list channel. .in -5 .sp The function of the JNT Mail channel (sending module) is described in \fBjntmail(8.mmdf)\fP, and handling of incoming files in \fBni_niftp(8.mmdf)\fP. For sites operating the York NIFTP (Blue Book), configuration and changes needed to the York NIFTP code are described in \fByork-inst(8.mmdf)\fP. The york code is located in the directory york/ on the UK distribution. .sp As noted above, all internal MMDF tables as described in \fBtables(5.mmdf)\fP, are in RFC 819 ordering. To make this transparent to the user, all channels should be configured to reformat to NRS ordering. This is done in the tailor file described in \fBmmdftailor(5.mmdf)\fP. The ap channel parameter should be set to ap=jnt [which is equivalent to ap=host (i.e. use host value not domain) + ap=733 (use JNT source routes) + ap=big (use NRS ordering)]. Some sites may prefer ap=733 + ap=big. This will be appropriate when the NRS is in general use. Example tailor lines are: .sp .nf MCHN janet name=janet,que=janet,tbl=janet,show="Janet Channel", pgm=niftp,poll=0,confstr="/usr/lib/x25/mhhcp $(FILE) $(ADR)", mod=reg,ap=jnt or MCHN janet name=janet,que=janet,tbl=janet,show="Janet Channel", pgm=niftp,poll=0,mod=reg,confstr="/usr/lib/x25/mhhcp $(FILE) $(ADR)", ap=733,ap=big .fi both satnet,niserc,nipss,niipss eliza:both satnet,niserc,nipss,niipss francis:both satnet,niserc,nipss,niipss # # routed over tunnel robert:both tunnel,niserc,nipss,niipss steve:both tunnel,nisemmdf/doc/authorization 444 0 12 73520 3631167653 10402 .\" *************************************************** .\" indra.me - Steve Kille - Jan 84 .\" .\" /vax2/staff/steve/doc/indra/in.me .\" .\" based on .\" .\" tmac.pl.in - internal/departmental-specific macros .\" .\" *************************************************** .\" .\" .\" .\" INTERNAL NOTE HEADER .\" .\" called as .IN "Internal Note number" "date" "other id" .\" .de IN .ec .ls 1P \" will be reset at top of next page to \n(LS .sp 4 .in +5 .tl ~Internal Note \\$1~~Internal~ .ie \\w~\\$3~ .tl ~\\$3~~Working \&~ .el .tl ~~~Working \&~ .ie \\w~\\$3~ \\{ .tl ~~~Paper \&~ . tl ~\\$2~~~ \\} .el .tl ~\\$2~~Paper \&~ .. .\" .\" *************************************************** .\" .\" TECHNICAL REPORT HEADER .\" called as .TR "Technical Report number" "date" "other id" .\" .de TR .ec .ls 1P \" will be reset at top of next page to \n(LS .sp 4 .tl ~TECHNICAL REPORT \\$1~~UCL-CS TR \\$1 \&~ .ie \\w~\\$3~ .tl ~\\$3~~~ .el .tl ~~~~ .ie \\w~\\$3~ \\{ .tl ~~~~ . tl ~\\$2~~~ \\} .el .tl ~\\$2~~~ .. .\" .\" *************************************************** .\" .\" TITLE & AUTHOR(S) .\" .\" called as .TA "title" "subtitle" "author(s)" "co-author(s)" .\" .de TA .sp |3i .rb .tl ~~\\$1~~ .if \\w~\\$2~ \\{\ . sp . tl ~~\\$2~~ \\} .sp 3 .tl ~~\\$3~~ .sp .if \\w~\\$4~ .tl ~~\\$4~~ .r .. .\" .\" *************************************************** .\" .\" ABSTRACT START .\" .de AB .sp |5..5i .ds AS ABSTRACT: .ll -(\\w~\\*(AS~u-1m) .in +(\\w~\\*(AS~u+4m) .ti -(\\w~\\*(AS~u+1m) \\*(AS \\c .. .\" .\" *************************************************** .\" .\" ABSTRACT END .\" .de AE .wh -6 UC .in .ls .ll .bp 1 .. .\" .\" *************************************************** .\" .\" UCL LOGO .\" .de UC .tl ~~Department of Computer Science~~ .sp .tl ~~University College London \&~~ .wh -6 .. .IN 1726 "April 1985" .TA "AUTHORISATION AND ACCOUNTING" \ IN \ "STORE AND FORWARD MESSAGE HANDLING SYSTEMS" \ "D.H. Brink and S.E. Kille" .he '[Authorisation]''[Indra Note 1726]' .fo '[Kille/Brink]''[Page %]' .AB Paper to be presented in June at Networks 85, Wembley. .AE .rb .ce 6 AUTHORISATION AND ACCOUNTING IN STORE AND FORWARD MESSAGE HANDLING SYSTEMS D.H. Brink and S.E. Kille Department of Computer Science University College London .r .sp 2 Most existing store and forward message handling systems have either ignored accounting and authorisation problems, or have treated them in a simple manner. The widespread adoption of the CCITT X.400 protocols is likely to introduce relayed services provided by multiple vendors. These services will, by their nature, require sophisticated authorisation and accounting. This paper develops a model for applying such controls, and describes experience with an experimental service implemented on the basis of this model. Finally, the general utility of this model is considered. .sp 2 .in +4c Denis Brink is a research assistant at University College London, Department of Computer Science. He has a BA in Philosophy and MSc in Computer Science from University College London. He is currently working on the interconnection service project, and has worked in industry on microcomputer operating systems. .sp 3 Steve Kille received a BA in Physics from Oxford in 1978, and MScs in Electrical Engineering from UMIST (1980) and Stanford (1981). He has been a Research Assistant in the department of Computer Science, at University College London since 1981, where he has worked on various aspects of Message Handling Systems. His current research work is on Directory Services. .in -4c .bp .sh 1 Introduction .lp Most existing store and forward message handling systems have either ignored accounting and authorisation problems, or have treated them in a simple manner. The widespread adoption of the CCITT X.400 protocols is likely to lead to the introduction of relayed services provided by multiple vendors (or management domains in X.400 terms) \*([.CCITT84a\*(.]. These services will, by their nature, require sophisticated authorisation and accounting. The first part of this paper develops a model of the controls which might be applied to such services. .pp The second part of the paper considers an implementation of such controls. University College London provides a number of message relaying services between the UK and the USA for registered groups of users. A system has been developed to prevent unauthorised usage, and to provide accounting and statistics for authorised usage. The structure of the database system used to generate control and accounting information, and the internal message system organisation used to apply these controls are described. Experience with the use of this system and pragmatic difficulties of its application are discussed. Finally, the application of such services in a wider context are considered. .sh 1 "A Model of Authorisation" .lp The message services under consideration are assumed to be provided by systems which can be described in terms of the CCITT X.400 model of store and forward Message Handling Systems (MHS). .sp 7c .ce Figure 1: X.400 functional model of MHS .sp The X.400 model assumes two layers. The lower layer is the Message Transfer Layer, and is provided by .rb "Message Transfer Agents" (MTA), which transfer messages in a store and forward manner. That is to say, a message may be transferred over a sequence of MTAs, and be queued at each one. The upper layer is the Inter Personal Messaging layer, which is provided by .rb "User Agents" (UA). A UA should be considered as the software with which a user interacts to compose or to read electronic messages. Communication between UAs is end to end, but there will be no OSI connection between a pair of UAs. Rather, the store and forward service of the MTA layer is used to provide an asynchronous end to end connection. A UA is specified by a (globally unique) Originator/Recipient Name, or .rb "O/R name" . There are O/R names at both the UA and MTA level. The MTA level O/R names (P1 level in X.400) are analogous to the names on an envelope in paper based mail. The UA level O/R names (P2 level in X.400) are analogous to the names in a contained letter. They are often, but not necessarily, the same. The transfer of a message consists of a number of distinct stages: .np A user composes a message and addresses it to a (UA level) recipient. .np The message is transferred to an MTA, together with the MTA level recipient O/R name. The MTA will ensure that a valid MTA level originator O/R name is specified. .np The message is routed through a sequence of MTAs on the basis of the MTA level recipient O/R name. .np The message is delivered to the recipient's UA. .np The recipient reads the message, which is usually interpreted in terms of UA level O/R names. .pp A final concept, which is of relevance here, is that of .rb "Management Domain" . A Management Domain is an organisation providing MTA, and (optionally) UA services. This is illustrated in figure 1. A Management Domain does not necessarily supply the underlying OSI connection services. .pp The model proposed here operates on the assumption that MTAs can in general be trusted, whereas UAs cannot. Thus all control is at the MTA level. This has the added advantage that if the MTA layer is used for traffic other than inter-personal messages, then the control mechanisms still work correctly. Controls are now considered, as applied by an individual MTA. An MTA is assumed to be trusted in two ways: .np When accepting a message from a UA, the MTA level originator O/R name is specified correctly by the MTA. .np When a message is transferred between Management Domains, this is indicated accurately (i.e. a specification of the Management Domain being left) in the MTA level trace information\**. .(f \n($f. In X.400, trace information is on the basis of transfer between Management Domains. In other systems, MTA level trace is on the basis of transfer between MTAs, and so what is considered as Management Domain accounting here, may be MTA accounting in other systems. .)f .lp This allows an MTA to determine responsibility for a given transfer, and implicitly an authority to bill, on the basis of two classes of item: .np One or more management domains. Trace information will allow the MTA to determine the sequence of Management Domains which have been traversed. A recipient O/R name can determine at least the next Management Domain, by use of a directory service. .np Originator and recipient O/R names. .lp It is assumed that the MTA has information allowing certain forms of access on the basis of these parameters. The set of parameters determining the authority for the MTA to process the message is used to determine a .rb "billing authority" \**. .(f \n($f. The term billing authority is used, but this does not necessarily imply any form of bill. In practice, the question: "who would pay for this if there was a charge" is useful to determine responsibility. .)f Examples: .ip - All traffic from Management Domain X to Management Domain Y is transferred, and billed to Management Domain X. .ip - All traffic from user "Zoe Foobar" may be transferred to Management Domain "Betta Message Services Inc", and billed to the organisation "Widget Manufacturing, Plc". .pp As well as determining whether or not a message is authorised to be processed by the MTA, the billing authority for a given message allows Quality of Service Parameters and appropriate costs to be determined. Some Quality of Service Parameters of interest are: .np Route choice, where message can be sent by more that one route. This may be an MTA level route, or choice of OSI connection to the next MTA. .np When to send message. For example, a message transfer may be timed to take advantage of the tariff structure of the OSI services. .np Selection of OSI connection Quality of Service (e.g. use of encryption). .pp The basis for identification of billing authority is now considered in more detail. A common policy will be an agreement between the local Management Domain and an adjacent Management Domain. This agreement might be to relay all traffic to a set of other Management Domains, and to deliver to UAs in (and/or serviced by) the local Management Domain. This might or might not be restricted to traffic originating in the adjacent Management Domain (which can be distinguished from relayed traffic by the trace information). There might also be an agreement with a Management Domain connected to through an intermediate Management Domain. It is likely that this intermediate Management Domain will be explicit, as: .ip - The intermediate Management Domain is likely to be party to the agreement, at least implicitly. .ip - Use of a different intermediate Management Domains might cause undesired bills .ip - It makes message forgery harder. .lp O/R names may be authorised to send and/or receive over a set of Management Domains. Again, for the same reasons, O/R names are likely to be authorised in the context of an explicit route of Management Domains. This type of authorisation should be regarded as the authorisation of individual users to use the services of the MTA in question. .lp The approach described here has the following weaknesses. .np It relies on MTA security not being breached. This seems reasonable, as an MTA will not in general be trusted unless real guarantees are given (e.g. by the payment of bills). .np It relies on the integrity of the OSI connection used to verify the identity of a remote MTA. In principle, this can be made as secure as desired. .lp In practice, some low level of forgery is acceptable, provided accounting and policing techniques allow for higher levels of abuse to be detected and traced. Some real experiences with this model are now considered. .sh 1 "UCL Experience" .lp The networking environment of the department of Computer Science at University College London, and its control and accounting requirements are now described. This is followed by overviews of the mail system, the database system, and their interaction for accounting and control. Problems encountered and weaknesses of the system are identified. .br .ne 7c .sp 6c .ce Figure 2: UCL Connectivity .pp The department offers an Interconnection Service\*([.Kirstein85a\*(.] to allow UK (and some continental European) university and research institution workers to communicate with their US counterparts. The requirement for such a service arises from the diverse protocol systems used by the interconnected networks \*([.Cole84a\*(.]. .pp The department has connections to the UK X.25 networks PSS and JANET, and the DARPA Internet. PSS (Packet Switched Service) is British Telecom's national public X.25 network, with gateways to the international X.25 service. JANET (Joint Academic NETwork) is the private X.25 network of the Science and Engineering Research Council and the Computer Board, linking universities and scientific research establishments. The higher level protocols used by the academic community on these X.25 networks in the UK are the "coloured book protocols" serving as an "intercept" standard pending the introduction of suitable ISO standards \*([.Rosner82a\*(.]. Mail services on Janet are provided by use of the JNT Mail Protocol (also known as Greybook) \*([.Kille84a\*(.]. This specifies both an MTA level protocol and a UA level protocol. .pp The DARPA (U.S. Defense Advanced Research Projects Agency) Internet is a concatenation of networks linked by gateways. The department's local area network can be treated as one such network. These networks use a common set of internet protocols based on datagrams \*([.Leiner85a\*(.]. The MTA level protocol is SMTP (Simple Mail Transfer Protocol) \*([.Postel82a\*(.], and the UA level protocol is a format specification known as RFC 822 \*([.Crocker82a\*(.]. The JNT Mail Protocol UA level is derived from RFC 822, and is identical in all the major aspects. Interworking is thus straightforward. The DARPA protocols are also used to access CSNET (the US Computer Science Network). UCL is also connected to a UNIX\** .(f \n($f. UNIX is a trademark of AT&T Bell Laboratories. .)f dialup telephone network, Usenet, which again uses RFC 822 derived protocols \*([.Nowitz78a\*(.]. .pp The department's local area network consists of three Cambridge rings interconnected by bridges, and also an Ethernet. Associated with each wide area network are one or more network access machines. Application processes access the network access machines by use of a special Host to Front-end protocol, which allows for working over multiple networks in a clean manner. Current service applications are Terminal Access, File Transfer and Mail. .\"Remote users have terminal access .\"to a host dedicated to providing interconnection services. Mail .\"systems run on all the hosts. .\".pp .\"Connectivity mail nets -- numbers direct/indirect .pp Use of the service is circumscribed by the policies of the funding bodies, and by the Value Added Carrier Licence from the UK Department of Trade. These restrictions are complex, specifying permitted national/international relaying and permitted message path, in terms of billing authority. Coupled with the extensive connectivity and high connection costs, the above restrictions necessitate effective control and accounting measures. Users are grouped according to sponsorship, charging policy and access rights. In practice this broadly divides users into a centrally-funded class, with access rights determined by sponsor, and a directly-funded class with corresponding access rights. .pp The Message Transfer service is provided by MMDF (Multi Channel Memo Distribution Facility) \*([.Kingston84a,\|Kille85a\*(.]. MMDF is a Message Handling System for the UNIX operating system, having support for multiple message transfer protocols, and a selection of UA systems. MMDF provides the user with a homogeneous view of the underlying connectivity. The MMDF MTA is implemented by a number of processes and queues. The process .rb submit is invoked to accept messages submitted both by local UA processes and by servers handling incoming messages. All authorisation controls are applied by submit at message submission time. There are a number of .rb channels to associate protocols, networks or logical divisions. Associated with each channel is a queue into which submit places incoming messages. The process .rb deliver reads and manages the queues associated with one or more channels and invokes subordinate channel processes to perform message transmission. .pp Channels are used as a mechanism for logically dividing hosts into sets for the application of management controls. There are three orthogonal mechanisms for doing this: .np Splitting a set of hosts into more than one channel. For example, PSS and Janet hosts are on separate channels as the former network charges for services, and the latter is free, though technically they could be on the same channel as the same protocols may be used. .np Allocating a set of hosts to more than one channel for route or Quality of Service selection. For example, there are two paths between UCL and the rest of the DARPA Internet, for use by different users, and with different polling frequencies. .np Separating inbound and outbound channels, when the policies are asymmetric (e.g. where a user can receive messages from a given channel, but not send messages over it). .pp It is noted that a message can be considered both to arrive and to depart over a channel. A message arrives over a fixed .rb "inbound channel" , but may leave over one of a set of .rb "outbound channels" , as the requested destination may well be accessible over more than one channel. Submit considers each possible outbound channel in turn, and queues the message for the first (if any) channel satisfying authorisation criteria. Access policies for each channel are implemented in terms of two basic sets of controls. The following channel access policies are identified; they may differ for inbound and outbound directions. .np FREE: No controls. .np LOG: Note unauthorised access. .ne 4 .np WARN: As for LOG, but send warning to sender of message. This is particularly useful for transition from a free to a controlled situation. .np BLOCK: Only authorised access to the channel. .pp The first set of controls is applied on the basis of user (O/R name). There is a (hash encoded) database of O/R names, giving a mode and a list of authorised channel. The following O/R name modes are identified. .np SEND: O/R name only valid as originator. .np RECV: O/R name only valid as recipient. .np BOTH: O/R name valid as both sender and recipient. .np LIST: special control for distribution lists expanded as a value added service. .lp When a message is being authorised by submit, the originator and recipient O/R names are looked up, and a set of valid inbound and outbound channels identified for each. This information is used .np To determine whether there is authorisation for the inbound channel. .np To identify which (if any) of the potential outbound channels are acceptable. .lp The priority on the matches are: 1) the first possible outbound channel; 2) recipient with list authorisation; 3) sender; 4) recipient. .pp The second set of controls is applied on the basis of just the MTAs and channels involved. These controls are applied by table lookup in four tables associated with each channel. Two tables are for inbound and two for outbound control. As well as MTA and channel, there is the concept of MTA route, to identify a sequence of MTAs, with the message having originated at the first MTA in the sequence or (ostensibly) be destined for a user at the last MTA in the sequence. An important special case is that of messages originating on, or destined for an adjacent MTA. The following may be controlled: .ip - Inbound channel to everywhere, or to a set of MTAs and outbound channels. .ip - Outbound channel from everywhere, or from a set of MTAs and inbound channels. .ip - Inbound MTA to everywhere, or to a set of MTAs and outbound channels. .ip - Outbound MTA from everywhere, or from a set of MTAs and inbound channels. .ip - Inbound MTA route to everywhere, or to a set of MTAs and outbound channels. .ip - Outbound MTA route from everywhere, or from a set of MTAs and inbound channels. .lp These MTA/channel controls are applied before the user controls and in general are considered to override them. They may also be applied in addition to the user controls, to apply policy controls on top of essentially O/R name based controls. This mechanism might be used to prevent third country relaying, in cases where both originator and recipient are authorised in other contexts. .pp The following checks and actions are performed by MMDF, to prevent forgery. .np Only privileged processes can access the message queues. .np Locally originated messages have correct UA level originator O/R name. .np Locally originated messages have correct MTA level originator O/R name. This is the O/R name used for authorisation. .np When a message is received, the name of the connecting MTA is added to the source route associated with the originator O/R name. O/R names must have an authorised route, to minimise the risk of forgery. .pp The Interconnection Service Management System (ISMS) used to provide high level access to and control of the above mechanisms is now discussed. ISMS is based on Mistress, a proprietary relational database package \*([.Rhodnius82a\*(.]. The basic model of operation is that high level administrative information is contained in the ISMS. This information is used to derive control information for MMDF, in the form of plain text files to be incorporated into the hash encoded database used by MMDF. When a message transfer is authorised or rejected by MMDF, this is noted in a log, together with: originator O/R name; recipient O/R name; inbound channel; outbound channel; any MTA route involved; the size of the message; and reason for authorisation for both the inbound and outbound channel. The MTAs directly involved are implicit in the O/R names. Example reasons for authorisation: .ip - Inbound by originator + no authorisation required outbound. .ip - Inbound by recipient + outbound by recipient. .ip - Inbound MTA to outbound channel pair. .lp These logs are then processed at intervals by the ISMS (typically once per day), and stored in terms of the administrative information in the ISMS. This information can then be used to generate bills, and usage statistics. The following are the main relations in the database. .ip - Class of user. This gives the set of channels to which access is permitted. Most users fall into one of a very small number of classes. .ip - Project. A project authorised to use the Interconnection Service. This contains funding information, and indicates the class. Also various management O/R names. These can be used for automatic mailing of accounting, statistical, and other project information. .ip - Mailbox. Indicates an O/R name in terms of project. .ip - Message. Indicates a transfer, and may be associated with one or two mailboxes for authorisation. .ip - Channel. Indicates charging policy in terms of class of user. .pp To prevent administrative overload, each project is given an account at UCL allowing project administrators to remotely update UA registrations by use of a simple program. This two level scheme, means that the UCL administration deals with projects and not individual users. .pp This scheme was brought into operation in February 1985, and has clearly demonstrated the viability of the proposed model. There are currently about 150 projects and 2000 UAs registered. The system handles about 1000 messages per day. Perhaps the hardest aspect to deal with was the transition from an uncontrolled situation, where messages were relayed without checking (even though they should have been authorised in principle). A significant number of project applications were processed over this period. The following problems were noted: .ip - Accurate naming is essential. MTA tables (on all MTAs involved) must be kept consistent for this scheme to work effectively. .ip - Certain MTA protocol requirements are made. A few implementations which violated message protocols for other obscure reasons had problems registering O/R names. .ip - Strict matching of the source route for incoming messages means that a few sites have to register multiple routes for each O/R name. This is due to both variable internal routing at these sites, and non-symmetrical routing for incoming and outgoing directions. This is seen as an unfortunate consequence of a control necessary to minimise forgery. .ip - If a message is routed over an insecure OSI connection, or through an insecure MTA, there is clearly a risk of forgery. This risk must be borne by the project registering such a route. .bp .ip - Submission time checking means that messages which are not correctly delivered are still charged for. This is undesirable, but removes the (non-trivial) problem of correlating submission and delivery logs. It does not appear to be a substantial problem in practice. .ip - If a message transfer is optimised by transferring one copy of the message for multiple recipients, the saving is not passed on to the user. This seems reasonable, on the grounds that the user should be charged for services in a uniform manner. .sh 1 Conclusions .lp The model developed here is not a general one; A broader description of the theoretical issues may be found in \*([.Kille85b\*(.]. However, we believe that our work has shown the viability of a scheme where messages are authorised, with no requirements placed on the remote systems other than accurate and trustworthy protocol implementation. Our logs have not, as yet, shown any successful attempts to break the security. In fact the problems of correctly registering authorised users, partly due to the stringency of the checks, have been far more of a problem. .pp The major advantage of the approach described is that it allows for MTA level accounting, with minimal assistance from other system components. It is seen as has having two basic styles of application. The first is where a Management Domain (or pair of Management Domains) offer communication over an expensive link (or some service such as message format conversion). This approach would enable billing of users and/or Management Domains for the use of this service. The UCL implementation is of this type. A second area where is might be useful is as a gateway to a Management Domain providing services for one organisation. This approach would allow the organisation to control its employees' access to Message Handling Services, much as telephone systems are sometimes controlled at present. This type of approach is seen a particularly important as controls are introduced into systems without overall control in this area. .sp .]< .\"1982-1 .ds [F Rhodnius82a .]- .ds [T Mistress: Relational Database Management System Version 2.1 .ds [Q Rhodnius Incorporated .ds [R Manual .ds [D 1982 .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"1984-2 .ds [F CCITT84a .]- .ds [Q CCITT SG 5/VII .ds [T Recommendations X.400 .ds [D November 1984 .ds [R Message Handling Systems: System Model - Service Elements .ds [K x400 .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Cole.R.H.-1984-3 .ds [F Cole84a .]- .ds [A R.H. Cole .as [A " and others .ds [T Network Interconnection Facilities at UCL .ds [D September 1984 .ds [J ICCC, Melbourne .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Crocker.D.H.-1982-4 .ds [F Crocker82a .]- .ds [A D.H. Crocker .ds [T Standard of the Format of ARPA Internet Text Messages .ds [D August 1982 .ds [R RFC 822 .ds [K RFC822 .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Kille.S.E.-1984-5 .ds [F Kille84a .]- .ds [A S.E. Kille, (editor) .ds [T JNT Mail Protocol (revision 1.0) .ds [D March 1984 .ds [I Joint Network Team .ds [C Rutherford Appleton Laboratory .ds [K greybook .ds [K jntmail .nr [T 0 .nr [A 0 .nr [O 0 .][ 2 book .\"Kille.S.E.-1985-6 .ds [F Kille85a .]- .ds [T Addressing in MMDF II .ds [A S.E. Kille .ds [J Proc. EUUG Conference, Paris .ds [D April 1985 .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Kille.S.E.-1985-7 .ds [F Kille85b .]- .ds [A S.E. Kille .as [A " and D.H. Brink .ds [T A Model of Message Flow Control .ds [D Sepetmber 1985 .ds [J Submitted to IFIP WG 6.5 Congress, Washington .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Kingston.D.P.-1984-8 .ds [F Kingston84a .]- .ds [A D.P. Kingston .ds [T MMDFII: A Technical Review .ds [J Usenix Conference .ds [C Salt Lake City .ds [D August 1984 .ds [K MMDF .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Kirstein.P.T.-1985-9 .ds [F Kirstein85a .]- .ds [T The University College London International Computer Communications Interconnection Service .ds [A P.T. Kirstein .ds [J Conference on Communications in Distributed Systems, Karlsruhe .ds [D March 1985 .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Leiner.B.M.-1985-10 .ds [F Leiner85a .]- .ds [T The DARPA Internet Protocol Suite .ds [A B.M. Leiner .as [A ", R.H. Cole .as [A ", J.B. Postel .as [A ", and D. Mills .ds [J Proceedings INFOCOM85 .ds [I IEEE .ds [D March 1985 .ds [l own paper .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .\"Nowitz.D.A.-1978-11 .ds [F Nowitz78a .]- .ds [A D.A. Nowitz .as [A " and M.E. Lesk .ds [T A Dial-up Network of UNIX systems .ds [D August 1978 .ds [R UNIX Programmer's Manual, 7th edition, Bell Labs. .ds [K UUCP .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Postel.J.B.-1982-12 .ds [F Postel82a .]- .ds [A J.B. Postel .ds [T SIMPLE MAIL TRANSFER PROTOCOL .ds [D August 1982 .ds [R RFC 821 .ds [K RFC821 .ds [K SMTP .nr [T 0 .nr [A 0 .nr [O 0 .][ 4 tech-report .\"Rosner.R.A.-1982-13 .ds [F Rosner82a .]- .ds [T Towards OSI among UK Universities .ds [A R.A. Rosner .ds [J Proc. ICCC'82, London .ds [I North-Holland .ds [D September 1982 .ds [P 607-611 .nr [P 1 .ds [K OSI Universities .nr [T 0 .nr [A 0 .nr [O 0 .][ 1 journal-article .]> .sp .ip Note: 7 RFC indicates a DARPA Request For Comments, which may be obtained from: USC Information Sciences Institute, Marina del Rey, Ca. USA. .sh 1 Acknowledgements .lp Thanks to Tom Daniel for helpful comments on the paper. The first is where a Management Domain (or pair of Management Domains) offer communication over an expensive link (or some service such as message format conversion). This appmmdf/doc/README 444 0 12 715 3631176106 6364 How to make the Documentation Unfortunately there is too much variation in document preparation proceedures for me to make a Makefile for it, but here is the basic info you need: addressing: troff -ms addressing administrators: administrators/Makefile review/*: troff -ms fnmacro p? auth.guide: troff -me auth.guide authorization: troff -me authorization table.sample.kille: pr table.sample.kille uk.overview: troff uk.overview (Troff in the raw!) 82, London .ds [I North-Holland .ds [D September 19mmdf/lib/ 755 0 12 0 3656466050 5446 mmdf/lib/addr/ 755 0 12 0 3671074611 6353 mmdf/lib/addr/gen 555 0 12 57 3620510355 7071 make -f ../../Makefile.com -f Makefile.real $* there is too much variation in document preparation proceedures for me to make a Makefile for it, but here is the basic info you need: addressing: troff -ms addressing administrators: administrators/Makefile review/*: troff -ms fnmacro p? auth.guide: troff -me auth.guide authorization: troff -me authorization table.sample.kille: pr table.sample.kille uk.overview: troff uk.overview (Troff in the raw!) 82, London .ds [I North-Holland .ds [D September 19mmdf/lib/addr/ap_util.c 444 0 12 30574 3664425127 10274 #include "util.h" #include "ap.h" #if DEBUG > 1 extern char debug; extern char *typtab[]; #endif /* Standard routines for handling address list element nodes /* < 1978 B. Borden Wrote initial version of parser code * 78-80 D. Crocker Reworked parser into current form * Apr 81 K. Harrenstein Hacked for SRI * Jun 81 D. Crocker Back in the fold. Finished v7 conversion * minor cleanups. * repackaging into more complete set of calls * Jul 81 D. Crocker ap_free & _alloc check for not 0 or -1 * malloc() error causes jump to ap_init error */ struct ap_prevstruct *ap_fle; /* parse state top of stack */ /* "fl" => file, but could be other */ int (*ap_gfunc) (); /* Ptr to character get fn */ extern int ap_peek; /* basic parse state info */ extern int ap_perlev; extern int ap_grplev; extern char *malloc(); /* ******************** LIST NODE PRIMITIVES *********************** */ AP_ptr ap_alloc () /* create node, return pointer to it */ { AP_ptr ap; /* NOSTRICT */ ap = (AP_ptr) malloc (sizeof (struct ap_node)); if (ap == (AP_ptr) 0) return ((AP_ptr) 0); ap_ninit (ap); return (ap); } ap_ninit (ap) register AP_ptr ap; { ap -> ap_obtype = APV_NIL; ap -> ap_obvalue = (char *) 0; ap -> ap_ptrtype = APP_NIL; ap -> ap_chain = (AP_ptr) 0; } ap_free (ap) /* free node's storage */ register AP_ptr ap; { switch ((int)ap) { /* get rid of node, if have one */ case OK: case NOTOK: /* nothing to free */ break; default: /* actually have a node */ switch ((int)(ap -> ap_obvalue)) { /* get rid of its data string */ case OK: case NOTOK: /* nothing to free */ break; default: free (ap -> ap_obvalue); } free ((char *) ap); } } ap_fllnode (ap, obtype, obvalue) /* add data to node at end of chain */ register AP_ptr ap; char obtype; register char *obvalue; { extern char *strdup (); ap -> ap_obtype = obtype; ap -> ap_obvalue = (obvalue == 0) ? (char *)0 : strdup (obvalue); #if DEBUG > 1 if (debug) printf ("(%s/'%s')", typtab[obtype], obvalue); #endif } AP_ptr ap_new (obtype, obvalue) /* alloc & fill node */ char obtype; char *obvalue; { register AP_ptr nap; nap = ap_alloc (); ap_fllnode (nap, obtype, obvalue); return (nap); } /* *************** LIST MANIPULATION PRIMITIVES ******************* */ ap_insert (cur, ptrtype, new) /* create/fill/insert node in list */ register AP_ptr cur; /* where to insert after */ char ptrtype; /* inserted is more or new address */ register AP_ptr new; /* where to insert after */ { /* Now copy linkages from current node */ new -> ap_ptrtype = cur -> ap_ptrtype; new -> ap_chain = cur -> ap_chain; /* Now point current node at inserted node */ cur -> ap_ptrtype = ptrtype; cur -> ap_chain = new; } AP_ptr ap_sqinsert (cur, type, new) /* insert sequence */ register AP_ptr cur, new; int type; { AP_ptr oldptr; int otype; switch ((int)new) { case OK: case NOTOK: return ((AP_ptr) 0); } oldptr = cur -> ap_chain; otype = cur -> ap_ptrtype; cur -> ap_chain = new; cur -> ap_ptrtype = type; while (new -> ap_ptrtype != APP_NIL && new -> ap_chain != (AP_ptr) 0 && new -> ap_chain -> ap_obtype != APV_NIL) new = new -> ap_chain; if (new -> ap_chain != (AP_ptr)0 && new -> ap_chain -> ap_obtype == APV_NIL) ap_delete (new); new -> ap_chain = oldptr; new -> ap_ptrtype = otype; return (new); } ap_delete (ap) /* remove next node in sequence */ register AP_ptr ap; { register AP_ptr next; if (ap != (AP_ptr) 0 && ap -> ap_ptrtype != APP_NIL) { /* only if there is something there */ next = ap -> ap_chain; /* link around one to be removed */ ap -> ap_ptrtype = next -> ap_ptrtype; ap -> ap_chain = next -> ap_chain; ap_free (next); } } AP_ptr ap_append (ap, obtype, obvalue) /* alloc, fill, insert node */ register AP_ptr ap; /* node to insert after */ char obtype; char *obvalue; { register AP_ptr nap; nap = ap_alloc (); ap_fllnode (nap, obtype, obvalue); ap_insert (ap, APP_ETC, nap); return (nap); } /**/ AP_ptr ap_add (ap, obtype, obvalue) /* try to append data to current node */ register AP_ptr ap; char obtype; register char *obvalue; { extern char *multcat (); register char *ovalue; if (ap -> ap_obtype != obtype) return (ap_append (ap, obtype, obvalue)); else /* same type or empty => can append */ { if (obvalue == 0) /* No data to add */ return (OK); if ((ovalue = ap -> ap_obvalue) == (char *) 0) ap_fllnode (ap, obtype, obvalue); else /* add to existing data */ { ovalue = ap -> ap_obvalue; ap -> ap_obvalue = multcat (ovalue, " ", obvalue, (char *)0); free (ovalue); } #if DEBUG > 1 if (debug) printf ("+%d/'%s')", obtype, obvalue); #endif } return (OK); } /**/ AP_ptr ap_sqdelete (strt_node, end_node) /* remove nodes, through end node */ register AP_ptr strt_node; register AP_ptr end_node; { switch ((int)strt_node) { case OK: case NOTOK: return ((AP_ptr)0); } while (strt_node -> ap_ptrtype != APP_NIL) { if (strt_node -> ap_chain == end_node) { /* last one requested */ ap_delete (strt_node); return (strt_node -> ap_chain); } ap_delete (strt_node); } return ((AP_ptr) 0); /* end of chain */ } AP_ptr ap_1delete (ap) /* remove all nodes of address to NXT */ register AP_ptr ap; /* starting node */ { while (ap -> ap_ptrtype != APP_NIL) { if (ap -> ap_ptrtype == APP_NXT) return (ap -> ap_chain); ap_delete (ap); } return ((AP_ptr) 0); /* end of chain */ } ap_sqtfix (strt, end, obtype) /* alter obtype of a node subsequence */ register AP_ptr strt; register AP_ptr end; register char obtype; { for ( ; ; strt = strt -> ap_chain) { if (strt -> ap_obtype != APV_CMNT) strt -> ap_obtype = obtype; if (strt == end || strt -> ap_ptrtype == APP_NIL) break; } } /**/ AP_ptr ap_move (to, from) /* move node after from to be after to */ register AP_ptr to, from; { register AP_ptr nodeptr; if (from -> ap_ptrtype == APP_NIL || from -> ap_chain == (AP_ptr) 0) return (from); /* quiet failure */ nodeptr = from -> ap_chain; from -> ap_chain = nodeptr -> ap_chain; from -> ap_ptrtype = nodeptr -> ap_ptrtype; ap_insert (to, APP_ETC, nodeptr); return (from); /* next in chain, now */ } AP_ptr ap_sqmove (to, from, endtype) /* move sequence */ register AP_ptr to, from; register char endtype; /* copy only COMMENT and this */ { switch ((int)from) { case OK: case NOTOK: return ((AP_ptr) 0); } while (from -> ap_ptrtype != APP_NIL && from -> ap_chain != (AP_ptr) 0) { if (endtype != (char) APV_NIL) if (from -> ap_obtype != APV_CMNT && from -> ap_obtype != endtype) break; to = ap_move (to, from); } return (to); /* end of chain */ } /* ************************ PARSE STATE *************************** */ AP_ptr ap_pstrt, /* current last node in parse tree */ ap_pcur; /* current last node in parse tree */ ap_iinit (gfunc) /* input function initialization */ int (*gfunc) (); { ap_gfunc = gfunc; /* Set character fetch func */ ap_peek = -1; /* No lex peek char */ } ap_clear () /* Clear out the parser state */ { ap_grplev = 0; /* Zero group nesting depth */ ap_perlev = 0; /* Zero <> nesting depth */ } AP_ptr ap_pinit (gfunc) /* init, alloc & set start node */ int (*gfunc) (); { ap_iinit (gfunc); return (ap_pstrt = ap_pcur = ap_alloc ()); } /* parse state saving uses a linked list of state information, * recorded in ap_prevstruct structures. * the list is manipulated as a simple stack. */ ap_ppush (gfunc) /* save parse context, ap_iinit */ int (*gfunc) (); { extern char *malloc (); register struct ap_prevstruct *tfil; /*NOSTRICT*/ if ((tfil = (struct ap_prevstruct *) malloc (sizeof (*tfil))) == (struct ap_prevstruct *) NULL) return (NOTOK); tfil -> ap_opeek = ap_peek; /* save regular parse state info */ tfil -> ap_ogroup = ap_grplev; tfil -> ap_opersn = ap_perlev; tfil -> ap_prvgfunc = ap_gfunc; tfil -> ap_prvptr = ap_fle; /* save previous stack entry */ ap_fle = tfil; /* save current stack entry */ ap_iinit (gfunc); /* create new parse state */ return (OK); } ap_ppop () /* restore previous parse state */ { register struct ap_prevstruct *tfil; tfil = ap_fle; ap_peek = tfil -> ap_opeek; ap_grplev = tfil -> ap_ogroup; ap_perlev = tfil -> ap_opersn; ap_gfunc = tfil -> ap_prvgfunc; ap_fle = tfil -> ap_prvptr; free ((char *) tfil); } /**/ /* the next three routines handle most of the overhead for acquiring * the address list from a file. */ ap_flget () /* get character from included file */ { register int c; c = getc (ap_fle -> ap_curfp); if (c == '\n') return (','); /* a minor convenience */ return (c); } ap_fpush (file) /* indirect input from file */ char file[]; { if (ap_ppush (ap_flget) == NOTOK) /* save current & set for file input */ return (NOTOK); if ((ap_fle -> ap_curfp = fopen (file, "r")) == (FILE *) NULL) { /* couldn't get the file, tho */ ap_ppop (); return (NOTOK); } return (OK); } ap_fpop () /* pop the stack, if any input nested */ { if (ap_fle -> ap_curfp != NULL) fclose (ap_fle -> ap_curfp); ap_ppop (); } /* ****************** PARSE LIST MANIPULATION ********************* */ /* these echo the basic list manipuation primitives, but use ap_pcur * for the pointer and any insert will cause ap_pcur to be updated * to point to the new node. */ ap_palloc () /* alloc, insert after pcur */ { ap_pnsrt (ap_alloc (), APP_ETC); } ap_pfill (obtype, obvalue) /* add data to node at end of chain */ char obtype; register char *obvalue; { extern char *strdup (); ap_pcur -> ap_obtype = obtype; ap_pcur -> ap_obvalue = (obvalue == (char *) 0) ? (char *) 0 : strdup (obvalue); #if DEBUG > 1 if (debug) printf ("(%s/'%s')", typtab[obtype], obvalue); #endif } ap_pnsrt (ap, ptrtype) /* add node to end of parse chain */ register AP_ptr ap; char ptrtype; { register AP_ptr rap_pcur; if ((rap_pcur = ap_pcur) -> ap_obtype == APV_NIL) { /* current one can be used */ rap_pcur -> ap_obtype = ap -> ap_obtype; rap_pcur -> ap_obvalue = ap -> ap_obvalue; ap -> ap_obvalue = 0; ap_free (ap); } else { /* really do the insert */ rap_pcur -> ap_ptrtype = ptrtype; rap_pcur -> ap_chain = ap; ap_pcur = ap; } } ap_pappend (obtype, obvalue) /* alloc, fill, append at end */ char obtype; char *obvalue; { ap_palloc (); /* will update pcur */ ap_fllnode (ap_pcur, obtype, obvalue); } ap_padd (obtype, obvalue) /* try to append data to current node */ char obtype; char *obvalue; { register AP_ptr nap; nap = ap_add (ap_pcur, obtype, obvalue); if (nap != OK) /* created new node */ ap_pcur = nap; } h */ } AP_ptr ap_pinit (gfunc) /* init, alloc & set start node */ int (*gfunc) (); { ap_mmdf/lib/addr/ap_p2s.c 444 0 12 27653 3671073140 10017 #include "util.h" /* from ../utildir */ #include "conf.h" /* from ../mmdf/h */ #include "ch.h" #include "ap.h" #include "dm.h" #include "ll_log.h" /* Format one address from pointers to constitutents, in a tree * * Returns: pointer to string if successful or * NOTOK if error * * SEK - using ap_p2s to output for ap_t2s has the general problem * of losing comments. Perhaps ap_t2s should be * separate? */ extern LLog *logptr; extern int ap_outtype; extern char *multcat(); extern char *strdup(); extern char *ap_dmflip(); extern Domain *dm_v2route(); char * ap_p2s (group, name, local, domain, route) AP_ptr group, /* beginning of group name */ name, /* beginning of person name */ local, /* beginning of local-part */ domain, /* basic domain reference */ route; /* beginning of 733 forward routing */ { Dmn_route dmnroute; AP_ptr lastptr; char *routp; /* 822 -> 733 route string */ int inperson, ingroup; register char *strp; /* The string we are building */ register char *cp; register AP_ptr curptr; char *flipptr; char *stripptr; char tmpbuf [LINESIZE]; char *tmpdomain [DM_NFIELD]; int tmpcnt; char buf[LINESIZE]; /* buf for dm_v2route */ char *drefptr; /* pointer to string to be output */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "ap_p2s()"); if ((ap_outtype & AP_822) == AP_822) /* AP_733 is implicit default */ ll_log (logptr, LLOGFTR, "AP_822 on"); ll_log (logptr, LLOGFTR, (ap_outtype & AP_BIG) ? "AP_BIG on" : "AP_LITTLE on"); if ((ap_outtype & AP_NODOTS) == AP_NODOTS) /* AP_DOTS is implicit def. */ ll_log (logptr, LLOGFTR, "AP_NODOTS on"); #endif inperson = ingroup = FALSE; strp = strdup(""); routp = strdup(""); if (group != (AP_ptr) 0) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "ap_p2s: group is '%s'", group -> ap_obvalue); #endif for (curptr = group; curptr != (AP_ptr)0; curptr = curptr -> ap_chain) { /* print munged addr */ switch (curptr -> ap_obtype) { default: case APV_NIL: break; case APV_CMNT: /* Output value as comment */ if (name != (AP_ptr) 0) { /* only output comments for pretty forms */ val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT); cp = multcat(strp, (strp[0]?" ":""), "(",tmpbuf,")", (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; } continue; case APV_NGRP: ingroup = TRUE; case APV_GRUP: val2str (tmpbuf, curptr -> ap_obvalue, APV_GRUP); cp = multcat(strp, tmpbuf, (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; continue; } break; } if (ingroup) { cp = multcat(strp, ": ", (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; } } if (name != (AP_ptr) 0) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "ap_p2s: name is '%s'", name -> ap_obvalue); #endif for (curptr = name; curptr != (AP_ptr)0; curptr = curptr -> ap_chain) { /* print munged addr */ switch (curptr -> ap_obtype) { default: case APV_NIL: break; case APV_CMNT: /* Output value as comment */ val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT); cp = multcat(strp, (strp[0]?" " : ""), "(", tmpbuf, ")", (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; continue; case APV_NPER: inperson = TRUE; case APV_PRSN: val2str (tmpbuf, curptr -> ap_obvalue, APV_PRSN); cp = multcat(strp, tmpbuf, (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; continue; } break; } } if (inperson) { cp = multcat(strp, " <", (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; } if (route != (AP_ptr) 0) /* we have routing info */ { #ifdef DEBUG ll_log (logptr, LLOGFTR, "ap_p2s: route is '%s'", route -> ap_obvalue); #endif for (lastptr = curptr = route; ; curptr = curptr -> ap_chain) { if (curptr == (AP_ptr)0) /* Grot Grot !!!!!!! */ goto defcase1; switch (curptr -> ap_obtype) { case APV_EPER: continue; defcase1:; /* YEUCH !! */ default: case APV_NIL: if ((ap_outtype & AP_822) == AP_822) { /* piece of cake */ cp = multcat(strp, ":", (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; } break; case APV_CMNT: /* Output value as comment */ if (name != (AP_ptr) 0) { val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT); cp = multcat(strp, (strp[0]?" ":""), "(",tmpbuf,")", (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; } continue; case APV_DLIT: case APV_DOMN: val2str (tmpbuf, curptr -> ap_obvalue, curptr -> ap_obtype); flipptr = stripptr = (char *) 0; drefptr = tmpbuf; if (((ap_outtype & AP_BIG) == AP_BIG) || (((ap_outtype & AP_NODOTS) == AP_NODOTS) && (curptr == lastptr)) ) { /* check domain ref in either case */ Domain *lrval = dm_v2route (tmpbuf, buf, &dmnroute); if(lrval == (Domain *)MAYBE) return( (char *)MAYBE); if (lrval != (Domain *) NOTOK) { if ((ap_outtype & AP_NODOTS) == AP_NODOTS && curptr == lastptr) { /* only strip domain on next-hop in route */ tmpcnt = cstr2arg (dmnroute.dm_argv[0], DM_NFIELD, tmpdomain, '.'); stripptr = strdup(tmpdomain[0]); drefptr = stripptr; } else if ((ap_outtype & AP_BIG) == AP_BIG) { flipptr = ap_dmflip (buf); drefptr = flipptr; } } } if ((ap_outtype & AP_822) == AP_822) { /* piece of cake */ cp = multcat(strp,(curptr!=lastptr?",@":"@"),drefptr,(char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; } else { if(routp[0] == '\0') cp = multcat("@", drefptr, (char *)0); else cp = multcat("%", drefptr, routp, (char *)0); free (routp); if(cp == (char *)0) return( (char *)NOTOK); routp = cp; } if (flipptr != (char *)0) free (flipptr); if (stripptr != (char *)0) free (stripptr); continue; } break; } } if (local != (AP_ptr) 0) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "ap_p2s: local is '%s'", local -> ap_obvalue); #endif for (curptr = local; curptr != (AP_ptr)0; curptr = curptr -> ap_chain) { switch (curptr -> ap_obtype) { /* print munged addr */ default: case APV_NIL: break; case APV_CMNT: /* SEK - don't skip these */ if (name != (AP_ptr) 0) { val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT); cp = multcat(strp, (strp[0]?" ":""), "(",tmpbuf,")", (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; } continue; case APV_WORD: case APV_MBOX: /* SEK - YUK */ if (strindex (":Include:", curptr -> ap_obvalue) == 0) (void) strcpy (tmpbuf, curptr -> ap_obvalue); else val2str (tmpbuf, curptr -> ap_obvalue, APV_MBOX); cp = multcat(strp, tmpbuf, (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; continue; } break; } } if (domain != (AP_ptr) 0) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "ap_p2s: domain is '%s'", domain -> ap_obvalue); #endif val2str (tmpbuf, domain -> ap_obvalue, domain -> ap_obtype); flipptr = stripptr = (char *) 0; drefptr = tmpbuf; if (((ap_outtype & AP_BIG) == AP_BIG) || (((ap_outtype & AP_NODOTS) == AP_NODOTS)) && route == (AP_ptr) 0) { /* check domain ref in either case */ Domain *lrval = dm_v2route (tmpbuf, buf, &dmnroute); if(lrval == (Domain *)MAYBE) return( (char *)MAYBE); if (lrval != (Domain *) NOTOK) { if (((ap_outtype & AP_NODOTS) == AP_NODOTS) && (route == (AP_ptr) 0)) { /* If there is no route, this domain is next hop: strip */ tmpcnt = cstr2arg (dmnroute.dm_argv[0], DM_NFIELD, tmpdomain, '.'); stripptr = strdup(tmpdomain[0]); drefptr = stripptr; } else if ((ap_outtype & AP_BIG) == AP_BIG) { flipptr = ap_dmflip (buf); drefptr = flipptr; } } } if ((ap_outtype & AP_822) == AP_822 || routp[0] == '\0') /* easy */ cp = multcat (strp, "@", drefptr, routp, (char *)0); else cp = multcat (strp, "%", drefptr, routp, (char *)0); if(cp == (char *)0) return(cp); free (strp); strp = cp; if (flipptr != (char *) 0) free (flipptr); if (stripptr != (char *) 0) free (stripptr); } free (routp); if (inperson) { cp = multcat(strp, ">", (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; } if (ingroup) { cp = multcat(strp, ";", (char *)0); free (strp); if (cp == (char *)0) return( (char *)NOTOK); strp = cp; } return (strp); } /* * This function is just barely usable. The hole problem of * quoted strings is hard to get right especially when the * mail system is trying to make up for human forgetfulness. * -DPK- * SEK - have improved this somewhat by giving knowledge of * the various object types. Does not handle strings of spaces. */ LOCFUN val2str (buf, value, obtype) /* convert to canonical string */ char *buf, *value, obtype; { int gotspcl; int inquote; register char *fromptr, *toptr; #ifdef DEBUG ll_log (logptr, LLOGFTR, "val2str ('%s', %d)", value, obtype); #endif if (obtype == APV_CMNT) { for (fromptr=value, toptr=buf; *fromptr != '\0'; *toptr++ = *fromptr++) switch (*fromptr) { case '\r': case '\n': case '\\': case '(': case ')': *toptr++ = '\\'; } *toptr = '\0'; return; } if (obtype == APV_DLIT) { for (fromptr=value, toptr=buf; *fromptr != '\0'; *toptr++ = *fromptr++) switch (*fromptr) { case '\r': case '\n': case '\\': *toptr++ = '\\'; continue; case '[': if (fromptr != value) *toptr++ = '\\'; continue; case ']': if (*(fromptr + 1) != (char) 0) *toptr++ = '\\'; } *toptr = '\0'; return; } inquote = FALSE; for (gotspcl = FALSE, fromptr = value; *fromptr != '\0'; fromptr++) { switch (*fromptr) { case '"': inquote = (inquote == TRUE ? FALSE : TRUE); /* Flip-Flop */ break; case '\\': case '\r': case '\n': if (inquote == FALSE) { gotspcl = TRUE; goto copyit; } break; case '<': case '>': case '@': case ',': case ';': case ':': case '\t': case '[': case ']': case '(': case ')': if( inquote == FALSE) { gotspcl = TRUE; goto copyit; } break; case ' ': if (inquote == FALSE) if ((obtype == APV_DOMN) || (obtype == APV_MBOX)) { gotspcl = TRUE; goto copyit; } else { /* SEK hack to handle " at " */ /* yes - this really is needed */ if ((uptolow(*(fromptr + 1)) == 'a') && (uptolow(*(fromptr + 2)) == 't') && (*(fromptr + 3) == ' ')) { gotspcl = TRUE; goto copyit; } } break; case '.': if (inquote == FALSE && ((obtype == APV_GRUP) || (obtype == APV_PRSN))) { gotspcl = TRUE; goto copyit; } break; } } gotspcl = inquote; /* If were in a quote, something's wrong */ copyit: toptr = buf; if (gotspcl) *toptr++ = '"'; for (fromptr = value; *fromptr != '\0'; *toptr++ = *fromptr++) switch (*fromptr) { case '\r': case '\n': case '\\': case '"': if (gotspcl) *toptr++ = '\\'; } if (gotspcl) *toptr++ = '"'; *toptr = '\0'; } se */ Domain *lrval = dm_v2route (tmpbuf, buf, &dmnroute); if(lrval == (Dmmdf/lib/addr/norm.c 444 0 12 4060 3620510360 7543 #include "util.h" #include "mmdf.h" #include "ap.h" #include "ch.h" extern LLog *logptr; extern int ap_outtype; main (argc, argv) int argc; char *argv[]; { char adr [LINESIZE]; char *p; AP_ptr *ap; AP_ptr *group; AP_ptr *name; AP_ptr *route; AP_ptr *domain; AP_ptr *local; AP_ptr *norm; int i; Chan *thechan; char *dfldomain = (char *) 0; char *dflhost = (char *) 0; mmdf_init ("NORM"); logptr -> ll_level = LLOGFTR; siginit (); if ((thechan = ch_nm2struct ("foo")) == (Chan *) NOTOK) { printf ("Chan foo not found\n"); thechan = (Chan *) 0; } else { printf ("channel '%s' used\n", thechan -> ch_show); dfldomain = thechan -> ch_ldomain; dflhost = thechan -> ch_lname; } fflush (stdout); for (i=1; i < argc; i++) { printf ("received: '%s'\n", argv[i]); fflush (stdout); ap = ap_s2tree (argv[i]); if (ap == (AP_ptr) NOTOK) { printf ("Parse failed\n\n"); continue; } ap_outtype = AP_822; ap_t2s (ap, &p); printf ("tree in full is: '%s'\n", p); fflush (stdout); free (p); norm = ap_normalize (dflhost, dfldomain, ap, thechan); ap_t2s (norm, &p); printf ("normalised tree (822, little) is: '%s' \n", p); fflush (stdout); free (p); ap_t2parts (ap, &group, &name, &local, &domain, &route); p = ap_p2s ((AP_ptr) 0, (AP_ptr) 0, local, domain, route); printf ("real bits: '%s'\n", p); fflush (stdout); free (p); ap_outtype = AP_733; ap_t2s (norm, &p); printf ("normalised tree (jnt, little) is: '%s' \n", p); fflush (stdout); free (p); ap_outtype = (AP_822 | AP_BIG); ap_t2s (norm, &p); printf ("normalised tree (822, big) is: '%s' \n", p); free (p); fflush (stdout); if (thechan == NULL) continue; ap_outtype = thechan -> ch_apout; ap_t2s (norm, &p); printf ("normalised tree (from channel type) is: '%s' \n", p); free (p); printf ("\n\n"); fflush (stdout); } } err_abrt (code, fmt, b, c, d) char code, *fmt, *b, *c, *d; { printf ("norm aborting (%s)\n", rp_valstr (code)); printf (fmt, b, c, d); fflush (stdout); exit (0); } .h" #include "ch.h" extern LLog *logptr; extern int ap_outtype; main (argc, argv) int argc; char *argv[]; { char adr [LINESIZE]; char *p; AP_ptr *ap; AP_ptr *group; AP_ptr *name; AP_ptr *route; AP_ptr *domain; AP_ptr *local; AP_ptr *norm; int i; Chan *thechan; char *dfldomain = (char *) 0; char *dflhost = (char *) 0; mmdf_init ("NORM"); logptr -> ll_level = LLOGFTR; siginit (); if ((thmmdf/lib/addr/parse.c 444 0 12 4601 3620510360 7703 /* $Header: parse.c,v 1.3 85/01/17 23:07:11 dpk Exp $ */ /* $Log: parse.c,v $ * Revision 1.3 85/01/17 23:07:11 dpk * Reworked, it now works * * Revision 1.2 83/11/18 17:09:41 reilly * Steve Kille's version * * < 1978 B. Borden Wrote initial version of parser code * 78-80 D. Crocker Reworked parser into current form */ #if DEBUG < 2 main() { fprintf(stderr, "parse is useless when compiled with -DDEBUG=x (x < 2)\n"); } #else #include "ap_lex.h" #include "util.h" #include "mmdf.h" #include "ap.h" extern char ap_llex; extern char *locname; AP_ptr ap_fullparse (); extern LLog *logptr; extern char *namtab[], *typtab[]; extern char debug; main (argc, argv) { AP_ptr ap_fp; int getach(); mmdf_init ("PARSE"); logptr -> ll_level = LLOGFTR; if (argc == 2) debug++; for (;;) { printf ("Parse: "); switch (ap_fp = ap_fullparse (getach)) { case NOTOK: printf ("\nNOTOK: on %s\n", namtab[ap_llex]); break; case DONE: printf ("\nNOTOK: on %s\n", namtab[ap_llex]); break; case OK: printf ("\nOK?: on %s\n", namtab[ap_llex]); break; default: printf ("\n\nAccept\n"); pretty (ap_fp); printf ("\n"); while (ap_sqdelete (ap_fp, (AP_ptr)0) != 0); ap_free (ap_fp); /* delete full string */ ap_fp = (AP_ptr) 0; continue; } break; } exit (0); } getach () { int c; c = getchar(); if (c == '\n') return (0); if (c == EOF) exit(0); return (c); } pretty (ap) register AP_ptr ap; { register int depth = 1; do { switch (ap -> ap_obtype) { case APV_EGRP: case APV_EPER: depth -= 2; } printf ("%.*s%-9s", depth, " "); if (ap -> ap_obtype >= 0 && ap -> ap_obtype <= 13) printf("%s %s", typtab[ap -> ap_obtype], ap -> ap_obvalue ? ap -> ap_obvalue : "NIL"); else printf("BOGUS!(%d) %s", ap -> ap_obtype, ap -> ap_obvalue ? ap -> ap_obvalue : "NIL"); switch (ap -> ap_ptrtype) { case APP_NIL: printf("\t(NIL)\n"); break; case APP_NXT: printf("\t(NXT)\n\n"); break; case APP_ETC: printf("\t(ETC)\n", ap -> ap_ptrtype); break; default: printf("\t(BOGUS!)\n", ap -> ap_ptrtype); } switch (ap -> ap_obtype) { case APV_NGRP: case APV_NPER: depth += 2; } } while ((ap = ap -> ap_chain) != (AP_ptr)0); printf ("End on null pointer\n"); } #endif = multcat(strp, ">", (char *)0); free (strp); if(cp == (char *)0) return( (char *)NOTOK); strp = cp; } if (ingrmmdf/lib/addr/ap_s2p.c 444 0 12 1735 3620510361 7763 #include "ap_lex.h" #include "util.h" #include "ap.h" /* * ap_s2p.c * * One of the main uses of this routine is to replace adrparse.c */ LOCVAR char *s2p_txt; /* hdr_in() passes to alst() */ LOCFUN s2p_in () /* adrs extracted from component text */ { if (s2p_txt == (char *) 0) /* nothing to give it */ return (EOF); switch (*s2p_txt) { case '\0': case '\n': /* end of string, DROP ON THROUGH */ s2p_txt = (char *) 0; return (0); } return (*(s2p_txt++)); } char * ap_s2p (strp, tree, group, name, local, domain, route) char *strp; AP_ptr *tree, *group, *name, *local, *domain, *route; { s2p_txt = strp; *tree = ap_pinit (s2p_in); switch (ap_1adr ()) { case DONE: return ((char *) DONE); case OK: ap_t2parts (*tree, group, name, local, domain, route); return (s2p_txt); /* so they can parse the next chunk */ } return ((char *) NOTOK); } ase DONE: printf ("\nNOTOK: onmmdf/lib/addr/ap_lex.c 444 0 12 16305 3664425130 10075 #include "util.h" #include "ap.h" #include "ap_lex.h" /* Perform lexical analysis on input stream The stream is assumed to be "unfolded" and the <crlf>/<lwsp> sequence is NOT checked for. This must be done by the character-acquisition routine, if necessary. In fact, space, tab and newline all return the same lexical token. Due to a number of bagbiting mail systems on the net which cannot handle having a space within a mailbox name, period (.) has been equated with space. Letters, numbers, and other graphics, except specials, also all return the same token. Note that only printable characters and format effectors are legal. All others cause an error return. Only COMMENTs and WORDs have data associated with them. */ /* < 1978 B. Borden Wrote initial version of parser code * 78-80 D. Crocker Reworked parser into current form * Apr 81 K. Harrenstein Hacked for SRI * Jun 81 D. Crocker Back in the fold. Finished v7 conversion * minor cleanups. */ extern char ap_lxtable[]; /* ascii chars -> symbolic terminals */ extern int ap_intype; int ap_peek = -1; /* one-character look-ahead */ char ap_llex; /* last lexeme returned by ap_lex () */ #if DEBUG > 1 extern char debug; char *namtab[] = { "eo-data", /* LV_EOD 0 */ "error", /* LV_ERROR 1 */ "comma", /* LV_COMMA 2 */ "at", /* LV_AT 3 */ "colon", /* LV_COLON 4 */ "semi", /* LV_SEMI 5 */ "comment", /* LV_COMMENT 6 */ "less", /* LV_LESS 7 */ "grtr", /* LV_GRTR 8 */ "word", /* LV_WORD 9 */ "from", /* LV_FROM 10 */ "domain-literal" /* LV_DLIT 11 */ }; #endif /**/ ap_lex (lexval) char lexval[]; { register char c, *lexptr; register int retval; while ((retval = ap_lxtable[c = ap_char ()]) == LT_SPC) ; /* Skip space, tab and newline */ lexptr = lexval; *lexptr++ = c; switch (retval) { case LT_ERR: /* Bad Character */ retval = LV_ERROR; break; case LT_EOD: /* End Of Data stream */ retval = LV_EOD; break; case LT_COM: /* comma "," -- addr list separator */ retval = LV_COMMA; break; case LT_AT: /* At sign "@" -- node separator */ retval = LV_AT; break; /* ******************* DATA TYPES AND GROUP LIST ******************** */ case LT_COL: /* colon ":" -- data type / group */ retval = LV_COLON; break; case LT_SEM: /* semicolon ";" -- group end */ retval = LV_SEMI; break; /* *********************** PERSON ADDRESS LIST ************************ */ case LT_LES: /* less-than-sign "<" -- person list */ if (ap_lxtable[c = ap_char ()] == LT_LES) retval = LV_FROM; /* << implies redirection */ else { ap_peek = c; /* restore xtra char */ retval = LV_LESS; } break; case LT_GTR: /* greater-than-sign ">" -- end person */ retval = LV_GRTR; break; /* ******************* QUOTED & UNQUOTED WORDS ********************** */ case LT_LTR: /* letters */ case LT_SQT: /* single-quote "'" -- just char, here */ case LT_RPR: /* right paren ")" -- just char, here */ for (;;) { switch (ap_lxtable[*lexptr++ = c = ap_char ()]) { case LT_LTR: case LT_SQT: case LT_RPR: continue; case LT_ERR: retval = LV_ERROR; break; case LT_EOD: /* permit eod to end string */ default: /* non-member character */ ap_peek = c; lexptr--; #ifdef notdef /* no more " at " == '@' */ if (ap_intype == AP_733 && lexptr == &lexval[2] && uptolow (lexval[0]) == 'a' && uptolow (lexval[1]) == 't' ) retval = LV_AT; else #endif notdef retval = LV_WORD; } break; } break; case LT_QOT: /* double quote "\"" => string */ retval = LV_WORD; --lexptr; /* don't put quotes into obvalue - SEK */ for (;;) { switch (ap_lxtable[*lexptr++ = c = ap_char ()]) { case LT_QOT: --lexptr; break; case LT_SQT: /* include next char w/out interpeting */ /* and drop on through */ *(lexptr - 1) = ap_char (); case LT_RPR: case LT_LPR: default: continue; case LT_ERR: case LT_EOD: retval = LV_ERROR; } break; } break; /* ************************* COMMENT ******************************** */ case LT_LPR: /* left paren "(" -- comment start */ lexptr--; /* remove left-most paren */ for (retval = 0;;) { /* retval is count of comment nesting */ switch (ap_lxtable[*lexptr++ = c = ap_char ()]) { case LT_LPR: /* nested comments */ retval++; /* just drop on through */ default: continue; case LT_SQT: /* include next char w/out interpeting */ *(lexptr - 1) = ap_char (); continue; case LT_RPR: if (--retval > 0) break; lexptr--; /* remove right-most paren */ retval = LV_COMMENT; break; case LT_EOD: case LT_ERR: retval = LV_ERROR; break; } break; } break; /* ********************* DOMAIN LITERAL ***************************** */ case LT_LSQ: /* left squar bracket "[" */ FOREVER { switch (ap_lxtable[*lexptr++ = c = ap_char ()]) { default: continue; case LT_SQT: /* include next char w/out interpeting */ *(lexptr - 1) = ap_char (); continue; case LT_RSQ: retval = LV_DLIT; break; case LT_EOD: case LT_ERR: retval = LV_ERROR; break; } break; } break; } /* *********************** CLEANUP AND RETURN ************************* */ *lexptr = '\0'; #if DEBUG > 1 if (debug) printf (" %s", namtab[retval]); #endif return (ap_llex = retval); } /* ******************* GET NEXT INPUT CHARACTER ********************* */ ap_char () { /* handle lookahead and 8th bit */ extern int (*ap_gfunc) (); /* Ptr to character get fn */ register int i; if (ap_peek == 0) return (0); if ((i = ap_peek) > 0) { ap_peek = -1; return (i); } if ((i = ((*ap_gfunc) ())) == -1) return (0); /* EOD */ return ((isascii (i)) ? i : '\177'); /* force error, if eighth bit is on */ } V_SEMI; break; /* *********************** PERSON ADDRESS LIST ************************ */ case LT_LES: /* less-than-sign "<" -- person list */ if (ap_lxtable[c = ap_char ()] == LT_LES) retval = LV_FROM; /* << implies redirection */ else { ap_peek = c; /*mmdf/lib/addr/ap_t2parts.c 444 0 12 5600 3620510361 10651 #include "util.h" #include "ap.h" #include "ll_log.h" /* Record beginnings of major sections of an address * * Returns: pointer to next node, after address * 0 if end of tree * -1 if error * * if both rroute and nroute are requested, and the tree has routing * in reverse-path form, then these will point to the same node. */ extern LLog *logptr; LOCVAR int perlev, grplev; AP_ptr ap_t2parts (tree, group, name, local, domain, route) register AP_ptr tree; /* the parse tree */ /* THESE ARE POINTERS TO POINTERS */ /* where to stuff the relevant pointers */ AP_ptr *group, /* beginning of group name */ *name, /* beginning of person name */ *local, /* beginning of local-part */ *domain, /* basic domain reference */ *route; /* beginning of 822 reverse routing */ { AP_ptr retptr; int gotlocal; #ifdef DEBUG ll_log (logptr, LLOGFTR, "ap_t2parts ()"); #endif if (tree -> ap_obtype == APV_NIL) return ((AP_ptr) OK); /* Ignore null stuff */ grplev = perlev = 0; if (group != (AP_ptr *) 0) *group = (AP_ptr) 0; if (name != (AP_ptr *) 0) *name = (AP_ptr) 0; if (local != (AP_ptr *) 0) *local = (AP_ptr) 0; if (domain != (AP_ptr *) 0) *domain = (AP_ptr) 0; if (route != (AP_ptr *) 0) *route = (AP_ptr) 0; for (gotlocal = FALSE; ; tree = tree -> ap_chain) { /* print munged addr */ switch (tree -> ap_obtype) { case APV_NIL: retptr = (AP_ptr) 0; goto endit; case APV_CMNT: /* Output value as comment */ break; case APV_NPER: perlev++; if (name != (AP_ptr *) 0 && *name == (AP_ptr) 0) *name = tree; break; case APV_PRSN: break; case APV_EPER: perlev--; break; case APV_NGRP: grplev++; if (group != (AP_ptr *) 0 && *group == (AP_ptr) 0) *group = tree; break; case APV_GRUP: break; case APV_EGRP: grplev--; break; case APV_WORD: case APV_MBOX: if (local != (AP_ptr *) 0 && *local == (AP_ptr) 0) *local = tree; gotlocal = TRUE; break; case APV_DLIT: case APV_DOMN: if (gotlocal) { /* reference after local */ if (domain != (AP_ptr *) 0 && *domain == (AP_ptr) 0) *domain = tree; } else { /* must be 822 route */ /* domain precedes local-part */ if (route != (AP_ptr *) 0 && *route == (AP_ptr) 0) *route = tree; } break; default: break; } switch (tree -> ap_ptrtype) { case APP_NXT: if (tree -> ap_chain -> ap_obtype != APV_NIL) { retptr = (AP_ptr) tree -> ap_chain; goto endit; } /* else DROP ON THROUGH */ case APP_NIL: retptr = (AP_ptr) OK; goto endit; /* Courtesy of Steve Kille */ } } endit: return (retptr); } logptr; LOCVAR int perlev, grplev; AP_ptr ap_t2parts (tree, group, name, local, domain, route) register AP_ptr tree; mmdf/lib/addr/ap_s2tree.c 444 0 12 1754 3620510361 10464 #include "util.h" #include "ap.h" #include "ll_log.h" /* parse a string into an address tree */ extern LLog *logptr; LOCVAR char *ap_strptr; LOCVAR getach () /* get next character from string */ { if (*ap_strptr == '\0') /* end of the string, of course */ return (0); return (*ap_strptr++); } AP_ptr ap_s2tree (thestr) char thestr[]; { short gotone; AP_ptr thetree; #ifdef DEBUG ll_log (logptr, LLOGBTR, "ap_s2tree (%s)", thestr); #endif ap_strptr = thestr; if ((thetree = ap_pinit (getach)) == (AP_ptr) NOTOK) goto badend; ap_clear(); gotone = FALSE; for ( ; ; ) switch (ap_1adr ()) { case NOTOK: ap_sqdelete (thetree, (AP_ptr) 0); ap_free (thetree); badend: ap_strptr = (char *) 0; return ((AP_ptr) NOTOK); case OK: gotone = TRUE; continue; /* more to process */ case DONE: ap_strptr = (char *) 0; if (gotone == FALSE) return ((AP_ptr) NOTOK); return (thetree); } /* NOTREACHED */ } parts ()"); #endif mmdf/lib/addr/ap_t2s.c 444 0 12 2624 3620510362 7766 #include "util.h" /* from ../utildir */ #include "conf.h" /* from ../mmdf/h */ #include "ch.h" #include "ap.h" #include "dm.h" #include "ll_log.h" /* Format one address from parse tree, into a string * * Returns: pointer to next node, after address * 0 if end of tree * NOTOK if error */ extern LLog *logptr; extern char *strdup(); extern char *ap_p2s(); extern AP_ptr ap_t2parts(); AP_ptr ap_t2s (thetree, strpp) AP_ptr thetree; /* the parse tree */ char **strpp; /* where to stuff the string */ { AP_ptr locptr, /* in case we need to fake personal name */ grpptr, namptr, domptr, routptr, rtntree; #ifdef DEBUG ll_log (logptr, LLOGFTR, "ap_t2s ()"); #endif rtntree = ap_t2parts (thetree, &grpptr, &namptr, &locptr, &domptr, &routptr); if (rtntree == (AP_ptr)NOTOK) { ll_log (logptr, LLOGTMP, "ap_t2s: error from ap_t2parts()"); *strpp = strdup ("(MMDF Error!)"); return ((AP_ptr)NOTOK); } *strpp = ap_p2s (grpptr, namptr, locptr, domptr, routptr); if(*strpp == (char *)MAYBE){ *strpp = strdup(""); /* is this needed ?? */ return ((AP_ptr)MAYBE); } else if (*strpp == (char *)NOTOK) { ll_log (logptr, LLOGTMP, "ap_t2s: error from ap_p2s()"); *strpp = strdup ("(MMDF Error!)"); return ((AP_ptr)NOTOK); } return (rtntree); } r) 0; for (gotlocal = FALSE; ; tree = tree -> ap_chain) { /* print munged addr mmdf/lib/addr/fullparse.c 444 0 12 4322 3620510362 10570 #include "ap_lex.h" #include "util.h" #include "ap.h" /* Build full parse tree from an address list */ /* I expect this not to be used very often, but it was convenient to provide and will work just fine when there are small address lists. It repeatedly invokes ap_1adr to acquire address segments, linking them together and NXT's. */ /* < 1978 B. Borden Wrote initial version of parser code * 78-80 D. Crocker Reworked parser into current form */ extern AP_ptr ap_pcur, /* Current ap node ptr */ ap_pstrt; /* Beginning of parse tree */ #if DEBUG > 1 extern char debug; /* True if in debug mode */ extern char *statnam[], *ptrtab[], *typtab[]; #endif /**/ AP_ptr ap_fullparse (gchfunc) char (*gchfunc) (); { AP_ptr ap_fp; /* Beginning of list */ char more; register AP_ptr ap_sp, ap_prevp = NULL; int naddrs = 0; ap_fp = ap_pinit (gchfunc); more = 0; for (;;) { ap_sp = ap_pcur; switch (ap_1adr ()) { case NOTOK: return ((AP_ptr)NOTOK); case DONE: if (--more > 0) /* > 0 means files are stacked */ ap_fpop (); /* just drop on through */ else if (naddrs) return(ap_fp); else return ((AP_ptr)DONE); case OK: naddrs++; switch (ap_sp -> ap_obtype) { case APV_DTYP: if (lexequ ("Include", ap_sp -> ap_obvalue)) { /* indirect through a file */ #if DEBUG > 1 if (debug) printf ("(File:%s)", ap_sp -> ap_chain -> ap_obvalue); #endif if (ap_fpush (ap_sp -> ap_chain -> ap_obvalue) != OK) return ((AP_ptr)NOTOK); more++; if ((ap_pcur = ap_sqdelete (ap_sp, ap_pcur)) == 0) ap_palloc (); else ap_pnsrt (ap_alloc (), APP_NXT); if (ap_fp == ap_sp) ap_fp = ap_pcur; if (ap_prevp != ap_sp) ap_prevp -> ap_chain = ap_pcur; continue; } default: #if DEBUG > 1 printf ("[Got an address]\n", ap_pcur); #endif ap_pcur -> ap_ptrtype = APP_NXT; ap_prevp = ap_pcur; ap_pstrt = ap_pcur = ap_alloc(); ap_prevp -> ap_chain = ap_pstrt; continue; } } } } if (gotlocal) { /* reference after local */ if (domain != (AP_ptr *) 0 && *domain == (AP_ptr) 0) *domain = tree; } else { /* must be 822 route */ /* domain precedes local-part */ if (route != (AP_ptr *) 0 && *route == (AP_ptr) 0) *route = trmmdf/lib/addr/Makefile.real 444 0 12 6530 3635173347 11032 # # Makefile for address parser portion of libmmdf.a # # Just the prefix of the name should be given (i.e. "foo" for "foo.c") MODULES = ap_s2tree ap_p2s ap_t2s ap_t2parts ap_normali \ ap_1adr ap_util ap_lex ap_lxtable ap_dmflip \ ap_s2p adrparse td norm parse ghost OBJECTS = ap_normali.o ap_s2tree.o ap_p2s.o ap_t2s.o ap_t2parts.o \ ap_1adr.o ap_util.o ap_lex.o ap_lxtable.o ap_dmflip.o \ ap_s2p.o adrparse.o LIBSRCS = ap_s2tree.c ap_p2s.c ap_t2s.c ap_t2parts.c ap_normali.c \ ap_1adr.c ap_util.c ap_lex.c ap_lxtable.c ap_dmflip.c \ ap_s2p.c adrparse.c real-default: ../addr-made ../addr-made: $(OBJECTS) $(AR) r ../libmmdf.a $(OBJECTS) -touch ../addr-made -@echo "address parser routines built normally" parse: parse.o fullparse.o $(MMDFLIBS) cc $(LDFLAGS) -o parse parse.o fullparse.o $(MMDFLIBS) $(SYSLIBS) td: td.o $(MMDFLIBS) cc $(LDFLAGS) -o td td.o $(MMDFLIBS) $(SYSLIBS) norm: norm.o $(MMDFLIBS) cc $(LDFLAGS) -o norm norm.o $(MMDFLIBS) $(SYSLIBS) ghost: ghost.o $(MMDFLIBS) cc $(LDFLAGS) -o ghost ghost.o $(MMDFLIBS) $(SYSLIBS) lint: $(LINT) -Caddr $(LFLAGS) $(LIBSRCS) clean: -rm -f parse td norm ghost *.o x.c makedep eddep \ make.out errs llib-laddr* # DO NOT DELETE THIS LINE -- make depend uses it ap_s2tree.o: ap_s2tree.c ap_s2tree.o: ../../h/util.h ap_s2tree.o: ../../h/ap.h ap_s2tree.o: ../../h/ll_log.h ap_p2s.o: ap_p2s.c ap_p2s.o: ../../h/util.h ap_p2s.o: ../../h/conf.h ap_p2s.o: ../../h/ch.h ap_p2s.o: ../../h/ap.h ap_p2s.o: ../../h/dm.h ap_p2s.o: ../../h/ll_log.h ap_t2s.o: ap_t2s.c ap_t2s.o: ../../h/util.h ap_t2s.o: ../../h/conf.h ap_t2s.o: ../../h/ch.h ap_t2s.o: ../../h/ap.h ap_t2s.o: ../../h/dm.h ap_t2s.o: ../../h/ll_log.h ap_t2parts.o: ap_t2parts.c ap_t2parts.o: ../../h/util.h ap_t2parts.o: ../../h/ap.h ap_t2parts.o: ../../h/ll_log.h ap_normali.o: ap_normali.c ap_normali.o: ../../h/util.h ap_normali.o: ../../h/conf.h ap_normali.o: ../../h/ll_log.h ap_normali.o: ../../h/ch.h ap_normali.o: ../../h/ap.h ap_normali.o: ../../h/ap_norm.h ap_normali.o: ../../h/dm.h ap_1adr.o: ap_1adr.c ap_1adr.o: ../../h/util.h ap_1adr.o: ../../h/conf.h ap_1adr.o: ../../h/ap.h ap_1adr.o: ../../h/ap_lex.h ap_1adr.o: ../../h/ll_log.h ap_util.o: ap_util.c ap_util.o: ../../h/util.h ap_util.o: ../../h/ap.h ap_lex.o: ap_lex.c ap_lex.o: ../../h/util.h ap_lex.o: ../../h/ap.h ap_lex.o: ../../h/ap_lex.h ap_lxtable.o: ap_lxtable.c ap_lxtable.o: ../../h/ap_lex.h ap_lxtable.o: ../../h/util.h ap_lxtable.o: ../../h/conf.h ap_dmflip.o: ap_dmflip.c ap_dmflip.o: ../../h/util.h ap_dmflip.o: ../../h/conf.h ap_dmflip.o: ../../h/ch.h ap_dmflip.o: ../../h/dm.h ap_dmflip.o: ../../h/ll_log.h ap_s2p.o: ap_s2p.c ap_s2p.o: ../../h/ap_lex.h ap_s2p.o: ../../h/util.h ap_s2p.o: ../../h/ap.h adrparse.o: adrparse.c adrparse.o: ../../h/util.h adrparse.o: ../../h/mmdf.h adrparse.o: ../../h/ap.h td.o: td.c td.o: ../../h/util.h td.o: ../../h/mmdf.h td.o: ../../h/ch.h td.o: ../../h/dm.h norm.o: norm.c norm.o: ../../h/util.h norm.o: ../../h/mmdf.h norm.o: ../../h/ap.h norm.o: ../../h/ch.h parse.o: parse.c parse.o: ../../h/ap_lex.h parse.o: ../../h/util.h parse.o: ../../h/mmdf.h parse.o: ../../h/ap.h ghost.o: ghost.c ghost.o: ../../h/util.h ghost.o: ../../h/mmdf.h ghost.o: /usr/include/pwd.h ghost.o: ../../h/ch.h ghost.o: ../../h/dm.h ghost.o: ../../h/ap.h # DEPENDENCIES MUST END AT END OF FILE # IF YOU PUT STUFF HERE IT WILL GO AWAY # see make depend above (retval = 0;;) { /* retval is count of comment nesting */ switch (ap_lxtable[*lexptr++ = c = ap_char ()]) { case LT_LPR: /* nested comments mmdf/lib/addr/ap_lxtable.c 444 0 12 6126 3620510362 10712 #include "ap_lex.h" #include "util.h" #include "conf.h" /* $Header: ap_lxtable.c,v 1.3 84/09/05 21:31:52 dpk Exp $ */ /* $Log: ap_lxtable.c,v $ * Revision 1.3 84/09/05 21:31:52 dpk * Added #include of util.h * * Revision 1.2 83/11/18 17:05:31 reilly * Steve Kille's version * */ /* mappings of lexical symbols to ascii values for address parser */ char ap_lxtable[] = { LT_EOD, LT_ERR, LT_ERR, LT_ERR, /* 000-003 nul */ LT_ERR, LT_ERR, LT_ERR, LT_ERR, /* 004-007 */ LT_LTR, LT_SPC, LT_SPC, LT_ERR, /* 010-013 bs tab lf */ LT_ERR, LT_ERR, LT_ERR, LT_ERR, /* 014-017 */ LT_ERR, LT_ERR, LT_ERR, LT_ERR, /* 020-023 */ LT_ERR, LT_ERR, LT_ERR, LT_ERR, /* 024-027 */ LT_ERR, LT_ERR, LT_ERR, LT_ERR, /* 030-033 */ LT_ERR, LT_ERR, LT_ERR, LT_ERR, /* 034-037 */ LT_SPC, LT_XTR, LT_QOT, LT_XTR, /* 040-043 sp ! " # */ #ifdef JNTMAIL /* In JNT domain treat % as @ */ LT_XTR, LT_AT, LT_XTR, LT_XTR, #else LT_XTR, LT_XTR, LT_XTR, LT_XTR, /* 044-047 $ % & ' */ #endif LT_LPR, LT_RPR, LT_XTR, LT_XTR, /* 050-053 ( ) * + */ LT_COM, LT_XTR, LT_XTR, LT_XTR, /* 054-057 , - . / */ LT_NUM, LT_NUM, LT_NUM, LT_NUM, /* 060-063 0 1 2 3 */ LT_NUM, LT_NUM, LT_NUM, LT_NUM, /* 014-067 4 5 6 7 */ LT_NUM, LT_NUM, LT_COL, LT_SEM, /* 070-073 8 9 : ; */ LT_LES, LT_XTR, LT_GTR, LT_XTR, /* 074-077 < = > ? */ LT_AT, LT_LTR, LT_LTR, LT_LTR, /* 100-103 @ A B C */ LT_LTR, LT_LTR, LT_LTR, LT_LTR, /* 014-107 D E F G */ LT_LTR, LT_LTR, LT_LTR, LT_LTR, /* 110-114 H I J K */ LT_LTR, LT_LTR, LT_LTR, LT_LTR, /* 115-117 L M N O */ LT_LTR, LT_LTR, LT_LTR, LT_LTR, /* 120-123 P Q R S */ LT_LTR, LT_LTR, LT_LTR, LT_LTR, /* 124-127 T U V W */ LT_LTR, LT_LTR, LT_LTR, LT_LSQ, /* 130-133 X Y Z [ */ LT_SQT, LT_RSQ, LT_XTR, LT_XTR, /* 134-137 \ ] ^ _ */ LT_XTR, LT_LTR, LT_LTR, LT_LTR, /* 140-143 ` a b c */ LT_LTR, LT_LTR, LT_LTR, LT_LTR, /* 144-147 d e f g */ LT_LTR, LT_LTR, LT_LTR, LT_LTR, /* 150-153 h i j k */ LT_LTR, LT_LTR, LT_LTR, LT_LTR, /* 154-157 l m n o */ LT_LTR, LT_LTR, LT_LTR, LT_LTR, /* 160-163 p q r s */ LT_LTR, LT_LTR, LT_LTR, LT_LTR, /* 164-167 t u v w */ LT_LTR, LT_LTR, LT_LTR, LT_XTR, /* 170-173 x y z { */ LT_XTR, LT_XTR, LT_XTR, LT_ERR, /* 174-177 | } ~ del */ }; t.o: ghost.c ghost.o: ../../h/util.h ghost.o: ../../h/mmdf.h ghost.o: /usr/include/pwd.h ghost.o: ../../h/ch.h ghost.o: ../../h/dm.h ghost.o: ../../h/ap.h # DEPENDENCIES MUST END AT END OF FILE # IF YOU PUT STUFF HERE IT WILL GO AWAY # see make depend above (retval = 0;;) { /* retval is count of comment nesting */ switch (ap_lxtable[*lexptr++ = c = ap_char ()]) { case LT_LPR: /* nested comments mmdf/lib/addr/ap_1adr.c 444 0 12 35046 3671073141 10136 #include "util.h" #include "conf.h" #include "ap.h" #include "ap_lex.h" /* ADDRESS PARSER, as per: "Standard for the Format of ARPA Network Text Messages", D. Crocker, J. Vittal, K. Pogran & D. Henderson, in ARPANET PROTOCOL HANDBOOK, E. Feinler & J. Postel (eds), NIC-7104-REV-1, Network Information Center, SRI International: Menlo Park, Ca. (1978) (NTIS AD-A0038901). and "Standard for the Format of Arpa Internet Text Messages", Revised by D. Crocker, RFC #822, in INTERNET PROTOCOL TRANSITION WORKBOOK, Feinler & J. Postel (eds), Network Information Center, SRI International: Menlo Park, Ca. (March 1982). A parsed address is normalized to have routing references placed into rfc822-type positioning. History: Fall 1977 Bruce Borden: Initial Design & Coding Summer 1979 Dave Crocker: Completed it & fixed bugs Major reorganization & re-labeling Minor changes to semantics Documentation Sept 81 Andy Knutsen case STPREND -> STPHRSE, to allow comments afterwards Sept 81 Dave Crocker generalized the use of STPHRSE, so that STOKEND occurs only on comma or eof. changed again, to cycle only accepting comments Nov 82 Dave Crocker Converted to accept 822 syntax, while trying also to juggle 733... Aug 83 Steve Kille Fix to STEPER This module is the real parser, tho it is intended to be co-routined with a caller who has something specific to do with ap_1adr's output. This is organized so as to make some attempt at limiting core consumption. Fullparse(), however, simply causes a full parse tree to be built up in core. The implementation deviates somewhat from the above specification. Deviations and the use of the package are discussed in the companion documentation. The parser's behavior is fairly straightforward. A singly-linked flat list of labelled (lexical) nodes is built up. Ap_1adr() is used to get the next "address segment", which consists of all of the lexical nodes up to the end of the next host-phrase (i.e., usually that means up to the next comma, semi-colon, or right-angle bracket). The caller is responsible for initializing state variables, via ap_init(), and linking together or using ap_1adr's output segments. Note that ap_1adr does NOT interpret address text to cause re-directed file input. The caller must do that. Ap_pshfil() and ap_popfil() can be used to save and restore the parse state and acquire the named file. The provision for this stacking, given the co-routining, is the reason state information is chained through global variables, rather than being saved on local (stack) variables. The amount of input processed on a single call may seem strange. The idea is to try to guess the most common use of the routine. This is presumed to be for address checking, in which acquisition of the MBOX and DOMAIN text are most important, with the rest actually being thrown away. It is, of course, possible for the core-limiting heuristic to lose if a ridiculous number of groups and personal lists are specified in a particular way. I am assuming that won't happen. /**/ #define STITER 0 #define STINIT 1 #define STECMNT 2 #define STEDONE 3 /* Returned when addresses were NOT found */ #define STEOK 4 /* Returned when addresses were found */ #define STEBAD 5 #define STPHRSE 6 #define STSTPER 7 #define STEPER 8 #define STEGRP 9 #define STDTYPE 10 #define STEDTYPE 11 #define STDOMAIN 12 #define STEDOMAIN 13 int ap_intype = AP_733; /* default to RFC #733 input */ int ap_outtype = AP_822; /* default to RFC #822 output */ /* with little endian domains */ int ap_grplev = 0; /* Group nesting depth */ int ap_perlev = 0; /* <> nesting depth */ int ap_routing; /* parsing a route */ #ifdef DEBUG #include "ll_log.h" extern LLog *logptr; extern AP_ptr ap_sqinsert (); extern char *strdup(); #endif #if DEBUG > 1 char debug; /* True if in debug mode */ char *statnam[] = { "Iterate", "Init", "CmntEnd", "DoneEnd", "OKEnd", "BadEnd", "Phrase", "Persstrt", "PersEnd", "GrpEnd", "DTypNam", "DTypeE", "Domain", "DomainE" }; char *ptrtab[] = { "Nil", "Etc", "Nxt" }; char *typtab[] = { "Nil", "Name", "MBox", "Domain", "DataType", "Comment", "Word", "Person", "NPersn", "EPersn", "Group", "NGroup", "EGroup", "DomainLit" }; #endif /**/ ap_1adr () { struct ap_node basenode; AP_ptr ap_sp; /* Saved ap node ptr */ AP_ptr r822ptr, r733prefptr; int got822; char buf[LINESIZE]; register int state; #if DEBUG > 1 if (debug) (void) putchar ('\n'); #endif ap_routing = DONE; ap_ninit (&basenode); ap_sqinsert (&basenode, APP_ETC, ap_pstrt); for (state = STINIT, got822 = FALSE, r733prefptr = (AP_ptr) 0; ;){ #if DEBUG > 1 if (debug) printf ("=>%d (%s)", state, (state >= 0 && state <= 13) ? statnam[state] : "BOGUS!"); #endif switch (state) { case STITER: /* Iteration to get real address */ ap_palloc (); state = STINIT; /* just DROP ON THROUGH */ case STINIT: /* START of parse; empty node */ ap_sp = ap_pcur; switch (ap_lex (buf)){ case LV_WORD: ap_pfill (APV_WORD, buf); state = STPHRSE; break; case LV_AT: if (!got822){ got822 = TRUE; r822ptr = ap_pcur; } ap_routing = OK; #if DEBUG > 1 if (debug) printf ("(routing)"); #endif state = STDOMAIN; break; case LV_COLON: /* DATA TYPE start */ state = STDTYPE; break; case LV_SEMI: /* GROUP LIST end */ state = STEGRP; break; case LV_GRTR: /* PERSONAL LIST end */ state = STEPER; break; case LV_LESS: /* allow one angle-bracket, here */ case LV_FROM: /* FILE source */ ap_pfill (APV_DTYP, "Include"); state = STITER; break; case LV_COMMENT: ap_pfill (APV_CMNT, buf); state = STITER; break; case LV_COMMA: break; /* ignore null addresses */ case LV_EOD: if (ap_perlev != 0 || ap_grplev != 0) state = STEBAD; else state = STEDONE; break; default: state = STEBAD; break; } continue; /* *************************** ENDING ********************************* */ case STECMNT: /* accept comments until end */ switch (ap_lex (buf)) { case LV_COMMENT: ap_pfill (APV_CMNT, buf); break; /* just cycle, accepting comments */ case LV_COMMA: state = STEOK; break; case LV_EOD: state = STEOK; break; default: state = STEBAD; break; } continue; case STEDONE: /* END clean; NO empty nodes? */ #if DEBUG > 1 if (debug) (void) putchar ('\n'); #endif ap_7to8 (r733prefptr, r822ptr); return (DONE); case STEOK: /* END clean */ #if DEBUG > 1 if (debug) (void) putchar ('\n'); #endif ap_7to8 (r733prefptr, r822ptr); return (OK); case STEBAD: /* END error */ #if DEBUG > 1 if (debug) (void) putchar ('\n'); #endif ap_clear(); /* Experimental, DPK, 7 Aug 84 */ return (NOTOK); /* ********************* GATHER A PHRASE **************************** */ case STPHRSE: /* PHRASE continuation; empty node */ switch (ap_lex (buf)) { case LV_WORD: /* append word to phrase, maybe */ ap_padd (APV_WORD, buf); break; case LV_AT: /* MAILBOX (name) end */ if (!got822) r822ptr = ap_pcur; ap_sqtfix (ap_sp, ap_pcur, APV_MBOX); ap_palloc (); state = STDOMAIN; break; case LV_LESS: /* PERSON NAME end */ state = STSTPER; break; case LV_COLON: /* GROUP NAME end */ if (ap_grplev++ >= 1 && ap_intype == AP_822) { /* may not be nested */ #if DEBUG > 1 if (debug) printf ("(intype=%d,ap_grplev=%d)", ap_intype, ap_grplev); #endif state = STEBAD; break; } ap_sqtfix (ap_sp, ap_pcur, APV_GRUP); ap_sp -> ap_obtype = APV_NGRP; state = STITER; break; case LV_SEMI: ap_sqtfix (ap_sp, ap_pcur, APV_MBOX); ap_sp -> ap_obtype = APV_MBOX; state = STEGRP; break; case LV_GRTR: state = STEPER; break; case LV_COMMA: ap_sqtfix (ap_sp, ap_pcur, APV_MBOX); state = STEOK; break; case LV_EOD: ap_sqtfix (ap_sp, ap_pcur, APV_MBOX); state = STEOK; break; case LV_COMMENT: ap_pappend (APV_CMNT, buf); break; default: state = STEBAD; break; } continue; /* *********************** ADDRESS LISTS **************************** */ case STSTPER: /* PERSONAL address list; NO empty node */ if (ap_perlev++ > 0 && ap_intype == AP_822) { /* may not be nested */ #if DEBUG > 1 if (debug) printf ("(intype=%d,ap_perlev=%d)", ap_intype, ap_perlev); #endif state = STEBAD; break; } ap_routing = OK; ap_sqtfix (ap_sp, ap_pcur, APV_PRSN); ap_sp -> ap_obtype = APV_NPER; state = STITER; continue; case STEPER: if (--ap_perlev < 0) { #if DEBUG > 1 if (debug) printf ("(ap_perlev=%d)", ap_perlev); #endif state = STEBAD; break; } ap_pappend (APV_EPER, (char *) 0); ap_palloc (); /* SEK add storage */ state = STECMNT; /* allow comments, etc */ continue; case STEGRP: if (--ap_grplev < 0) { #if DEBUG > 1 if (debug) printf ("(ap_grplev=%d)", ap_grplev); #endif state = STEBAD; break; } ap_pappend (APV_EGRP, (char *) 0); state = STECMNT; continue; /* ************************** DATA TYPE ******************************* */ case STDTYPE: /* DATA TYPE name; empty node */ if (ap_intype == AP_822) { /* data types not legal in 822 */ #if DEBUG > 1 if (debug) printf ("(intype=%d)", ap_intype); #endif state = STEBAD; break; } if (ap_lex (buf) != LV_WORD) { state = STEBAD; continue; } ap_pfill (APV_DTYP, buf); state = STEDTYPE; /* Just drop on through */ case STEDTYPE: /* DATA TYPE name end; empty node */ state = (ap_lex (buf) == LV_COLON) ? STITER : STEBAD; continue; /* ************************* DOMAIN *********************************** */ case STDOMAIN: /* DOMAIN/Host; NO empty parse node */ switch (ap_lex (buf)) { default: state = STEBAD; continue; case LV_COMMENT: ap_pappend (APV_CMNT, buf); continue; case LV_DLIT: ap_pappend (APV_DLIT, buf); state = STEDOMAIN; continue; case LV_WORD: ap_pfill (APV_DOMN, buf); state = STEDOMAIN; } /* just drop on through */ case STEDOMAIN: /* DOMAIN end; NO empty parse node */ switch (ap_lex (buf)) { case LV_AT: /* sequence of HOST's => @ separation */ /* SEK correct here */ if (r733prefptr == (AP_ptr) 0) r733prefptr = ap_pcur; ap_palloc (); /* node which points to first routing ref */ state = STDOMAIN; break; case LV_SEMI: state = STEGRP; break; case LV_GRTR: state = STEPER; break; case LV_COMMA: if (ap_routing != DONE) state = STITER; else state = STEOK; break; case LV_EOD: if (ap_routing != DONE || ap_perlev != 0 || ap_grplev != 0) state = STEBAD; else state = STEOK; break; case LV_COMMENT: ap_pappend (APV_CMNT, buf); break; case LV_COLON: if (ap_routing != DONE) { ap_routing = DONE; state = STITER; continue; } /* else DROP ON THROUGH */ default: state = STEBAD; break; } continue; } } } LOCFUN ap_7to8 (r733prefptr, r822ptr) AP_ptr r733prefptr, r822ptr; { AP_ptr routbase; AP_ptr ap; char *perneeded; if (r733prefptr == (AP_ptr) 0) return; /* don't have to move it to 822 style */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "733 ref"); #endif if (ap_pstrt -> ap_obtype == APV_MBOX) { perneeded = strdup (r822ptr -> ap_obvalue); ap_pappend (APV_EPER, (char *) 0); } else perneeded = (char *) 0; routbase = ap_alloc (); /* move the sequence to newroute */ while (r733prefptr -> ap_ptrtype != APP_NIL && r733prefptr -> ap_ptrtype != APP_NXT && r733prefptr -> ap_chain != (AP_ptr) 0) { switch (r733prefptr -> ap_chain -> ap_obtype) { default: /* only move domain info */ goto endit; case APV_CMNT: if (routbase -> ap_chain != (AP_ptr) 0) { /* try to append, not prepend, comments */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "comment appended'%s'", r733prefptr -> ap_chain -> ap_obvalue); #endif ap_move (routbase -> ap_chain, r733prefptr); continue; } /* else DROP ON THROUGH */ case APV_DLIT: case APV_DOMN: #ifdef DEBUG ll_log (logptr, LLOGFTR, "val='%s'", r733prefptr -> ap_chain -> ap_obvalue); #endif ap_move (routbase, r733prefptr); continue; } } endit: /* treatment here depends on whether we have an */ /* 822 route already. Note that 822 pointer is*/ /* NOT easy, as an easy pointer was too hard! */ /* SEK need to copy first part of route */ /* to r822ptr */ if (r822ptr -> ap_obtype != APV_DOMN && r822ptr -> ap_obtype != APV_DLIT) { ap_insert (r822ptr, APP_ETC, ap_new (r822ptr -> ap_obtype, r822ptr -> ap_obvalue)); free (r822ptr -> ap_obvalue); ap_fllnode (r822ptr, routbase -> ap_chain -> ap_obtype, routbase -> ap_chain -> ap_obvalue); ap_sqinsert (r822ptr, APP_ETC, routbase -> ap_chain -> ap_chain); /* add it before local-part */ ap_free (routbase); ap_free (routbase -> ap_chain); } else { ap = r822ptr; while (ap -> ap_chain -> ap_obtype == APV_DOMN || ap -> ap_chain -> ap_obtype == APV_DLIT) ap = ap -> ap_chain; ap_sqinsert (ap, APP_ETC, routbase -> ap_chain); } if (perneeded != 0) /* SEK need to kludge person name */ { ap_insert (r822ptr, APP_ETC, ap_new (r822ptr -> ap_obtype, r822ptr -> ap_obvalue)); free (r822ptr -> ap_obvalue); ap_fllnode (r822ptr, APV_NPER, perneeded); free (perneeded); } } TEBAD; continue; /* ************************* DOMAIN *********************************** */ case STDOMAIN: /* DOMAIN/Host; NO empty parse node */ switch (ap_lex (buf)) { default: state = STEBAD; continue; case LV_COMMENT: ap_pappend (APV_CMNT, buf); continue; case LV_DLIT: ap_pappend (APV_DLIT, buf); state = STEDOMAIN; continue; case LV_WORD: ap_pfill (APV_DOMN, buf); state = STEDOMAIN; } mmdf/lib/addr/ap_dmflip.c 444 0 12 1660 3671073142 10536 #include "util.h" #include "conf.h" #include "ch.h" #include "dm.h" #include "ll_log.h" extern char *strdup(); extern char *malloc(); extern struct ll_struct *logptr; char * ap_dmflip (buf) char * buf; { char tbuf [LINESIZE]; char *cp; int argc; char *argv [DM_NFIELD]; int i; #ifdef DEBUG ll_log (logptr, LLOGBTR, "Flipping domain '%s'", buf); #endif if ((buf[0] == '"') || (buf[0] == '[')) /*, don't flip if quoted or dlit */ return (strdup (buf)); (void) strcpy (tbuf, buf); argc = cstr2arg (tbuf, DM_NFIELD, argv, '.'); cp = malloc (strlen (buf) + 1); if (cp == (char *) 0) { ll_log (logptr, LLOGTMP, "dm_flip - malloc failure"); return ((char *) 0); } (void) strcpy (cp, argv [argc - 1]); for (i = argc - 2; i >= 0; i--) { strcat (cp, "."); strcat (cp, argv [i]); } #ifdef DEBUG ll_log (logptr, LLOGFTR, "Flipped domain is '%s'", cp); #endif return (cp); } route already. Note that 822 pointer is*/ /* NOT easy, as an easy pointer mmdf/lib/addr/ap_normali.c 444 0 12 36443 3671073144 10755 #include "util.h" #include "conf.h" #include "ll_log.h" #include "ch.h" #include "ap.h" #include "ap_norm.h" #include "dm.h" /* Normalize a parse tree, to fill-in host references, etc. * * Returns: 0 if ok * -1 if error */ extern LLog *logptr; extern struct ap_hstab *ap_exhstab; /* translation table */ extern char *locname; extern char *locdomain; extern char *strdup (); extern char *multcat (); Domain *dm_v2route(); /**/ AP_ptr ap_normalize (dflhost, dfldomain, thetree, exorchan) char *dflhost, /* string to append, as host name */ *dfldomain; /* string to append, as domain name */ AP_ptr thetree; /* the parse tree */ Chan *exorchan; /* The channel to exorcise with respect to */ { struct ap_node basenode; /* first node in routing chain */ AP_ptr r822prefptr, perptr, mbxprefptr, dmprefptr, lstcmntprefptr, lastptr, grpptr, ap; struct ap_hstab *tabp; char official[128]; char *save; char *strp; #ifdef DEBUG ll_log (logptr, LLOGBTR, "ap_normalize ()"); ap_t2s (thetree, &strp); ll_log (logptr, LLOGFTR, "normalize input: %s", strp); free (strp); #endif ap_ninit (&basenode); ap_sqinsert (&basenode, APP_ETC, thetree); /* make sure that 'pref' ptrs are easy */ ap_ptinit (&basenode, &perptr, &r822prefptr, &mbxprefptr, &dmprefptr, &lstcmntprefptr, &lastptr, &grpptr); /* set some pointers */ /* SEK - lts try replacing this section */ /* It may have marginal use */ if (perptr == (AP_ptr) 0 && grpptr == (AP_ptr) 0 && lstcmntprefptr != (AP_ptr) 0) { /* no person name or group, so use last comment */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "comment -> person name"); #endif ap_insert (&basenode, APP_ETC, ap_new (APV_NPER, lstcmntprefptr -> ap_chain -> ap_obvalue)); if (mbxprefptr == &basenode) /* first node was local-part */ mbxprefptr = basenode.ap_chain; ap_append (lastptr, APV_EPER, (char *) 0); #ifdef DEBUG ap_t2s (basenode.ap_chain, &strp); ll_log (logptr, LLOGFTR, "comment result: %s", strp); free (strp); #endif } /* * Remember "list: ;"... -DPK- */ if (mbxprefptr == (AP_ptr) 0) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "No mailbox!?!"); #endif return (basenode.ap_chain); } /* Normalize all refs in source route */ if (r822prefptr != (AP_ptr) 0) { ap = r822prefptr; FOREVER { switch (ap -> ap_ptrtype) { case APP_NIL: case APP_NXT: break; case APP_ETC: ap = ap -> ap_chain; if(ap == (AP_ptr)0) break; switch (ap -> ap_obtype) { case APV_DOMN: if(ap_dmnormalize (ap, exorchan) == MAYBE) return( (AP_ptr)MAYBE); case APV_DLIT: case APV_CMNT: continue; } } break; } } #ifndef JNTMAIL /* * SEK if JNT mail, % is treated as lexically equivalent * to @, and CSNET style routes ignored. Might accept * CSNET routes if someone wants this... */ if (dmprefptr == (AP_ptr) 0) { /* no domain, so add default and leave */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "no domain"); #endif ap_locnormalize (&basenode, &r822prefptr, &mbxprefptr, &dmprefptr); #ifdef DEBUG ap_t2s (basenode.ap_chain, &strp); ll_log (logptr, LLOGFTR, "locnorm result: %s", strp); free (strp); #endif } #endif not JNTMAIL if (dmprefptr != (AP_ptr) 0) { switch(ap_dmnormalize (dmprefptr -> ap_chain, exorchan)) { case MAYBE: return ( (AP_ptr)MAYBE); case OK:; #ifdef DEBUG ll_log (logptr, LLOGFTR, "Local reference"); #endif #ifndef JNTMAIL ap_locnormalize (&basenode, &r822prefptr, &mbxprefptr, &dmprefptr); #ifdef DEBUG ap_t2s (basenode.ap_chain, &strp); ll_log (logptr, LLOGFTR, "locnorm result: %s", strp); free (strp); #endif #endif not JNTMAIL } } if (dmprefptr == (AP_ptr) 0 && r822prefptr == (AP_ptr) 0) { /* no host references anywhere */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "mbx = %o, mbx->typ = %o, mbx->ch = %o", mbxprefptr, mbxprefptr->ap_obtype, mbxprefptr->ap_chain); if (mbxprefptr != (AP_ptr) 0) ll_log (logptr, LLOGFTR, "mbx->ch->ty = %o", mbxprefptr->ap_chain->ap_obtype); #endif if (mbxprefptr == (AP_ptr) 0 || mbxprefptr -> ap_chain -> ap_obtype == APV_NGRP || mbxprefptr -> ap_chain -> ap_obtype == APV_GRUP) /* Yuck. */ return (basenode.ap_chain); if (!isstr(dflhost)) { sprintf (official, "%s.%s", locname, locdomain); ap_append (mbxprefptr -> ap_chain, APV_DOMN, official); #ifdef DEBUG ll_log (logptr, LLOGFTR, "appending %s domain (local)", official); #endif } else { if (!isstr(dfldomain)) (void) strcpy (official, dflhost); else sprintf (official, "%s.%s", dflhost, dfldomain); ap_append (mbxprefptr -> ap_chain, APV_DOMN, official); #ifdef DEBUG ll_log (logptr, LLOGFTR, "appending %s domain", official); #endif if(ap_dmnormalize (mbxprefptr -> ap_chain -> ap_chain, exorchan) == MAYBE) return( (AP_ptr)MAYBE); } return (basenode.ap_chain); } #ifdef DEBUG ll_log (logptr, LLOGFTR, "exorcise special host refs"); #endif /* SEK change the dreaded exorcising */ /* so as only to apply to the host */ /* directly connected to. It is */ /* absurd to do this to the middle of*/ /* a source route */ if (r822prefptr != (AP_ptr) 0) ap = r822prefptr -> ap_chain; else ap = dmprefptr -> ap_chain; for (tabp = ap_exhstab; tabp -> name != (char *) 0; tabp++) if (lexequ (tabp -> name, ap -> ap_obvalue)) { /* Yes, perform holy rites */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "exorcising: %s -> %%%s@%s", ap -> ap_obvalue, tabp -> dot, tabp -> at); #endif if (isstr (tabp -> dot)) { free (ap -> ap_obvalue); ap -> ap_obvalue = strdup (tabp -> at); save = mbxprefptr -> ap_chain -> ap_obvalue; mbxprefptr -> ap_chain -> ap_obvalue = multcat ( mbxprefptr -> ap_chain -> ap_obvalue, "%", tabp -> dot, (char *)0); } else { save = ap -> ap_obvalue; ap -> ap_obvalue = strdup (tabp -> at); } free (save); return (basenode.ap_chain); } /* * We do not have a specific exorcising rule. See if the * host is known to this channel, and if not, exocise it anyway. * * (I think this will work, closer examination welcome. -DPK-) * * SEK - * only play this game if ch_known is specified. * to allow it to be turned off * * assume that the table is a channel table form * and thus we need the first host name */ if (exorchan != NULL && exorchan -> ch_known != NULL) { Dmn_route route; char dm_buf[LINESIZE]; Domain *lrval; int irval; lrval = dm_v2route (ap -> ap_obvalue, dm_buf, &route); if(lrval == (Domain *)MAYBE) return( (AP_ptr)MAYBE); else if(lrval == (Domain *)NOTOK) { ll_log (logptr, LLOGTMP, "failure to reconvert domain (%s)", ap-> ap_obvalue); return (basenode.ap_chain); } /* SEK this might need beefing up to cope */ /* with unknown subdomains */ irval = tb_k2val(exorchan->ch_known, TRUE, route.dm_argv[0], official); if(irval == MAYBE) return( (AP_ptr)MAYBE); else if(irval == NOTOK) { if (r822prefptr == (AP_ptr) 0) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "exorcising: %s -> %%%s@%s", ap -> ap_obvalue, route.dm_argv[0], exorchan -> ch_lname); #endif save = mbxprefptr -> ap_chain -> ap_obvalue; mbxprefptr -> ap_chain -> ap_obvalue = multcat ( mbxprefptr->ap_chain->ap_obvalue, "%", route.dm_argv[0], (char *)0); free (save); free (dmprefptr -> ap_chain -> ap_obvalue); dmprefptr -> ap_chain -> ap_obvalue = multcat (exorchan->ch_lname, ".", exorchan->ch_ldomain, (char *)0); } else { #ifdef DEBUG ll_log (logptr, LLOGFTR, "adding exorcise component to route"); #endif /* SEK note ap points to r822prefptr - chain */ ap_sqinsert (ap, APP_ETC, ap_new (ap -> ap_obtype, ap -> ap_obvalue)); free (ap -> ap_obvalue); ap -> ap_obvalue = multcat (exorchan->ch_lname, ".", exorchan->ch_ldomain, (char *)0); } } } return (basenode.ap_chain); } /**/ LOCFUN ap_ptinit (baseprefptr, perptr, r822prefptr, mbxprefptr, dmprefptr, lstcmntprefptr, lastptr, grpptr) AP_ptr baseprefptr; AP_ptr *perptr, *r822prefptr, *mbxprefptr, *dmprefptr, *lstcmntprefptr, *lastptr, *grpptr; { AP_ptr ap; #ifdef DEBUG ll_log (logptr, LLOGFTR, "ap_ptinit ()"); #endif *perptr = *r822prefptr = *mbxprefptr = *dmprefptr = *lstcmntprefptr = *lastptr = *grpptr = (AP_ptr) 0; /* SEK need switch here to catch */ /* leadin mbox or domn */ switch (baseprefptr -> ap_chain -> ap_obtype) { case APV_MBOX: *mbxprefptr = baseprefptr; break; case APV_DLIT: case APV_DOMN: *r822prefptr = baseprefptr; } for (ap = baseprefptr -> ap_chain; ap -> ap_obtype != APV_NIL; ap = ap -> ap_chain) { *lastptr = ap; #ifdef DEBUG ll_log (logptr, LLOGFTR, "val '%s'", ap -> ap_obvalue); #endif switch (ap -> ap_obtype) { case APV_NPER: #ifdef DEBUG ll_log (logptr, LLOGFTR, "perptr"); #endif *perptr = ap; break; case APV_NGRP: #ifdef DEBUG ll_log (logptr, LLOGFTR, "grpptr"); #endif *grpptr = ap; break; } if (ap -> ap_ptrtype == APP_NXT) break; if (ap -> ap_ptrtype == APP_NIL) break; if (ap -> ap_chain == (AP_ptr) 0) break; switch (ap -> ap_chain -> ap_obtype) { case APV_CMNT: #ifdef DEBUG ll_log (logptr, LLOGFTR, "gotcmnt"); #endif *lstcmntprefptr = ap; break; /** DPK@BRL **/ case APV_WORD: /** DPK@BRL **/ #ifdef DEBUG ll_log (logptr, LLOGFTR, "gotword"); #endif if (*mbxprefptr != (AP_ptr) 0) break; case APV_MBOX: #ifdef DEBUG ll_log (logptr, LLOGFTR, "gotmbxpref"); #endif *mbxprefptr = ap; /* one before the mbox */ break; case APV_DLIT: case APV_DOMN: if (*r822prefptr == (AP_ptr) 0 && *mbxprefptr == (AP_ptr) 0) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "got r822prefptr"); #endif *r822prefptr = ap; } else if ((*dmprefptr == (AP_ptr) 0) && (*mbxprefptr != (AP_ptr) 0)) { /* SEK need mailbox befor domain */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "gotdmprefptr"); #endif *dmprefptr = ap; } break; } } #ifdef DEBUG if (*lastptr == (AP_ptr) 0) ll_log (logptr, LLOGFTR, "no lastptr"); else ll_log (logptr, LLOGFTR, "lastptr '%s'", (*lastptr) -> ap_obvalue); #endif } /**/ #ifndef JNTMAIL ap_locnormalize (obaseptr, or822prefptr, ombxprefptr, odmprefptr) AP_ptr obaseptr, *or822prefptr, *ombxprefptr, *odmprefptr; { /* tear local-part apart */ struct ap_node basenode; AP_ptr curptr; AP_ptr r822prefptr, perptr, mbxprefptr, dmprefptr, lstcmntprefptr, lastptr, grpptr; char *cptr; #ifdef DEBUG ll_log (logptr, LLOGBTR, "ap_locnormalize ()"); #endif /* SEK need fixex here to normalize any new */ /* domain references */ #ifdef DONE_IN_SUBMIT if( ! ((*ombxprefptr) -> ap_obtype == APV_DTYP && /* protect files */ lexequ((*ombxprefptr) -> ap_obvalue, "include"))) for (cptr = (*ombxprefptr) -> ap_chain -> ap_obvalue; *cptr != '\0'; cptr++) if (*cptr == '.' || *cptr == '%') *cptr = '@'; #endif #ifdef DEBUG ll_log (logptr, LLOGBTR, "parsing '%s'", (*ombxprefptr) -> ap_chain -> ap_obvalue); #endif ap_ninit (&basenode); ap_sqinsert (&basenode, APP_ETC, ap_s2tree ((*ombxprefptr) -> ap_chain -> ap_obvalue)); #ifdef DEBUG ap_t2s (obaseptr ->ap_chain, &cptr); ll_log (logptr, LLOGFTR, "parse result: %s", cptr); free (cptr); #endif ap_ptinit (&basenode, &perptr, &r822prefptr, &mbxprefptr, &dmprefptr, &lstcmntprefptr, &lastptr, &grpptr); if (dmprefptr != (AP_ptr) 0) /* actually have some stuff */ { free ((*ombxprefptr) -> ap_chain -> ap_obvalue); (*ombxprefptr) -> ap_chain -> ap_obvalue = strdup (mbxprefptr -> ap_chain -> ap_obvalue); /* replace old reference */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "newlocal '%s'", (*ombxprefptr) -> ap_chain -> ap_obvalue); #endif if (r822prefptr != (AP_ptr) 0 || *odmprefptr != (AP_ptr) 0) if (*or822prefptr == (AP_ptr) 0) { #ifdef DEBUG ap_t2s (obaseptr ->ap_chain, &cptr); ll_log (logptr, LLOGFTR, "odm move result: %s", cptr); free (cptr); #endif *or822prefptr = obaseptr; } /* initialize route pointer */ if (*odmprefptr == (AP_ptr) 0) *odmprefptr = mbxprefptr -> ap_chain; else /* get rid of old domain reference */ { ap_move (*or822prefptr, *odmprefptr); #ifdef DEBUG ap_t2s (obaseptr ->ap_chain, &cptr); ll_log (logptr, LLOGFTR, "odm move result: %s", cptr); free (cptr); #endif } if (r822prefptr != (AP_ptr) 0) { /* put new chain at end of old */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "adding new routing to end info"); #endif for (curptr = *or822prefptr; curptr -> ap_chain -> ap_obtype == APV_DOMN || curptr -> ap_chain -> ap_obtype == APV_DLIT || curptr -> ap_chain -> ap_obtype == APV_CMNT; curptr = curptr -> ap_chain); ap_sqmove (curptr, r822prefptr, APV_DOMN); #ifdef DEBUG ap_t2s (obaseptr ->ap_chain, &cptr); ll_log (logptr, LLOGFTR, "routing move result: %s", cptr); free (cptr); #endif } ap_move (*odmprefptr, dmprefptr); #ifdef DEBUG ap_t2s (obaseptr ->ap_chain, &cptr); ll_log (logptr, LLOGFTR, "dm move result: %s", cptr); free (cptr); #endif } } #endif not JNTMAIL /**/ ap_dmnormalize (dmptr, thechan) register AP_ptr dmptr; Chan *thechan; { Dmn_route dmnroute; char official [LINESIZE]; char buf[LINESIZE]; Domain *domain; #ifdef DEBUG ll_log (logptr, LLOGBTR, "ap_dmnormalize ()"); #endif domain = dm_v2route (dmptr -> ap_obvalue, official, &dmnroute); switch ((int)domain) { /* do we know this reference? */ case NOTOK: /* unknown host */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "unknown domain (ap_normalize): '%s'", dmptr -> ap_obvalue); #endif switch ((int)ch_nm2struct (dmptr -> ap_obvalue)) { /* explicit channel routing? */ default: /* it IS a channel name */ return (OK); /* return an analyse local part */ /* leave channel ref in address */ case NOTOK: ll_log (logptr, LLOGFST, "nonexistent dom/host (ap_normalize): '%s'", dmptr -> ap_obvalue); return (NOTOK); } case MAYBE: return(MAYBE); default: sprintf (buf, "%s.%s", domain -> dm_lname, domain -> dm_domain); /* SEK might be better to get dm_v2routem*/ /* to do this */ if (lexequ (official, buf)) { if (thechan == NULL) sprintf (official, "%s.%s", locname, locdomain); else sprintf (official, "%s.%s", thechan -> ch_lname, thechan -> ch_ldomain); } } #ifdef DEBUG ll_log (logptr, LLOGFTR, "Route = '%s', Official name = '%s'", dmnroute.dm_buf, official); #endif free (dmptr -> ap_obvalue); dmptr -> ap_obvalue = strdup (official); return (7); /* anything other than OK / NOTOK */ } ap_obvalue, "include"))) for (cptr = (*ombxprefptr) -> ap_chain -> ap_obvalue; *cptr != '\0'; cptr++) if (*cptr == '.' || *cptr == '%') *cptr = '@'; #endif #ifdef DEBUG ll_log (logptr, LLOGBTR, "parsinmmdf/lib/addr/td.c 444 0 12 2462 3620510364 7207 #include "util.h" #include "mmdf.h" #include "ch.h" #include "dm.h" extern LLog *logptr; main () { char adr [LINESIZE]; char result [LINESIZE]; Dmn_route rbuf; Dmn_route *route = &rbuf; Domain dbuf; Domain *dmn = &dbuf; Chan *tchan; mmdf_init ("TEST-DM"); siginit (); logptr -> ll_level = LLOGFTR; while (TRUE) { int i; printf ("\ngive test string (q to quit):"); scanf ("%s", adr); if (lexequ (adr, "q")) { printf ("quitting\n"); exit(0); } printf ("received: '%s'\n", adr); printf ("dm_v2route (%s) gives: ",adr); dmn = dm_v2route (adr, result, route); if (dmn == (Domain *) NOTOK) { printf ("Error \n\n\n"); } else { printf ("%s \n", dmn->dm_show); printf ("domain in full is '%s'\n", result); printf ("Route: '%s'\n", route->dm_buf); for (i=0; i <= route->dm_argc; i++) printf (" Component (%d): '%s'\n", i, route->dm_argv[i]); } fflush (stdout); for (i=1;;i++) { if ((tchan = ch_h2chan (adr, i)) == (Chan *) NOTOK) { printf ("ch_h2chan returns NOTOK\n"); break; } else { if (tchan == (Chan *) OK) printf ("local reference (%d)\n", i); else printf ("ch_h2chan (%d) gives channel '%s'\n", i, tchan -> ch_show); } } printf ("\n\n"); } } err_abrt () { printf ("aborting\n"); } ptr ->ap_chain, &cptr); ll_log (logptr, LLOGFTR, "odm move result: %s", cptr); free (cptr); #endif } if (r822prefptr != (AP_ptr) 0) { /* put new chain at end of old mmdf/lib/addr/ghost.c 444 0 12 10425 3671073145 7751 #include "util.h" #include "mmdf.h" #include <pwd.h> #include "ch.h" #include "dm.h" #include "ap.h" extern struct ll_struct *logptr; extern char *ch_dflnam; extern char *blt(); extern char *index(); extern char *rindex(); extern char *locname; extern char *supportaddr; extern char *sitesignature; extern char *locmachine; /* local machine name */ extern int mgt_inalias; char *adr_fulldmn; /* Name of 'full' domain */ char *adr_orgspec; /* original mailbox string */ LOCVAR char adr_gotone; char *adr_ghost (); main () { char instr[1000]; char thename[1000]; char * result; char tstline[1000]; char newloc[1000]; mmdf_init ("GHOST"); logptr -> ll_level = LLOGFTR ; printf ("Locname '%s', Locmachine '%s'\n\n", locname, locmachine); while (1) { printf ("Enter the string to parse: "); fflush(stdout); scanf ("%s", instr); while (1) { result = adr_ghost(instr, (short) strlen(instr), thename); if (result == (char *) NOTOK ) { printf ("Error returned by 'adr_ghost' for '%s'", instr); fflush(stdout); goto done; } else { printf ("Host is '%s' ", thename); fflush(stdout); printf ("Pointer at '%s'\n", result); fflush (stdout); if (lexequ(thename, locmachine)) { printf ("Let's try again \n") ; fflush (stdout); continue ; } /* keep going until we're out of names or we find one */ /* Now, we should have a host left now. Let's see if it exists */ if (dm_h2domain (thename, tstline) == (Domain *) NOTOK) { printf ("Lossage trying to get '%s' in the tables\n", thename); fflush(stdout); goto done; } else { *result = '\0'; /* terminate the new "local" part */ printf ("Local is '%s' and Domain is '%s'\n", instr, tstline); fflush(stdout); } } } /* end WHILE (1) */ } done: printf ("Bye\n"); fflush(stdout); } /**/ char * adr_ghost (buf, len, to) /* get host field from end of text */ char *buf; /* the buffer holding the text */ short len; /* length of the buffer */ char *to; /* put "hostname" into here */ { extern char *compress (); register char *strptr; int hostlen; char *hostptr; char *endptr; #ifdef DEBUG ll_log (logptr, LLOGFTR, "adr_ghost (%s)", buf); #endif for (strptr = &(buf[len - 1]); isspace (*strptr); strptr--); /* skip trailing white space */ for (hostptr = endptr = strptr;; hostptr = strptr--) { if (strptr <= buf) return ((char *) NOTOK); /* reached beginning of string */ switch (*strptr) { case '>': /* a "foo bar <adr @ host>" spec */ *strptr = '\0'; for (strptr = buf; ; strptr++) switch (*strptr) { case '\0': return ((char *) NOTOK); case '<': compress (++strptr, buf); return (adr_ghost (buf, strlen (buf), to)); } case ')': /* trailing comment field */ { short pcount = 1; while (--strptr > buf && pcount) { if (*strptr == '(') pcount--; if (*strptr == ')') pcount++; if (pcount == 0) { while (strptr > buf && isspace (*--strptr)); *++strptr = '\0'; break; } } } continue; default: continue; case '@': case '%': /* same as @ */ case '.': /* same as @ */ break; case ' ': case '\t': for (strptr--; (strptr > buf) && isspace (*strptr); strptr--); switch (*strptr) /* got one now */ { case '@': case '%': case '.': break; default: /* " at "?? */ if (equal (&strptr[-1], "AT", 2)) strptr--; else /* tsk tsk */ return ((char *) NOTOK); } break; } break; } if (to) { hostlen = endptr - hostptr; if (hostlen == 0) (void) strcpy (to, locname); else { *(blt (hostptr, to, hostlen + 1)) = '\0'; compress (to, to); if (strlen (to) == 0) /* null host field */ { /* default to local host */ (void) strcpy (to, locname); } } } while (isspace (*--strptr)); strptr++; /* leave pointer just AFTER graphics */ return (strptr); } /**/ /* explicit channel routing? */ default: /* it IS a channel name */ return (OK); /* return an analyse local part */ /* leave channel ref in address */ case NOTOK: ll_log (logptrmmdf/lib/addr/adrparse.c 444 0 12 2350 3671073146 10405 /* ** ** R E V I S I O N H I S T O R Y ** ** 03/08/85 LDK Added compress() in this module to remove ** VAK leading and trailing space. ** */ #include "util.h" #include "mmdf.h" #include "ap.h" extern char *ap_s2p(); extern char *compress(); parsadr (thestr, name, mbox, host) register char *thestr; /* string with an address */ char *name, /* where to put name part */ *mbox, /* where to put mailbox part */ *host; /* where to put hostname part */ { register char *cp; AP_ptr treep, namep, local, domain; if (name != (char *) 0) *name = '\0'; if (mbox != (char *) 0) *mbox = '\0'; if (host != (char *) 0) *host = '\0'; cp = ap_s2p (thestr, &treep, (AP_ptr *)0, &namep, &local, &domain, (AP_ptr *)0); if (cp == (char *) DONE || cp == (char *) NOTOK) return; else if (local -> ap_obvalue == NULL) return; /* Bad format */ if (name && namep != 0 && namep -> ap_obvalue != 0) (void) strcpy (name, namep->ap_obvalue); if (mbox && local != 0 && local -> ap_obvalue != 0) (void) strcpy (mbox, local->ap_obvalue); if (host && domain != 0 && domain -> ap_obvalue != 0) (void) strcpy (host, domain->ap_obvalue); ap_free (treep); return; } compress (); register char *strptr; int hostlen; char *hostptr; char *endptr; #ifdef DEBUG ll_log (logptr, LLOGFTR, "adr_ghost (%s)", buf); #endif for (strptr = &(buf[len - 1]); isspace (*strptr); strptr--); /* skip trailing white space */ for (hostptr =mmdf/lib/addr/Notes 444 0 12 21067 3623253340 7472 .TH ADDRESS 3.mmdf 'ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i .SH NAME RFC733 Address Parser (ap_) .SH SYNOPSIS .nf #include "util.h" #include "ap.h" struct ap_node { char ap_obtype, /* parse-type of object */ APV_NIL APV_NAME /* personal name */ APV_MBOX /* mailbox-part of addr */ APV_NODE /* host-part of address */ APV_DTYP /* "data-type" */ APV_CMNT /* comment (...) */ APV_WORD /* generic word */ APV_PRSN /* start of personal list <...> */ APV_NPER /* name of person */ APV_EPER /* end of personal list */ APV_GRUP /* start of group list x:..; */ APV_NGRP /* name of group */ APV_EGRP /* end of group list */ ap_ptrtype; /* "role" of the next node */ APP_NIL /* no next node */ APP_ETC /* part of this address */ APP_NXT /* start of new address */ char *ap_obvalue; /* string value of object */ struct ap_node *ap_chain; /* pointer to next node */ }; typedef AP_ptr struct ap_node * .fi .PD 0 .SS "NODE PRIMITIVES" .IP "ap_alloc() -> AP_ptr" 25 Allocate a list node element .IP "ap_free(AP_ptr)" 25 Free a list node elelment .IP "ap_fllnode(AP_ptr, obtype, obval)" 25 Fill-in node's data fields .IP "ap_new(obtype, obval) -> AP_ptr" 25 Alloc and fill .SS "LIST MANIPULATION" .IP "ap_insert(cur-AP_ptr, new-AP_ptr)" 25 Add new node after current one .IP "ap_delete(AP_ptr)" 25 Remove node after current one .IP "ap_append(AP_ptr, obtype, obvalue) -> AP_ptr" 25 Alloc, fill, and insert after current node .IP "ap_add(AP_ptr, obtype, obvalue) -> AP_ptr" 25 Try to add to data field of current node, else append .IP "ap_sqdelete(strt-AP_ptr, end-AP_ptr)" 25 Remove sequence of nodes from one after starting node through ending one, or until end of chain. .IP "ap_1delete(strt-AP_ptr)" 25 Remove sequence of nodes from one after starting node until end of current address (ptrtype==APP_NXT) or until end of chain. .IP "ap_sqtfix(strt-AP_ptr, end-AP_ptr, obtype)" 25 Fix object type of sequence of nodes from starting node to ending one .SS "PARSE STATE" .IP "AP_ptr ap_pstrt" 25 Pointer to first parse node .IP "AP_ptr ap_pcur" 25 Pointer to current(last) node .IP "ap_init(gfunc)" 25 Context initialization .IP "ap_ppush(gfunc)" 25 Save parse context, init .IP "char (*gfunc) ()" 25 .IP "ap_ppop()" 25 Restore parse context .IP "ap_fpush(filename) -> NOTOK/OK" 25 Ppush, init for new input file .IP "ap_fpop()" 25 Close file and ppop .SS "PARSE LIST" .PP .RS These echo the List Manipulation primitives, always relative to ap_pcur; any insert updates ap_pcur. .RE .IP "ap_palloc()" 25 Alloc, insert at end of chain .IP "ap_pfill()" 25 Fill-in values at end of chain .IP "ap_pnsrt(new-AP_ptr)" 25 Insert at end of chain .IP "ap_pappend(obtype, obvalue)" 25 .IP "ap_padd(obtype, obvalue)" 25 .br .SS "PARSING FUNCTIONS" .IP "ap_pinit(&getc()) -> AP_ptr" 25 Init, set for input from function getc(), alloc initial node, and store address in ap_pstrt and ap_pcur .IP "ap_1adr() -> NOTOK/OK/DONE" Parse next address, through host reference .PD .bp .SH DESCRIPTION .PP This package provides routines for parsing text address strings and producing an in-core linked list of the parsed elements. Routines also are provided for manipulation of the list. .PP The syntax of addresses is a superset of the official syntax used for ArpaNet mail, specified in "Standard for the Format of ARPA Network Text Messages", D. Crocker, J. Vittal, K. Pogran & D. Henderson, in ARPANET PROTOCOL HANDBOOK, E. Feinler & J. Postel (eds), NIC-7104-REV-1, Network Information Center, SRI International: Menlo Park, Ca. (1978) (NTIS AD-A0038901). .PP The parser does not interpret the data values, such as the validity of a local mailbox. Nor does it act on specific values. For example, the syntax permits referring to addresses which are in another file. The parser detects the specification, but does not retrieve the contents of the file. However, .I ap_fpush(filename) and .I ap_fpop() are provided for handling most of the overhead for this. .SS List Manipulation .PP The routines for manipulating the address linked-list are loosely divided into five groups. The first performs dynamic storage management for list nodes, having allocate, free, and data value fill-in calls. .I ap_new() simply combines allocate and fill-in. .PP The second group performs standard linked-list operations of insertion and deletion. The list is singly-linked, with all operations performed relative to a leading node. Hence, the deletion call specifies the node which .I precedes the one that is to be removed. For convenience, .I ap_append() will create, fill-in, and insert a node. As an efficiency mechanism, it often is possible to combine the data values of contiguous parse nodes, if the nodes are of the same object type. .I ap_add() performs the necessary data concatenation, if possible; if the node types are not the same, then it will just do an append of a new node. .PP Also within this group are three routines for manipulating a sequence of list nodes. .I ap_sqdelete() performs a series of list deletions, from the node immediately following the start node, until an ending node (or until the list is exhausted). .I ap_1delete() is identical to .I ap_sqdelete(), except that it terminates at the end of a completed address, much like .I ap_1adr(). That is, it terminates when it has deleted the last element before a new address begins, as indicated by a APP_NXT pointer. The routine .I ap_sqtfix() will scan a list sub-sequence and modify the ap_obtype field. This is primarily used by the parser, since the syntax does not resolve the type of some parse elements until an indeterminate amount of parsing has been done (e.g., personal name or group name fields, versus mailbox string of an unnamed address). .PP The third group keeps track of the parse state. The two global variables, ap_pstrt and ap_pcur, point to the beginning and end (current) of the linked list. .I ap_init() initializes these context variables and set the pointer for a character-input function. The push and pop calls are used to start a new parse context (e.g., to expand a mailing list) and then return to the old one. Two extra push and pop calls are used for acquiring file input; the push call requires the name of the input file, rather than a pointer to a input function. .PP The fourth group of functions are used by the parser to build the linked list. They are essentially the same as the primitives in the first group, but their actions are relative to ap_pcur and will update ap_pcur, when an insertion is done. Also, .I ap_palloc() differs by automatically inserting the new node at the end of the list. .SS Parsing Routines .PP The final group performs the actual parsing. The routine .I ap_pinit(gfunc) is used to initialize the system for input parsing; this includes allocating and setting ap_pstrt and ap_pcur to point to a first list node. Its argument is the address of the function which acquires address characters, one at a time, and should return -1 on end of input. .I ap_1adr() performs the basic parsing, building a parse tree, relative to the global pointer, ap_pcur. As it scans the input, it adds nodes after ap_pcur and updates it. The routine returns a status value to indicate its termination condition. NOTOK means there was a parsing error; OK means a full address was acquired; and DONE signals end of input. .PP .I ap_1adr() returns on encountering an end of input or a comma which separates addresses. Fully parsing the input, before returning, would tend to overflow storage. .I ap_1adr() therefore tries to reduce the storage requirement by returning when it has parsed enough to be a complete unit for most applications. The caller has the option of simply repeating the call to the routine, until an end of input, thereby building a full in-core parse tree. (The directory containing the package's sources has a routine, called fullparse.c, which will parse an entire input stream and build the list for it.) Elements of one address are linked with a ap_ptrtype of APP_ETC. When a new address is started, the link is APP_NXT. .SH FILES .IP "addr/ap.h" 25 Include file .SH AUTHORS .PD 0 .IP "< 1978 B. Borden" 25 Wrote initial version of parser code .IP "78-80 D. Crocker" 25 Reworked parser into current form .IP "Apr 81 K. Harrenstein" 25 Hacked for SRI .IP "Jun 81 D. Crocker" 25 Back in the fold. Finished V7 conversion, minor cleanups, and repackaging of list primitives. Documentation. .PD linked, with all operations performed relative to a leading node. Hence, the deletion call specifies the node which .I precedes the one that is to be removed. For convenience, .I ap_append() will create, fill-in, and insert a node. As an efficiency mechanism, it often is possible to combine the data values of contiguous parse nodes, if the nodes are of the same object type. .I ap_add() performs the necessary data concatenation, if possible; if the node mmdf/lib/mmdf/ 755 0 12 0 3671074614 6367 mmdf/lib/mmdf/qu_io.c 444 0 12 27735 3671073147 7770 #include "util.h" #include "mmdf.h" /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ /* QU_IO: Channel interaction with Deliver */ /* Sep, 80 Dave Crocker fix qu_minit, to allow no return address * Nov, 80 Dave Crocker convert to V7 * Mar, 81 John Pickens add seek arg to qu_rsinit, add qu_fileno * Dec, 82 Doug Kingston added definition of hd_init() as a long. * Mar, 84 Dennis Rockwell updated for 4.2 (s_io) */ #include <sys/stat.h> #include "adr_queue.h" #include "ch.h" #include "phs.h" #include "ap.h" /*#define RUNALON */ extern LLog *logptr; extern long hd_init(); extern char *strdup(); extern char *index(); extern char *mquedir; extern int ap_outtype; /* 733 or 822 */ extern FILE *hd_fmtfp; /* handle on hd_format massaged header */ char *qu_msgfile; /* PUBLIC INFO: path to text file */ long qu_msglen; /* byte count of message */ LOCVAR int qu_nadrs; /* number of addressees */ /* some versions of stdio do not handle simultaneous reads and writes, so * we must have two handles on it. we can play this game only because the * access characteristics are fairly constrained. */ LOCVAR FILE *qu_rfp, /* address list read handle */ *qu_wfp; /* address list write handle */ LOCVAR Chan *qu_chptr; /* channel we are running as */ LOCVAR long qu_seek; /* beginning of unmassaged body */ LOCVAR int qu_txfd; /* msg text file file descriptor */ LOCVAR char qu_hdr; /* take from massaged header */ LOCVAR struct rp_construct rp_nxerr = { RP_FIO, 'U', 'n', 'a', 'b', 'l', 'e', ' ', 't', 'o', ' ', 'o', 'p', 'e', 'n', ' ', 'n', 'e', 'x', 't', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 'f', 'i', 'l', 'e', '\0' } , rp_rrcerr = { RP_LIO, 'q', 'u', '_', 'r', 'd', 'r', 'e', 'c', ' ', 'p', 'i', 'p', 'e', ' ', 'e', 'r', 'r', 'o', 'r', '\0' } , rp_rsterr = { RP_FIO, 'q', 'u', '_', 'r', 'd', 's', 't', 'm', ' ', 'f', 'i', 'l', 'e', ' ', 'e', 'r', 'r', 'o', 'r', '\0' } , rp_tsterr = { RP_NS, 'N', 'a', 'm', 'e', 's', 'e', 'r', 'v', 'e', 'r', ' ', 'T', 'i', 'm', 'e', 'o', 'u', 't', '\0' }; /* **************** (qu_) DELIVER I/O SUB_MODULE ***************** */ qu_init (argc, argv) /* get ready to process Deliver mail */ int argc; /* NOTE: other modules have no args */ char *argv[]; { #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_init"); #endif if ((qu_chptr = ch_nm2struct (argv[0])) == (Chan *) NOTOK) { ll_log (logptr, LLOGTMP, "chan '%s' unknown", argv[0]); qu_chptr = (Chan *) 0; } #ifdef RUNALON qu_rfp = fdopen (0, "r"); qu_wfp = fdopen (1, "w"); qu_msgfile = strdup ("tmp"); qu_txfd = open ("tmp", 0); qu_sender = strdup ("dcrocker"); domsg = TRUE; #else RUNALON if (argc < 3) { ll_log (logptr, LLOGTMP, "Channel invoked with too few arguments (%d)", argc); return (RP_NO); } #ifdef DEBUG ll_log (logptr, LLOGFTR, "'%s' ('%s','%s')", argv[0], argv[1], argv[2]); #endif /* fdopen input fd to get addresses from * and output fd to return responses */ if ( !isdigit (*argv[1]) || (qu_rfp = fdopen (atoi (argv[1]), "r")) == NULL || !isdigit (*argv[2]) || (qu_wfp = fdopen (atoi (argv[2]), "w")) == NULL ) { ll_log (logptr, LLOGTMP, "Invalid arguments given to channel"); return (RP_NO); } if( argv[3] && index(argv[3], 'w') ) domsg = TRUE; #endif RUNALON return (RP_OK); } qu_end (type) /* done performing Deliver function */ int type; /* ignore type of termination */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_end"); #endif #ifdef RUNALON return (RP_OK); #endif if (qu_wfp != (FILE *) EOF && qu_wfp != NULL) fclose (qu_wfp); qu_rfp = qu_wfp = (FILE *) EOF; return (RP_OK); } qu_pkinit () /* get ready to receive mail */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_pkinit"); #endif return (RP_OK); } qu_pkend () /* done receiving mail */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_pkend"); #endif return (RP_OK); } /**/ qu_minit (msgfile, dohdr) /* set up for next message */ char msgfile[]; /* name of message to be processed */ int dohdr; /* perform address massaging */ { struct stat statbuf; /* for getting the message size */ char linebuf[LINESIZE]; #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_minit (%s, dohdr=%d)", msgfile, dohdr); #endif sprintf (linebuf, "%s%s", mquedir, msgfile); qu_msgfile = strdup (linebuf); if (qu_txfd > 0) (void) close (qu_txfd); /* reset file descriptor */ if ((qu_txfd = open (qu_msgfile, 0)) == NOTOK) { ll_err (logptr, LLOGFAT, "Unable to open message file '%s'.", qu_msgfile); qu_wrply ((RP_Buf *) &rp_nxerr, rp_conlen (rp_nxerr)); return (RP_FIO); } qu_seek = 0L; fstat (qu_txfd, &statbuf); qu_msglen = st_gsize (&statbuf); /* size of basic message */ qu_nadrs = 0; qu_hdr = FALSE; #ifdef DEBUG ll_log (logptr, LLOGBTR, "basic msglen = %D", qu_msglen); #endif if (dohdr != AP_SAME) /* fix up the addresses */ { ap_outtype = dohdr; qu_seek = hd_init (qu_chptr, qu_fileno ()); #ifdef DEBUG ll_log (logptr, LLOGFTR, "qu_seek = %ld", qu_seek); #endif if(qu_seek == (long) MAYBE){ qu_wrply ((RP_Buf *) &rp_tsterr, rp_conlen (rp_tsterr)); return (RP_NS); } if (qu_seek != 0L) { qu_hdr = TRUE; /* fix message length count, by subtracting size of original * header and adding size of new one. */ qu_msglen -= qu_seek; fstat (fileno (hd_fmtfp), &statbuf); qu_msglen += st_gsize (&statbuf); #ifdef DEBUG ll_log (logptr, LLOGBTR, "modified msglen = %D, qu_seek = %ld", qu_msglen, qu_seek); #endif } } return (RP_OK); } qu_mend () /* end of current message */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_mend"); #endif if (qu_seek > 0L) qu_hdr = FALSE; hd_end (); /* get rid of massaged data */ if (qu_txfd >= 0) (void) close (qu_txfd); /* free unneeded fd's */ qu_txfd = NOTOK; if (qu_msgfile != NULL) free (qu_msgfile); qu_msgfile = NULL; return (RP_OK); } /* ************ TELL DELIVER OF RESULT ****************** */ qu_wrply (valstr, len) /* pass a reply to local process */ RP_Buf *valstr; /* structure containing reply */ int len; /* length of the structure */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_wrply()"); ll_log (logptr, LLOGFTR, "(%s)'%s'", rp_valstr (valstr -> rp_val), valstr -> rp_line); #endif switch (valstr -> rp_val) { /* do msg stats for typical channels */ case RP_AOK: qu_nadrs++; break; case RP_MOK: if (qu_nadrs == 0) /* addresses weren't batched */ qu_nadrs++; /* => one addr/msg */ phs_msg (qu_chptr, qu_nadrs, qu_msglen); qu_nadrs = 0; /* reset */ break; } return (qu_wrec ((char *) valstr, len)); } /* BASIC I/O WITH DELIVER */ qu_rrec (linebuf, len) /* read a record from Deliver */ char *linebuf; /* where to stuff the address */ int *len; /* where to stuff address length */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_rrec ()"); #endif #ifdef RUNALON qu_wrec ("qu_rrec: ", 10); #endif switch (*len = gcread (qu_rfp, linebuf, LINESIZE - 1, "\000\n\377")) { case NOTOK: ll_err (logptr, LLOGFAT, rp_rrcerr.rp_cline); qu_wrply ((RP_Buf *) &rp_rrcerr, rp_conlen (rp_rrcerr)); return (RP_LIO); case OK: /* closed the pipe */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_rrec (EOF)"); #endif return (RP_EOF); case 1: if (isnull (linebuf[0]) || linebuf[0] == '\n') { #ifdef DEBUG ll_log (logptr, LLOGBTR, "DONE"); #endif return (RP_DONE); /* the only valid one-char records */ } ll_err (logptr, LLOGFAT, "1-char record ('%c')", linebuf[0]); return (RP_PARM); /* bad parameter */ } *len -= 1; linebuf[*len] = '\0'; /* so it can be lloged as a string */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "(%d)\"%s\"", *len, linebuf); #endif return (RP_OK); } /**/ qu_rsinit (theseek) /* initialize stream (text) position */ long theseek; { extern long lseek (); #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_rsinit (%ld)", theseek); #endif if ((theseek == 0L) && (qu_seek > 0L)) { lseek (qu_txfd, qu_seek, 0); hd_minit (); qu_hdr = TRUE; } else lseek (qu_txfd, theseek, 0); } qu_fileno () /* return fd for message text file */ { return (qu_txfd); } qu_rstm (buffer, len) /* read next part of message stream */ char *buffer; /* where to stuff next part of text */ int *len; /* where to stuff length of part */ { int want = *len - 1; #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_rstm ()"); #endif if (qu_hdr) { /* take from massaged header */ switch (*len = hd_read (buffer, want)) { case NOTOK: qu_hdr = FALSE; goto fioerr; case OK: #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_rstm (hdr DONE)"); #endif qu_hdr = FALSE; /* start taking from regular file */ break; /* drop down to file read */ default: buffer[*len] = '\0'; #ifdef DEBUG ll_log (logptr, LLOGFTR, "\"%s\"", buffer); #endif return (RP_OK); } } switch (*len = read (qu_txfd, buffer, want)) { /* raw read of the file */ case NOTOK: fioerr: ll_err (logptr, LLOGFAT, rp_rsterr.rp_cline); qu_wrply ((RP_Buf *) &rp_rsterr, rp_conlen (rp_rsterr)); return (RP_FIO); case 0: /* end of message; not treated as EOF */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_rstm (DONE)"); #endif return (RP_DONE); case 1: if (isnull (buffer[0])) return (RP_DONE); /* Drop Through */ default: buffer[*len] = '\0'; /* so it can be ll_loged as a string */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "\"%s\"", buffer); #endif return (RP_OK); } /* NOTREACHED */ } /**/ qu_wrec (str, len) /* write a record to Deliver */ char *str; int len; { #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_wrec() (%d)\"%s\"", len, str ? str : ""); #endif if (str != 0) fnput (str, len, qu_wfp); (void) putc ('\0', qu_wfp); /* null is record terminator */ if (fflush (qu_wfp) == NOTOK) return (RP_LIO); return (RP_OK); } typical channels */ case RP_AOK: mmdf/lib/mmdf/ch_struct.c 444 0 12 4163 3664425134 10617 #include "util.h" #include "mmdf.h" #include "ch.h" /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ extern Chan **ch_tbsrch; #ifdef DEBUG extern LLog *logptr; #endif /* ************* (ch_) ACCESS CHANNEL DESCRIPTIONS ***************** */ Chan *ch_nm2struct (name) /* give pointer to structure for chan */ register char *name; /* string name of chan */ { register Chan **chanptr; #ifdef DEBUG ll_log(logptr, LLOGBTR, "ch_nm2struct(%s)",name); #endif for (chanptr = ch_tbsrch; *chanptr != 0; chanptr++) if (lexequ (name, (*chanptr) -> ch_name)) return (*chanptr); /* return addr of chan struct entry */ return ((Chan *)NOTOK); } Chan *ch_qu2struct (name) /* give pointer to structure for chan */ register char *name; /* internal queue name */ { register Chan **chanptr; #ifdef DEBUG ll_log(logptr, LLOGBTR, "ch_qu2struct(%s)",name); #endif for (chanptr = ch_tbsrch; *chanptr != 0; chanptr++) if (lexequ (name, (*chanptr) -> ch_queue)) return (*chanptr); /* return addr of chan struct entry */ return ((Chan *) NOTOK); } qu_hdr = FALSE; /* start taking from regular file */ break; /* drop down to file read */ default: buffer[*len] = '\0'; #ifdef DEBUG ll_log (logptr, LLOGFTR, "\"%s\"", buffer); #endif return (RP_OK); } } switch (*len = read (qu_txfd, buffer, want)) { /* raw read of the file */ case NOTOK: fmmdf/lib/mmdf/qu_rdmail.c 444 0 12 11727 3671073150 10615 #include "util.h" #include "mmdf.h" /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ /* DM_IO: Channel interaction with Deliver */ /* */ /* Nov, 80 Dave Crocker convert to V7 */ #include "adr_queue.h" extern char *strdup(); extern char *strncpy(); extern LLog *logptr; LOCVAR char *qu_host, /* to store 1st adr of msg */ *qu_adr, qu_info[2]; LOCVAR struct rp_construct rp_dmiook = { RP_OK, '\0' }; /* ************* (qu_) DELIVER MAIL-READING SUB-MODULE ************* */ qu_rinit (info, retadr, dohdr) /* ready to get a message */ char *info; /* where to stuff init ifno */ char *retadr; /* where to stuff return address */ int dohdr; /* massage address headers */ { int len; int retval; char *arglist[20]; char host[LINESIZE], adr[LINESIZE], linebuf[LINESIZE]; #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_rinit (dohdr=%d)", dohdr); #endif if (rp_isbad (retval = qu_rrec (linebuf, &len))) return (retval); /* get message initialization info */ str2arg (linebuf, 20, arglist, (char *) 0); if (lexequ ("end", arglist[0])) return (RP_DONE); if (!lexequ ("msg", arglist[0])) { ll_log (logptr, LLOGFAT, "qu_rinit err reading new msg: '%s'", arglist[0]); return (RP_PARM); } (void) strncpy (retadr, arglist[2], ADDRSIZE-1); if (rp_isbad (retval = qu_minit (arglist[1], dohdr))) return (retval); /* open the message text */ qu_wrply ((RP_Buf *) &rp_dmiook, rp_conlen (rp_dmiook)); if (rp_isgood (retval = qu_radr (host, adr))) { if (!isnull (host[0])) qu_host = strdup (host); qu_adr = strdup (adr); (void) strcpy (info, qu_info); } return (retval); } qu_radr (host, adr) /* read next address from Deliver */ char *host, /* where to put name of next host */ *adr; /* where to put rest of address */ { int len; short retval; char linebuf[LINESIZE]; char *arglist[20]; #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_radr"); #endif if (qu_adr != 0) /* fake read of 1st address */ { (void) strncpy (adr, qu_adr, ADDRSIZE-1); free (qu_adr); qu_adr = 0; if (qu_host != 0) { (void) strncpy (host, qu_host, ADDRSIZE-1); free (qu_host); qu_host = 0; } else host[0] = '\0'; #ifdef DEBUG ll_log (logptr, LLOGFTR, "1st msg/call: '%s'@'%s'", adr, host); #endif return (RP_OK); } retval = qu_rrec (linebuf, &len); if (rp_gval (retval) != RP_OK) return (retval); str2arg (linebuf, 20, arglist, (char *) 0); if (lexequ ("aend", arglist[0])) return (RP_DONE); if (lexequ ("hend", arglist[0])) return (RP_HOK); if (!lexequ ("addr", arglist[0])) { ll_log (logptr, LLOGFAT, "qu_radr err reading addr: '%s'", arglist[0]); return (RP_PARM); } (void) strncpy (host, arglist[1], ADDRSIZE-1); (void) strncpy (adr, arglist[2], ADDRSIZE-1); /* copy host & address parts */ qu_info[0] = 'm'; #ifdef DEBUG ll_log (logptr, LLOGFTR, "('%s'@'%s')", adr, host); #endif return (RP_OK); } /**/ qu_rtinit (theseek) /* initialize for reading text */ long theseek; /* position in message to start at */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_rtinit (%ld)", theseek); #endif return (qu_rsinit (theseek)); /* => stream init */ } qu_rtxt (buffer, len) /* read next portion of msg text */ char *buffer; /* where to stuff buffer (not line) */ int *len; /* where to stuff len (BUF not LINE) */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "qu_rtxt"); #endif return (qu_rstm (buffer, len)); } qu_rend () { return( qu_mend()); } uf; /* where to stuff thmmdf/lib/mmdf/mm_io.c 444 0 12 26600 3671073152 7736 /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ /* mm_io: low-level i/o for local mail submission mail */ /* May, 80 Dave Crocker lm_chend, move kill before closes * Jun, 80 Dave Crocker lm_chinit, to make submit reply channel * separate from standard output * mm_cinit, make regfdaray-setting loop tmp<=NOTOK */ #include "nexec.h" #include "util.h" #include "mmdf.h" #define LM_SUBMIT 0 #define LM_PICKUP 1 extern struct ll_struct *logptr; extern int *regfdary; /* nexecl's fd rearrangement array */ extern char *cmddfldir; extern char *pathsubmit, /* program to invoke for submission */ *namsubmit, /* name of it */ *pathpkup, /* program to invoke for pickup */ *nampkup; /* name of it */ extern int numfds; LOCVAR FILE *mm_rfp, /* pipe input stream */ *mm_wfp; /* pipe output stream */ LOCVAR int mm_cid; /* process id of child mail process */ /* ************ (mm_) LOCAL MAIL I/O SUB-MODULE ****************** */ mm_init () /* get ready for local mail processing */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_init ()"); #endif return (RP_OK); } mm_end (type) /* done with mail process */ int type; { #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_end ()"); #endif mm_cend (type); /* get rid of child */ return (RP_OK); } mm_sbinit () /* initialize local submission */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_sbinit ()"); #endif return (mm_cinit ((char *) 0)); /* indicate not a pickup */ } mm_sbend () /* done with submission */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_sbend ()"); #endif return (mm_cend (OK)); } mm_pkinit (chname) /* initialize local pickup */ char chname[]; /* name of channel being picked up */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_pkinit ()"); #endif return (mm_cinit (chname)); } mm_pkend () /* done with pickup */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_pkend ()"); #endif return (mm_cend (OK)); } /* PROCESS REPLIES */ mm_rrply (valstr, len) /* get a reply from remote process */ struct rp_bufstruct *valstr; /* where to stuff copy of reply text */ int *len; /* where to indicate text's length */ { short retval; char *rplystr; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_rrply()"); #endif retval = mm_rrec ((char *) valstr, len); if (rp_gval (retval) != RP_OK) return (retval); #ifdef RUNALON switch (rp_gval (valstr -> rp_val)) { case 'd': valstr -> rp_val = RP_DONE; break; case 'y': valstr -> rp_val = RP_OK; break; case 'm': valstr -> rp_val = RP_MOK; break; case 'a': valstr -> rp_val = RP_AOK; break; case 'n': valstr -> rp_val = RP_NO; break; } #endif rplystr = rp_valstr (valstr -> rp_val); if (*rplystr == '*') { /* replyer did a no-no */ ll_log (logptr, LLOGTMP, "ILLEGAL REPLY: (%s)", rplystr); valstr -> rp_val = RP_RPLY; } #ifdef DEBUG else ll_log (logptr, LLOGFTR, "(%s)'%s'", rplystr, valstr -> rp_line); #endif return (RP_OK); } mm_wrply (valstr, len) /* pass a reply to local process */ struct rp_bufstruct *valstr; /* string describing reply */ int len; /* length of the string */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_wrply() (%s) '%s'", rp_valstr (valstr -> rp_val), valstr -> rp_line); #endif return (mm_wrec ((char *) valstr, len)); } /* READ DATA FROM LOCAL MAIL (SUB)PROCESS */ mm_rrec (linebuf, len) /* read one "record" */ char *linebuf; /* where to stuff the text */ int *len; /* where to stuff the length count */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_rrec ()"); #endif switch (*len = gcread (mm_rfp, linebuf, LINESIZE - 1, "\000\n\377")) { /* a record == one line */ case NOTOK: ll_log (logptr, LLOGFAT, "read error"); return (RP_LIO); case OK: #ifdef DEBUG ll_log (logptr, LLOGFTR, "eof"); #endif return (RP_EOF); case 1: #ifdef DEBUG ll_log (logptr, LLOGFTR, "DONE"); #endif return (RP_DONE); /* the only valid one-char record */ } *len -= 1; /* cut off the newline */ linebuf[*len] = '\0'; /* keep things null-terminated */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "(%d)'%s'", *len, linebuf); #endif return (RP_OK); } /**/ mm_rstm (buffer, len) /* read buffered block of text */ char *buffer; /* where to stuff the text */ int *len; /* where to stuff count */ { static char goteof; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_rstm ()"); #endif if (goteof) { goteof = FALSE; *len = 0; return (RP_DONE); } switch (*len = gcread (mm_rfp, buffer, LINESIZE - 1, "\000\377")) { /* read until full */ case NOTOK: ll_log (logptr, LLOGFAT, "read error"); return (RP_LIO); case OK: #ifdef DEBUG ll_log (logptr, LLOGFTR, "DONE"); #endif return (RP_DONE); } buffer[*len] = '\0'; if (isnull (buffer[*len - 1])) { goteof = TRUE; *len -= 1; } #ifdef DEBUG ll_log (logptr, LLOGFTR, "(%d)'%s'", *len, buffer); #endif return (RP_OK); } /* WRITE DATA TO LOCAL MAIL (SUB)PROCESS */ mm_wrec (buf, len) /* write a record/packet */ char *buf; /* chars to write */ int len; /* number of chars to write */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_wrec () (%d)'%s'", len, buf ? buf : ""); #endif if (!isstr(buf) || len == 0) (void) putc ('\0', mm_wfp); /* send an end-of-stream */ else { /* send the record text */ fnput (buf, len, mm_wfp); (void) putc ('\n', mm_wfp); } fflush (mm_wfp); /* force it out */ if (ferror (mm_wfp)) { /* direct write */ ll_log (logptr, LLOGFTR, "write error"); return (RP_LIO); } return (RP_OK); } /**/ mm_wstm (buffer, len) /* write next part of char stream */ char *buffer; /* chars to write */ int len; /* number of chars to write */ { register char doflush; /* flush all the text out? */ doflush = (buffer == 0 || len == 0); #ifdef DEBUG if (!doflush) buffer[len] = '\0'; ll_log (logptr, LLOGBTR, "mm_wstm (): (%d)\"%s\"", len, (doflush) ? "[EOF]" : buffer); #endif if (doflush) fflush (mm_wfp); else fnput(buffer, len, mm_wfp); if (ferror (mm_wfp)) { ll_log (logptr, LLOGFAT, "write error"); return (RP_LIO); } return (RP_OK); } /* LOCAL MAIL PROCESS SYSTEM CREATE/DELETE CALLS */ LOCFUN mm_cinit (chname) /* get a mail process */ char chname[]; /* name of channel to invoke */ { Pip pkpin, pkpout; short tmp; char temppath[LINESIZE]; char chbuf[LINESIZE]; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_cinit()"); #endif #ifdef RUNALON mm_rfp = stdin; mm_wfp = stdout; return (RP_OK); #endif; if (pipe (pkpin.pipcall) < OK || pipe (pkpout.pipcall) < OK) err_abrt (RP_LIO, "Unable to pipe() in mm_pkinit()"); regfdary[0] = pkpout.pip.prd; /* set up io fd's for the child */ if (chname == 0) /* make reply channel separate from */ { /* regular text output for submit */ regfdary[1] = 1; regfdary[2] = pkpin.pip.pwrt; } else /* deliver keeps them merged */ { regfdary[1] = pkpin.pip.pwrt; regfdary[2] = CLOSEFD; } for (tmp = numfds-1; tmp > 2; tmp--) regfdary[tmp] = CLOSEFD; if (chname == 0) /* exec submit */ { getfpath (pathsubmit, cmddfldir, temppath); #ifdef DEBUG ll_log (logptr, LLOGFTR, "Forking %s", temppath); #endif if ((mm_cid = nexecl (FRKEXEC, FRKCIGO, regfdary, temppath, namsubmit, (char *)0)) == NOTOK) err_abrt (RP_NO, "Unable to nexecl ('%s')", temppath); } else /* exec pickup process (deliver) */ { getfpath (pathpkup, cmddfldir, temppath); sprintf (chbuf, "-c%s", chname); #ifdef DEBUG ll_log (logptr, LLOGFTR, "Forking %s (%s)", temppath, chbuf); #endif if ((mm_cid = nexecl (FRKEXEC, 0, regfdary, temppath, nampkup, "-p", chbuf, (char *)0)) == NOTOK) err_abrt (RP_NO, "Unable to nexecl ('%s')", temppath); } (void) close (pkpout.pip.prd); (void) close (pkpin.pip.pwrt); mm_rfp = fdopen (pkpin.pip.prd, "r"); mm_wfp = fdopen (pkpout.pip.pwrt, "w"); return (RP_OK); } LOCFUN mm_cend (type) /* get rid of local mail process */ int type; { register int status; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_cend (%d)", type); #endif #ifdef RUNALON return (OK); #endif if (mm_cid == 0) return (OK); /* if and ending is dirty, try to kill the child and preemptively * close the file descriptors (if open). a close, rather than fclose, * is used to avoid the chance of writing to a broken pipe. */ if (type != OK) { kill (mm_cid, 9); if (mm_wfp != (FILE *) EOF && mm_wfp != NULL){ (void) close (fileno (mm_wfp)); fclose(mm_wfp); } if (mm_rfp != (FILE *) EOF && mm_rfp != NULL){ (void) close (fileno (mm_rfp)); fclose(mm_rfp); } } else { if (mm_wfp != (FILE *) EOF && mm_wfp != NULL) fclose (mm_wfp); if (mm_rfp != (FILE *) EOF && mm_rfp != NULL) fclose (mm_rfp); } mm_wfp = mm_rfp = NULL; status = pgmwait (mm_cid); #ifdef DEBUG ll_log (logptr, LLOGBTR, "child = %s", rp_valstr (status)); #endif mm_cid = 0; return (status); } (void) putc ('\n', mm_wfp); } fflush (mm_wfp); /* force it out */ if (ferror (mm_mmdf/lib/mmdf/err_abrt.c 444 0 12 4037 3620510366 10413 #include "util.h" #include "mmdf.h" /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ /* this is a surrogate err_abrt, which is part of the mmdf library and is included in case the user's program doesn't specify one. it is called by the lm_ routines, for example. the action, here, is to try to print the error message and log the message, and then terminate unceremoniously. */ extern struct ll_struct *logptr; /* VARARGS2 */ err_abrt (code, fmt, b, c, d) /* terminate ourself */ int code; char fmt[], b[], c[], d[]; { char newfmt[LINESIZE]; if (rp_isbad (code)) { printx ("mmdf: "); printx (fmt, b, c, d); printx ("\n"); fflush (stdout); sprintf (newfmt, "%s%s\n", "err [ ABEND (%s) ] ", fmt); ll_err (logptr, LLOGFAT, newfmt, rp_valstr (code), b, c, d); fprintf (stderr, newfmt, rp_valstr (code), b, c, d); #ifdef DEBUG fflush (stdout); fflush (stderr); abort (); #endif } ll_close (logptr); /* in case of cycling, close neatly */ exit (RP_NO); } og (logptr, LLOGFTR, "Forking %s", temppath); #endif if ((mm_cid = nexecl (FRKEXEC, FRKCIGO, regfdary, temppath, namsubmit, (char *)0)) == NOTOK) err_abrt (RP_NO, "Unable to nexecl ('%s')", temppath); } else /* exec pickup process (deliver) */ { getfpath (pathpkup, cmddfldir, temppath); sprintf (chbuf, "-c%s", chname); #ifdef DEBUG ll_log (logptr, LLOGFTR, "Forking %s (%s)", temppath, chbuf); #endif if ((mm_cid = nexecl (mmdf/lib/mmdf/interrupt.c 444 0 12 4420 3652603432 10645 #include "util.h" #include "mmdf.h" /* INTERRUPT TRAP SETTING AND CATCHING * * Aug, 81 Dave Crocker major addition to timeout, for conditionally * using timerest only when non-zero. */ #include <signal.h> extern struct ll_struct *logptr; /* ******************* SOFTWARE INTERRUPT TRAPS ********************* */ #ifndef DEBUG sig10 () /* signal 10 interrupts to here */ { signal (SIGBUS, SIG_DFL); ll_err (logptr, LLOGFAT, "Dying on sig10: bus error"); sigabort ("sig10"); } sig12 () /* signal 12 interrupts to here */ { signal (SIGSYS, SIG_DFL); ll_err (logptr, LLOGFAT, "Dying on sig12: bad sys call arg"); sigabort ("sig12"); } #endif DEBUG sig13 () /* signal 13 interrupts to here */ { signal (SIGPIPE, SIG_DFL); ll_err (logptr, LLOGFAT, "Dying on sig13: pipe write w/no reader"); sigabort ("sig13"); } /**/ siginit () /* setup interrupt locations */ { extern int timeout(); #ifndef DEBUG signal (SIGBUS, sig10); signal (SIGSYS, sig12); #endif DEBUG signal (SIGPIPE, sig13); signal (SIGALRM, timeout); /* in case alarm is set & fires */ } /* *********************** TIME-OUT TRAP **************************** */ jmp_buf timerest; /* where to restore to, from timeout */ /* not external, if not used outside */ /* otherwise, set by return routine */ int flgtrest; /* TRUE, if timerest has been set */ timeout () /* alarm timeouts/interrupts to here */ { extern int errno; signal (SIGALRM, timeout); /* reenable interrupt for here */ if (flgtrest) { errno = EINTR; flgtrest = 0; longjmp (timerest, TRUE); } ll_log (logptr, LLOGFAT, "ALARM signal"); return; /* probably generate an i/o error */ } s_alarm(n) unsigned int n; { flgtrest = ((n == 0) ? 0 : 1); alarm(n); } /* ARGSUSED */ sigabort (thesig) char thesig[]; { signal (SIGHUP, SIG_DFL); signal (SIGINT, SIG_DFL); signal (SIGILL, SIG_DFL); signal (SIGBUS, SIG_DFL); signal (SIGSYS, SIG_DFL); signal (SIGPIPE, SIG_DFL); signal (SIGALRM, SIG_DFL); #ifdef DEBUG abort (); #endif exit (NOTOK); /* NOTREACHED */ } ptr, LLOGFAT, newfmt, rp_valstr (code), b, c, d); fprintf (stderr, newfmt, rp_valstr (code), b, c, d); #ifdef DEBUG fflush (stdout); fflush (stderr); abort (); #endif } ll_close (logptr); /* in case of cycling, close nmmdf/lib/mmdf/mm_rdmail.c 444 0 12 6637 3620510366 10564 #include "util.h" #include "mmdf.h" /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ extern struct ll_struct *logptr; /* ************ (mm_) LOCAL MAIL-READING SUB-MODULE **************** */ mm_rinit (info, retadr) /* get initialization info for msg */ char *info, /* where to put general init info */ *retadr; /* where to put return address */ { short retval; int len; char linebuf[LINESIZE]; char *fromptr, *toptr; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_rinit"); #endif retval = mm_rrec (linebuf, &len); if (rp_gval (retval) != RP_OK) return (retval); for (fromptr = linebuf, toptr = info; *fromptr != '\n' && *fromptr != ';'; *toptr++ = *fromptr++) switch (*fromptr) /* mailing mode */ { /* make sure only has ok values */ case ' ': case 'a': case 'A': case 'm': case 'M': case 's': case 'S': break; /* ok to copy to info field */ default: /* invalid code */ ll_log (logptr, LLOGFAT, "illegal mode value: %c", *fromptr); return (RP_NO); }; *toptr = '\0'; /* copy intialization info */ for (toptr = retadr, fromptr++; !isnull (*fromptr) && *fromptr != '\n'; *toptr++ = *fromptr++); *toptr = '\0'; /* copy return address */ return (RP_OK); } /**/ mm_radr (host, adr) /* get an address spec from remote */ char *host, /* where to stuff name of next host */ *adr; /* where to stuff rest of address */ { short retval; int len; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_radr()"); #endif *host = '\0'; /* no-op this field */ retval = mm_rrec (adr, &len); if (rp_gval (retval) != RP_OK) return (retval); #ifdef DEBUG ll_log (logptr, LLOGFTR, "'%s'", adr); #endif return (RP_OK); } mm_rtxt (buffer, len) /* read next part of msg text */ char *buffer; /* where to stuff copy of text */ int *len; /* where to indicate text's length */ { short retval; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_rtxt()"); #endif retval = mm_rstm (buffer, len); if (rp_gval (retval) != RP_OK) return (retval); #ifdef DEBUG ll_log (logptr, LLOGFTR, "'%s'", buffer); #endif return (RP_OK); } (stderr); abort (); #endif } ll_close (logptr); /* in case of cycling, close nmmdf/lib/mmdf/mm_wtmail.c 444 0 12 6573 3631170266 10613 #include "util.h" #include "mmdf.h" /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ /* LM_WTMAIL: Submit local mail */ /* */ /* Jun, 80 Dave Crocker make immediate local delivery conditional */ extern struct ll_struct *logptr; /* ************ (mm_) LOCAL MAIL-WRITING SUB-MODULE **************** */ mm_winit (vianet, info, retadr) /* initialize for one message */ char *vianet; /* what channel coming in from */ char *info, /* general info */ *retadr; /* return address for error msgs */ { short retval; char infoline[LINESIZE]; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_winit (%s, %s, %s)", vianet ? vianet : "", info, retadr ? retadr : ""); #endif if (vianet != (char *)0) /* we are relay; add name of source */ sprintf (infoline, "ti%s*",vianet); else infoline[0] = '\0'; strcat (infoline, info); retval = mm_wrec (infoline, strlen (infoline)); /* if -r switch used, retadr == 0 */ /* if no return address available, retadr[0] == '\0' */ if (isstr(retadr)) retval = mm_wrec (retadr, strlen (retadr)); else if (retadr) /* Don't send a null if handed "" */ retval = mm_wrec (" ", 1); return (retval); } /**/ mm_wadr (host, adr) /* send one address spec to local */ char *host, /* "next" location part of address */ *adr; /* rest of address */ { short retval; char adrbuf[LINESIZE]; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_wadr()"); #endif if (isstr (host)) { sprintf (adrbuf, "%s@%s", adr, host); adr = adrbuf; } retval = mm_wrec (adr, strlen (adr)); return (retval); } mm_waend () /* end of address list */ { short retval; if (rp_isbad (retval = mm_wrec ("!", 1))) return (retval); return (RP_DONE); } mm_wtxt (buffer, len) /* send next part of msg text */ char *buffer; /* the text */ int len; /* length of text */ { return (mm_wstm (buffer, len)); } mm_wtend () /* end of message text */ { return (mm_wrec ((char *) 0, 0)); /* flush buffer and indicate end */ } fer); #endif return (RP_OK); } (stderr); abort (); #endif } ll_close (logptr); /* in case of cycling, close nmmdf/lib/mmdf/logptr.c 444 0 12 520 3620510367 10074 #include "util.h" #include "mmdf.h" /* this is a surrogate declaration of logptr. most of the mmdf modules use "logptr" as the generic reference to the log structure. most mmdf main modules bind logptr to msg.log or chan.log or somesuch. this file will bind it to chan.log. */ extern LLog chanlog; LLog *logptr = &chanlog; dif if (vianet != (char *)0) /* we are relay; add name of source */ sprintf (infoline, "ti%s*",vianet); else infoline[0] = '\0'; strcat (infoline, info)mmdf/lib/mmdf/rp_valstr.c 444 0 12 5364 3620510367 10634 #include "util.h" #include "mmdf.h" /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ /* String Labels for Reply Values */ char * rp_valstr (val) /* return text string for reply value */ int val; { static char noval[] = "*** Illegal: 0000"; /* (noval[0] == '*') => illegal */ switch (rp_gval (val)) { case RP_DONE: return ("DONE"); case RP_OK: return ("OK"); case RP_MOK: return ("MOK"); case RP_HOK: return ("HOK"); case RP_DOK: return ("DOK"); case RP_MAST: return ("MAST"); case RP_SLAV: return ("SLAV"); case RP_AOK: return ("AOK"); case RP_NET: return ("NET"); case RP_BHST: return ("BHST"); case RP_DHST: return ("DHST"); case RP_LIO: return ("LIO"); case RP_NIO: return ("NIO"); case RP_LOCK: return ("LOCK"); case RP_EOF: return ("EOF"); case RP_NS: return ("NS"); case RP_AGN: return ("AGN"); case RP_TIME: return ("TIME"); case RP_NOOP: return ("NOOP"); case RP_FIO: return ("FIO"); case RP_FCRT: return ("FCRT"); case RP_PROT: return ("PROT"); case RP_RPLY: return ("RPLY"); case RP_MECH: return ("MECH"); case RP_NO: return ("NO"); case RP_NDEL: return ("NDEL"); case RP_HUH: return ("HUH"); case RP_NCMD: return ("NCMD"); case RP_PARM: return ("PARM"); case RP_UCMD: return ("UCMD"); case RP_USER: return ("USER"); case RP_FOPN: return ("FOPN"); case RP_NAUTH: return ("NAUTH"); default: /* print illegal octal value */ noval[15] = rp_gbbit (val) + '0'; noval[16] = rp_gcbit (val) + '0'; noval[17] = rp_gsbit (val) + '0'; return (noval); } } } else /* exec pickup process (deliver) */ { getfpath (pathpkup, cmddfldir, temppath); sprintf (chbuf, "-c%s", chname); #ifdef DEBUG ll_log (logptr, LLOGFTR, "Forking %s (%s)", temppath, chbuf); #endif if ((mm_cid = nexecl (mmdf/lib/mmdf/qu_fakrply.c 444 0 12 7520 3620510367 10771 #include "util.h" #include "mmdf.h" /* send RP_DHST replies back to Deliver, if foreign site goes down */ /* The following state diagram is for the qu2??_send functions. The concern * is for making sure that Deliver properly receives replies, in the event * that the remote host goes dead. For simplicity, a modification of the * following is used, to keep the state variable local to this file. * * The parenthesized tokens are error-condition actions, to be taken * within qu_fakrply(): ABORT=abort/return, RINIT=qu_rinit, RDADR=qu_radr, * ARPLY=address ??_wrply, TRPLY=text ??_wrply * * (ABORT) * | * qu_init * \---->--(RINIT)---> ??_init * qu_pkinit <--(ABORT)---<---/ * \---->--(RINIT)---> ??_sbinit * | * /->--> (ABORT) <------------<---/ * | | * | qu_rinit * | | * | [ok] -->--(RDADR)---> ??_winit ->-\ * | [end] | * | | | * | qu_pkend ---------- | * | \====>============> |??_sbend| | * | ---------- | * | /-> (ABORT) <------------<-------------<-/ * | | | * | | QU_RADR * | | | * | | [end]-->--(TRPLY)---> ??_waend ->-\ * | | | | * | | [ok] -->--(ARPLY)---> ??_wadr | * | | | | * | | ??_rrply | * | | QU_WRPLY <--(ABORT)-------/ | * | \-<------/ | * | | * | qu_rinit <------------<-------------<-/ * | | * | qu_rtxt <------------<-------------<-\ * | | | * | [ok] -->------------> ??_wtxt ->-/ * | | * | [end]-->------------> ??_wend * | | * | ??_rrply * | QU_WRPLY <------------<---/ * \-<---------/ */ #include "chan.h" extern struct ll_struct *logptr; LOCVAR struct rp_construct rp_fdone = { RP_DHST, 'D', 'O', 'N', 'E', ' ', '&', ' ', 's', 't', 'i', 'l', 'l', ' ', 'g', 'o', 'n', 'e', '\0' } , rp_fstill = { RP_DHST, 's', 't', 'i', 'l', 'l', ' ', 'g', 'o', 'n', 'e', '\0' }; qu_fakrply (snd_state) /* send RP_DHST's to Deliver */ int snd_state; { /* behavior depends on state */ char host[LINESIZE], adr[LINESIZE], info[LINESIZE], sender[LINESIZE]; #ifdef DEBUG ll_log (logptr, LLOGPTR, "qu_fakrply"); #endif switch (snd_state) { case SND_ABORT: #ifdef DEBUG ll_log (logptr, LLOGBTR, "SND_ABORT"); #endif return; case SND_RINIT: goto rinit; case SND_ARPLY: goto arply; case SND_TRPLY: goto mrply; } FOREVER { switch (rp_gval (qu_radr (host, adr))) { case RP_DONE: /* list done, say ok to text */ mrply: qu_wrply ((struct rp_bufstruct *) &rp_fdone, rp_conlen (rp_fdone)); rinit: if (rp_gval (qu_rinit (info, sender)) != RP_OK) { #ifdef DEBUG ll_log (logptr, LLOGBTR, "fake DONE"); #endif return; } break; case RP_OK: /* say ok to an address */ arply: #ifdef DEBUG ll_log (logptr, LLOGFTR, "faking '%s'", adr); #endif qu_wrply ((struct rp_bufstruct *) &rp_fstill, rp_conlen (rp_fstill)); break; default: ll_log (logptr, LLOGTMP, "error value in loop"); return; } } } cend (type) /* get rid of local mail process */ int type; { register int status; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mm_cend (%d)", type); #endmmdf/lib/mmdf/phs_note.c 444 0 12 7443 3642267571 10451 #include "util.h" #include "mmdf.h" #include <sys/stat.h> #include "ch.h" #include "phs.h" /* note mmdf channel activity phases */ extern Llog *logptr; extern char *phsdfldir; /* directory for timestamps */ #define PHS_NUL 0 #define PHS_SND 1 #define PHS_RCV 2 LOCVAR int phs_mode; /* are sending or receiving */ LOCVAR time_t starttime; LOCVAR char cnstrt[] = "%s/%s/cstrt", cngot[] = "%s/%s/cgot", cnend[] = "%s/%s/cend", restrt[] = "%s/%s/rstrt", remsg[] = "%s/%s/rmsg", reend[] = "%s/%s/rend", wrstrt[] = "%s/%s/wstrt", wrmsg[] = "%s/%s/wmsg", wrend[] = "%s/%s/wend"; /**/ phs_note (thechan, phase) /* make a timestamp */ Chan *thechan; int phase; { char stamploc[LINESIZE]; char stampdir[LINESIZE]; char *fmt; switch (phase) { case PHS_CNSTRT: ll_log (logptr, LLOGPTR, "strt"); fmt = cnstrt; /*NOSTRICT*/ starttime = 0L; break; case PHS_CNGOT: ll_log (logptr, LLOGPTR, "conn"); fmt = cngot; time (&starttime); break; case PHS_CNEND: phs_mode = PHS_NUL; /*NOSTRICT*/ starttime = 0L; fmt = cnend; break; case PHS_RESTRT: ll_log (logptr, LLOGPTR, "rcv"); fmt = restrt; phs_mode = PHS_RCV; break; case PHS_REMSG: fmt = remsg; break; case PHS_REEND: ll_log (logptr, LLOGPTR, "rend"); fmt = reend; phs_mode = PHS_NUL; break; case PHS_WRSTRT: ll_log (logptr, LLOGPTR, "writ"); fmt = wrstrt; phs_mode = PHS_SND; break; case PHS_WRMSG: fmt = wrmsg; break; case PHS_WREND: ll_log (logptr, LLOGPTR, "wend"); fmt = wrend; phs_mode = PHS_NUL; break; } sprintf (stamploc, fmt, phsdfldir, thechan -> ch_name); /* We rely on umask() == 0 */ if (close (creat (stamploc, 0666)) < 0) { sprintf(stampdir, "%s/%s", phsdfldir, thechan -> ch_name); if ( creatdir (stampdir, 0777, 0, 0) != OK || (close (creat (stamploc, 0666)) < 0) ) return (NOTOK); } return(0); } /**/ phs_msg (thechan, naddrs, len) /* note trasmission of 1 message */ Chan *thechan; int naddrs; long len; { if (naddrs <= 0 && len <= 0L) return; /* nothing to record */ switch (phs_mode) { case PHS_RCV: ll_log (logptr, LLOGFST, "rmsg %4da %10ldc", naddrs, len); phs_note (thechan, PHS_REMSG); /* make a timestamp */ break; case PHS_SND: ll_log (logptr, LLOGFST, "wmsg %4da %10ldc", naddrs, len); phs_note (thechan, PHS_WRMSG); /* make a timestamp */ break; } } /**/ phs_end (thechan, status) /* note end of session */ Chan *thechan; int status; /* mmdf end value */ { time_t endtime; if (starttime == 0L) ll_log(logptr, LLOGBST, "end (%s)", rp_valstr (status)); else { time (&endtime); ll_log (logptr, LLOGBST, "end (%s)\t%lds", rp_valstr (status), (long) (endtime - starttime)); /*NOSTRICT*/ starttime = 0L; } phs_note (thechan, PHS_CNEND); /* make a timestamp */ } /**/ time_t phs_get (thechan, phase) /* read a timestamp */ Chan *thechan; int phase; { struct stat statbuf; char stamploc[LINESIZE]; char *fmt; switch (phase) { case PHS_CNSTRT: fmt = cnstrt; break; case PHS_CNGOT: fmt = cngot; break; case PHS_CNEND: fmt = cnend; break; case PHS_RESTRT: fmt = restrt; break; case PHS_REMSG: fmt = remsg; break; case PHS_REEND: fmt = reend; break; case PHS_WRSTRT: fmt = wrstrt; break; case PHS_WRMSG: fmt = wrmsg; break; case PHS_WREND: fmt = wrend; break; } sprintf (stamploc, fmt, phsdfldir, thechan -> ch_name); if (stat (stamploc, &statbuf) < 0) return (0L); /*NOSTRICT*/ return (statbuf.st_mtime); } (type != OK) { kill (mm_cid, 9); if (mm_wfp != (FILE *) EOF && mm_wfp != NULL){ (void) close (fileno (mm_wfp)); fclose(mm_wfp); } if (mm_rfp != (FILE *) EOF && mm_rfp != NULL){ (void) close (filenommdf/lib/mmdf/mq_rdmail.c 444 0 12 30243 3623755265 10612 /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * This module and its listings may be used freely by United States * federal, state, and local government agencies and by not-for- * profit institutions, after sending written notification to * Professor David J. Farber at the above address. * * For-profit institutions may use this module, by arrangement. * * Notification must include the name of the acquiring organization, * name and contact information for the person responsible for * maintaining the operating system, name and contact information * for the person responsible for maintaining this program, if other * than the operating system maintainer, and license information if * the program is to be run on a Western Electric Unix(TM) operating * system. * * Documents describing systems using this module must cite its * source. * * Development of this module was supported in part by the * University of Delaware and in part by contracts from the United * States Department of the Army Readiness and Materiel Command and * the General Systems Division of International Business Machines. * * Portions of the MMDF system were built on software originally * developed by The Rand Corporation, under the sponsorship of the * Information Processing Techniques Office of the Defense Advanced * Research Projects Agency. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version 1 David H. Crocker May 1981 * */ #include "util.h" #include "mmdf.h" #include <signal.h> #include <sys/stat.h> #include "ch.h" #include "msg.h" /* msg_cite() is defined in adr_queue.h */ #include "adr_queue.h" #ifndef EWOULDBLOCK #define EWOULDBLOCK ETXTBSY /* To handle Berkeley 4.2 and Others */ #endif extern LLog *logptr; extern long lseek(); extern time_t time(); extern char *supportaddr; extern char *aquedir; extern char *mquedir; extern char *squepref; /* string to preface sub-queue name */ extern char *lckdfldir; extern int lk_open(); long mq_gtnum(); /* reads long ascii # from addr file */ LOCVAR int mq_fd; /* read file descriptor, saved */ LOCVAR FILE *mq_rfp; /* read handle, for getting entries */ LOCVAR char mq_dlm; /* mq_gtnum's delimeter char */ LOCVAR long mq_curpos; /* Current offset of read pointer (rfp) */ LOCVAR long mq_lststrt; /* Offset to start of addr list */ LOCVAR long mq_optstrt; /* Offset to options */ LOCVAR Msg *mq_curmsg; /* SEK handle on current message */ mq_rkill (type) /* give-up access to msg info */ int type; /* type of ending (currently ignored) */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "mq_rkill (type= %d, mq_fd=%d, mq_rfp=%o)", type, mq_fd, mq_rfp); #endif if (mq_fd != NOTOK) lk_close (mq_fd, (char *) 0, lckdfldir, mq_curmsg -> mg_mname); if (mq_rfp != (FILE *) EOF && mq_rfp != (FILE *) NULL) fclose (mq_rfp); mq_fd = NOTOK; mq_rfp = NULL; } /**/ mq_rinit (thechan, themsg, retadr) /* gain access to info on the msg */ Chan *thechan; /* channel points to sub-queue */ Msg *themsg; char *retadr; /* return address */ { char msgname[LINESIZE]; extern int errno; register short len; int tmpfd; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mq_rinit ()"); #endif mq_curmsg = themsg; /* SEK not message for file locking */ if (thechan == (Chan *) 0) /* do the entire queue */ { sprintf (msgname, "%s%s", aquedir, themsg -> mg_mname); } else { sprintf (msgname, "%s%s/%s", squepref, thechan -> ch_queue, themsg -> mg_mname); } if ((mq_fd = lk_open (msgname, 2, lckdfldir, themsg -> mg_mname, 20)) < OK || (tmpfd = dup (mq_fd)) < OK || (mq_rfp = fdopen (tmpfd, "r")) < OK) { /* msg queued in file w/its name */ switch (errno) { case ENOENT: /* another deliver probably did it */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "%s no entry", msgname); break; #endif case EWOULDBLOCK: /* another deliver probably using it */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "%s busy", msgname); #endif printx ("Message '%s': busy.\n", themsg -> mg_mname); break; default: printx ("can't open address list '%s'\n", msgname); ll_err (logptr, LLOGTMP, "can't open '%s'", msgname); } mq_rkill (NOTOK); /* Cleanup open files */ return (NOTOK); } mq_lststrt = 0L; /*NOSTRICT*/ themsg -> mg_time = (time_t) mq_gtnum (); mq_optstrt = mq_lststrt - 1; /* Offset to option bits */ themsg -> mg_stat = (char) mq_gtnum (); themsg -> mg_stat |= (mq_dlm == ADR_DONE ? ADR_WARNED : 0); /* whether warning been sent */ if (fgets (retadr, ADDRSIZE, mq_rfp) == NULL) { ll_err (logptr, LLOGTMP, "Can't read %s sender name", themsg -> mg_mname); mq_rkill (OK); retadr[0] = '\0'; /* eliminate old sender name */ return (NOTOK); } len = strlen (retadr); mq_lststrt += len; if (retadr[len - 1] != '\n') { while (fgetc(mq_rfp) != '\n') { mq_lststrt++; if (feof(mq_rfp) || ferror(mq_rfp)) { ll_err (logptr, LLOGTMP, "Can't read %s sender name", themsg -> mg_mname); mq_rkill (OK); retadr[0] = '\0'; /* eliminate old sender name */ return (NOTOK); } } mq_lststrt++; /* The address was probably too long, substitute orphanage */ sprintf (retadr, "%s (Orphanage)", supportaddr); } else retadr[len - 1] = '\0'; /* eliminate the newline */ mq_curpos = mq_lststrt; #ifdef DEBUG ll_log (logptr, LLOGFTR, "name='%s' ret='%s' (fd=%d)", themsg -> mg_mname, retadr, fileno (mq_rfp)); #endif return (OK); } /**/ /* * mq_setpos() repositions the read pointer (rfp) but because mq_rfp is * just a dup of mq_fd, mq_fd is repositioned as well. Be careful! */ mq_setpos (offset) long offset; { #ifdef DEBUG ll_log (logptr, LLOGBTR, "mq_setpos (%ld)", offset); #endif /* * The following is needed due to a bug in V7/SysIII/4.nBSD Stdio * which will not allow you to invalidate the incore buffer of * of a file fopened for reading. (DPK @ BRL) */ fclose (mq_rfp); mq_rfp = NULL; if ((mq_rfp = fdopen (dup(mq_fd), "r")) == NULL) { mq_rkill (NOTOK); ll_log (logptr, LLOGFAT, "mq_setpos(), cannot reopen address list"); } if (offset == 0L) { fseek (mq_rfp, mq_lststrt, 0); mq_curpos = mq_lststrt; } else { fseek (mq_rfp, offset, 0); mq_curpos = offset; } } mq_radr (theadr) /* obtain next address in msg's queue */ register struct adr_struct *theadr; { char *arglist[20]; int argc; register int len; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mq_read ()"); #endif theadr -> adr_que = 0; /* null it to indicate empty entry */ theadr -> adr_pos = mq_curpos; for (;;) { if (fgets (theadr->adr_buf, sizeof (theadr->adr_buf), mq_rfp) == NULL) { if (feof (mq_rfp)) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "end of list"); #endif return (DONE); /* simply an end of file */ } ll_err (logptr, LLOGTMP, "Problem reading address"); return (NOTOK); } len = strlen (theadr -> adr_buf); if (theadr -> adr_buf[2] != ADR_DONE) break; theadr -> adr_pos += len; } mq_curpos = theadr -> adr_pos + len; #ifdef DEBUG ll_log (logptr, LLOGFTR, "adr: '%s'", theadr -> adr_buf); #endif theadr -> adr_buf[len - 1] = '\0'; /* so entry can be handled as string */ argc = str2arg (theadr -> adr_buf, 20, arglist, (char *) 0); if (argc < 4) { ll_log (logptr, LLOGTMP, "error with queue address line"); return (NOTOK); /* error with address entry */ } theadr -> adr_tmp = arglist[0][0]; theadr -> adr_delv = arglist[1][0]; theadr -> adr_que = arglist[2]; theadr -> adr_host = arglist[3]; theadr -> adr_local = arglist[4]; #ifdef DEBUG ll_log (logptr, LLOGFTR, "adr parts: '%c' '%c' '%s' '%s' '%s'", theadr -> adr_tmp, theadr -> adr_delv, theadr -> adr_que, theadr -> adr_host, theadr -> adr_local); #endif return (OK); } /**/ /* * If the message was successfully delivered, or if a permanent failure * resulted, indicate that this address has been handled by changing the * delimiter between the descriptor and address info. */ mq_wrply (rply, themsg, theadr) /* modify addr's entry as per reply */ RP_Buf *rply; Msg *themsg; struct adr_struct *theadr; { char donechr; #ifdef DEBUG ll_log (logptr, LLOGBTR, "mq_wrply (%s)", themsg -> mg_mname); #endif fflush (stdout); switch (rp_gval (rply -> rp_val)) { case RP_AOK: /* name ok; text later */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "AOK @ offset(%ld)", (long) (theadr -> adr_pos + ADR_TMOFF)); #endif donechr = ADR_AOK; /* temporary mark */ lseek (mq_fd, (long) (theadr -> adr_pos + ADR_TMOFF), 0); write (mq_fd, &donechr, 1); break; case RP_NDEL: /* won't be able to deliver */ case RP_DONE: /* finished processing this addr */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "DONE @ offset(%ld)", (long) (theadr -> adr_pos + ADR_DLOFF)); #endif donechr = ADR_DONE; lseek (mq_fd, (long) (theadr -> adr_pos + ADR_DLOFF), 0); write (mq_fd, &donechr, 1); break; case RP_NOOP: /* not processed, this time */ case RP_NO: /* no success, this time */ default: if( theadr->adr_tmp == ADR_AOK ) /* We need to reset it */ { #ifdef DEBUG ll_log (logptr, LLOGFTR, "CLR @ offset(%ld)", (long) (theadr -> adr_pos + ADR_TMOFF)); #endif donechr = ADR_CLR; lseek (mq_fd, (long) (theadr -> adr_pos + ADR_TMOFF), 0); write (mq_fd, &donechr, 1); } } /* * The following is needed due to a bug in V7/SysIII/4.nBSD Stdio * which will not allow you to invalidate the incore buffer of * of a file fopened for reading. (DPK @ BRL) */ fclose (mq_rfp); mq_rfp = NULL; if ((mq_rfp = fdopen (dup(mq_fd), "r")) == NULL) { mq_rkill (NOTOK); ll_log (logptr, LLOGFAT, "mq_wrply(), cannot reopen address list"); } fseek (mq_rfp, mq_curpos, 0); /* Back to where we were */ } /**/ LOCFUN long mq_gtnum () /* read a long number from adr queue */ { register long thenum; register char c; #ifdef DEBUG ll_log (logptr, LLOGFTR, "mq_gtnum"); #endif for (thenum = 0; isdigit (c = getc (mq_rfp)); thenum = thenum * 10 + (c - '0'), mq_lststrt++); mq_lststrt++; mq_dlm = c; /* note the first non-number read */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "(%ld)'%c'", thenum, c); #endif return (thenum); } mq_rwarn () /* note that warning has been sent */ { static char donechr = ADR_DONE; #ifdef DEBUG ll_log (logptr, LLOGFTR, "WARN @ offset(%ld)", (long) (mq_optstrt)); #endif lseek (mq_fd, mq_optstrt, 0); write (mq_fd, &donechr, 1); /* * The following is needed due to a bug in V7/SysIII/4.nBSD Stdio * which will not allow you to invalidate the incore buffer of * of a file fopened for reading. (DPK @ BRL) */ fclose (mq_rfp); mq_rfp = NULL; if ((mq_rfp = fdopen (dup(mq_fd), "r")) == NULL) { mq_rkill (NOTOK); ll_log (logptr, LLOGFAT, "mq_rwarn(), cannot reopen address list"); } fseek (mq_rfp, mq_curpos, 0); /* Back to where we were */ } = '\0'; /* so entry can be handled as string */ argc = str2arg (theadr -> adr_buf, 20, arglist, (char *) 0); if (argc < 4) { ll_log (logptr, LLOGTMP, "error with queue address line"); return (NOTOK); /* error with address entry */ } theadr -> adr_tmp = arglist[0][0]; theadr -> adr_delv = argmmdf/lib/mmdf/mmdf_init.c 444 0 12 5612 3664425135 10570 #include "util.h" #include "mmdf.h" #include "ch.h" extern LLog *logptr; extern char *mmtailor; /* where the tailoring file is */ extern char *logdfldir; extern int mid_enable; extern Table tb_mailids; extern Table tb_users; extern Table tb_mc; extern Table **tb_list; extern int tb_numtables; extern int tb_maxtables; extern char *strdup(); static char version[] = "$@(#)MMDFII, Release B"; #define MAXARG 100 mmdf_init (pgmname) /* initialize an mmdf process */ char *pgmname; { extern char *dupfpath (); char *argv[MAXARG]; int argc; char *savelog; /* SEK hold log file name */ #ifdef DEBUG int dbind; #endif pgmname = (pgmname ? pgmname : "???"); /* Paranoid */ ll_log (logptr, LLOGPTR, "mmdf_init (%s)", pgmname); if (!isstr (mmtailor)) /* tailor info is compiled in */ return; mmdf_fdinit(); /* SEK save initial part of log name */ savelog = strdup (logptr -> ll_file); ll_hdinit (logptr, pgmname); /* make header unique */ if (logdfldir != (char *) 0) logptr -> ll_file = dupfpath (logptr -> ll_file, logdfldir); if (tai_init (mmtailor) != OK) err_abrt (RP_FIO, "Can't access tailoring file '%s'", mmtailor); while ((argc = tai_get (MAXARG, argv)) > 0) { /* test for mmdf or dial info */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "argc = %d", argc); for (dbind = 0; dbind < argc; dbind++) ll_log (logptr, LLOGFTR, "(%d) '%s'", dbind, argv[dbind]); #endif if (argv[0] == 0 || argv[0][0] == '\0') continue; /* noop */ switch (uip_tai (argc, argv)) { /* user program tailoring info? */ case YES: case NOTOK: continue; } switch (mm_tai (argc, argv)) { /* mmdf tailoring info? */ case YES: case NOTOK: continue; } #ifndef NODIAL switch (d_tai (argc, argv)) { /* dialing package info? */ case YES: case NOTOK: continue; } #endif NODIAL post_tai (argc, argv); } if (argc == NOTOK) err_abrt (RP_MECH, "Error processing tailoring file '%s'", mmtailor); tai_end (); if (mid_enable) { if ((tb_numtables+1) >= tb_maxtables) err_abrt (RP_MECH, "No space in tb_list for tb_users/mailids"); tb_list[tb_numtables++] = &tb_users; tb_list[tb_numtables++] = &tb_mailids; tb_list[tb_numtables] = (Table *) 0; } if (logdfldir != (char *) 0) { /* we know of malloc in code, above */ free (logptr -> ll_file); logptr -> ll_file = dupfpath (savelog, logdfldir); free (savelog); } return; } tai_error (error, errp, argc, argv) char *error, *errp; int argc; char **argv; { char errline[LINESIZE]; argv[argc] = (char *) 0; arg2vstr (0, sizeof errline, errline, argv); ll_log (logptr, LLOGFAT, "%s (%s) in '%s'", error, errp, errline); } t"); } fseek (mq_rfp, mq_curpos, 0); /* Back to where we were */ } /**/ LOCFUN long mq_gtnum () /*mmdf/lib/mmdf/hd_format.c 444 0 12 6357 3671073153 10571 #include "util.h" #include "mmdf.h" #include "ch.h" /* reformat addresses in the headers of a message */ extern LLog *logptr; extern long amp_hdr(); FILE *hd_fmtfp; /* handle on the formatted header */ LOCVAR FILE *hd_fpdup; /* stream pointers for original and alternate file*/ LOCVAR char hd_fn[] = "/tmp/am.hdrXXXXXX"; /* file for mapped headers */ long hd_init (chanptr, fd) /* ready to process one message */ Chan *chanptr; /* channel to NOT reformat refs to */ int fd; /* handle on file with message */ { long hd_seek; char hd_cur[sizeof hd_fn]; extern char *mktemp (); /* sri transform */ /* begin special code for header translation, channel initialization phase * (dhc) only call mktemp on the first time through this routine. * the following assumes that x_fn is compile-time specified within * this routine; otherwise, turn the sizeof into a strlen. * a cleaner approach would be to keep the base string somewhere else * and then strcpy it into x_fn and then call mktemp each time. */ hd_seek = 0L; hd_fpdup = fdopen (dup (fd), "r"); /* open stream to msg text */ (void) strcpy (hd_cur, hd_fn); mktemp (hd_cur); /* create name for translated header file */ if (close(creat(hd_cur, 0600)) < 0) { ll_err (logptr, LLOGTMP, "** '%s' hd_format file open err", hd_cur); } else if ((hd_fmtfp = fopen(hd_cur, "a+")) == NULL) { ll_err (logptr, LLOGTMP, "Header file re-open failure"); hd_seek = 0L; } else { if ( (hd_seek = amp_hdr (chanptr, hd_fpdup, hd_fmtfp)) <= 0L || hd_seek == (long)MAYBE) { ll_log (logptr, LLOGFAT, "Failure in header parse"); fclose (hd_fmtfp); /* give it back */ hd_fmtfp = (FILE *) NULL; /* signal no xlated file to read */ if(hd_seek != (long)MAYBE) hd_seek = 0L; } else /* setup for the readings */ fseek (hd_fmtfp, 0L, 0); } (void) unlink (hd_cur); /* Blow it away!! */ if (hd_fpdup != (FILE *) NULL) { fclose (hd_fpdup); /* toss unneeded stream pointer */ hd_fpdup = (FILE *) NULL; } return (hd_seek); } hd_minit () /* ready to process one copy */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "hd_minit ()"); #endif if (hd_fmtfp != (FILE *) NULL) rewind (hd_fmtfp); /* rewind xlated file */ } hd_read (buf, max) char *buf; /* where to put the data */ int max; /* maximum size of buf */ { int nread; #ifdef DEBUG ll_log (logptr, LLOGBTR, "hd_read ()"); #endif /* sri transform */ /* special code for header translation, each address phase */ if (hd_fmtfp != (FILE *) NULL) { /* * we have a translated file */ nread = fread (buf, sizeof (char), max, hd_fmtfp); if (nread == 0 && ferror(hd_fmtfp)) return (NOTOK); else return (nread); } return (0); } hd_end () { #ifdef DEBUG ll_log (logptr, LLOGBTR, "hd_end ()"); #endif if (hd_fmtfp != (FILE *) NULL) { fclose (hd_fmtfp); /* done reading xlated file */ hd_fmtfp = (FILE *) NULL; } } 0'), mq_lststrt++); mq_lststrt++; mq_dlm = c; /* note the first non-number read */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "(%ld)'%c'", thenum, c); #endif return (thenum); } mq_rwarn () /* note that warning has beemmdf/lib/mmdf/uip_tai.c 444 0 12 620 3620510371 10213 #include "util.h" #include "mmdf.h" /* fake user program tailoring, in case the user program does not * use it or it is not a user program. This way, everyone can just * call mmdf_init(). */ uip_tai (argc, argv) /* tailor some uip parameter */ int argc; /* number of values */ char *argv[]; /* list of values */ { return (NO); } (chanptr, fd) /* ready to process one message */ Chan *chanptr; /* channel to NOT refmmdf/lib/mmdf/mm_tai.c 444 0 12 76210 3655234571 10114 #include "util.h" #include "mmdf.h" #include "cmd.h" #include "ch.h" #include "ap.h" extern char *malloc (); extern LLog *logptr, chanlog; extern char *locmachine, /* UCL local machine name */ *locname, *locdomain, *sitesignature, *mmdflogin, *supportaddr; extern char *logdfldir, *phsdfldir, *tbldfldir, *tbldbm, *quedfldir, *cmddfldir, *chndfldir, *lckdfldir, *mldfldir; extern int queprot, warntime, failtime, maxhops, maxqueue, mgt_addid, lnk_listsize, mid_enable, mailsleep, sentprotect; extern LLog msglog, chanlog, authlog; extern char *def_trn; extern char *tquedir, *aquedir, *mquedir; extern char *namsubmit, *pathsubmit, *namdeliver, *pathdeliver, *nampkup, *pathpkup, *nammail, *pathmail; extern char *ch_dflnam, *dlvfile, *mldflfil, *delim1, *delim2; extern char /* NIFTP channel bits */ *pn_quedir; extern char *authrequest, /* Authorisation request */ *Uuxstr; /* uux command string */ /**/ #define MMLOCHOST 1 #define MMSIGN 2 #define MMLOGIN 3 #define MMSUPPORT 4 #define MMLOGDIR 5 #define MMPHSDIR 6 #define MMTBLDIR 7 #define MMDBM 8 #define MMQUEDIR 9 #define MMCMDDIR 10 #define MMCHANDIR 11 #define MMDLVRDIR 12 #define MMTEMPT 13 #define MMADDRQ 14 #define MMMSGQ 15 #define MMQUEPROT 16 #define MMMSGLOG 17 #define MMCHANLOG 18 #undef Gonzo 19 #undef Gonzo 20 #define MMWARNTIME 21 #define MMFAILTIME 22 #define MMMAXSORT 23 #define MMSLEEP 24 #define MMSUBMIT 25 #define MMDELIVER 26 #define MMPICKUP 27 #define MMV6MAIL 28 #define MMMBXPROT 29 #define MMMBXNAME 30 #define MMMBXPREF 31 #define MMMBXSUFF 32 #define MMDLVFILE 33 #define MMAXHOPS 34 #define MADDID 35 #define MLISTSIZE 36 #define MMDFLCHAN 37 #define UUxstr 38 #define MMTBL 39 #define MMLOCDOMAIN 40 #define MMDOMAIN 41 #define ALIAS 42 #define MMLOCMACHINE 43 #define NIQUEDIR 44 #define MMMAILIDS 45 #define MMLCKDIR 46 #define AUTHLOG 47 #define AUTHREQUEST 48 #define MMCHAN 49 #define MMNOOP 100 /**/ Cmd cmdtab[] = { "mlocmachine", MMLOCMACHINE, 1, "mlname", MMLOCHOST, 1, "mlochost", MMLOCHOST, 1, "mldomain", MMLOCDOMAIN, 1, "msig", MMSIGN, 1, "mlogin", MMLOGIN, 1, "msupport", MMSUPPORT, 1, "mlogdir", MMLOGDIR, 1, "mphsdir", MMPHSDIR, 1, "mtbldir", MMTBLDIR, 1, "mlckdir", MMLCKDIR, 1, "mdbm", MMDBM, 1, "mdflchan", MMDFLCHAN, 1, "mquedir", MMQUEDIR, 1, "mcmddir", MMCMDDIR, 1, "mchndir", MMCHANDIR, 1, "mdlvrdir", MMDLVRDIR, 1, "mtempt", MMTEMPT, 1, "mtmpt", MMTEMPT, 1, "maddrq", MMADDRQ, 1, "mmsgq", MMMSGQ, 1, "mqueprot", MMQUEPROT, 1, "mmsglog", MMMSGLOG, 1, "mchanlog", MMCHANLOG, 1, "authlog", AUTHLOG, 1, "mwarntime", MMWARNTIME, 1, "mfailtime", MMFAILTIME, 1, "mmaxsort", MMMAXSORT, 1, "msleep", MMSLEEP, 1, "msubmit", MMSUBMIT, 1, "mdeliver", MMDELIVER, 1, "mpkup", MMPICKUP, 1, "mv6mail", MMV6MAIL, 1, "mmbxprot", MMMBXPROT, 1, "mmbxname", MMMBXNAME, 1, "mmbxpref", MMMBXPREF, 1, "mmbxsuff", MMMBXSUFF, 1, "mdlv", MMDLVFILE, 1, "maddid", MADDID, 1, "mmaxhops", MMAXHOPS, 1, "mlistsize", MLISTSIZE, 1, "uuxstr", UUxstr, 1, "mtbl", MMTBL, 1, "mchn", MMCHAN, 1, "mmailid", MMMAILIDS, 1, "mdmn", MMDOMAIN, 1, "niquedir", NIQUEDIR, 1, "authrequest", AUTHREQUEST,1, "alias", ALIAS, 1, "", MMNOOP, 0, 0, 0, 0 }; /**/ mm_tai (argc, argv) /* process mmdf tailor info */ int argc; char *argv[]; { int retval; #ifdef DEBUG ll_log (logptr, LLOGBTR, "m_tai (%s: %d)", argv[0], argc); #endif retval = YES; switch (cmdsrch (argv[0], --argc, cmdtab)) { case NO: case MMNOOP: default: /* command not known to us */ retval = NO; break; case MMLOCMACHINE: locmachine = argv[1]; break; case MMLOCHOST: locname = argv[1]; break; case MMLOCDOMAIN: locdomain = argv[1]; break; case MMSIGN: sitesignature = argv[1]; break; case MMLOGIN: mmdflogin = argv[1]; break; case MMSUPPORT: supportaddr = argv[1]; break; case MMLOGDIR: logdfldir = argv[1]; break; case MMPHSDIR: phsdfldir = argv[1]; break; case MMTBLDIR: tbldfldir = argv[1]; break; case MMDBM: tbldbm = argv[1]; break; case MMQUEDIR: quedfldir = argv[1]; break; case MMCMDDIR: cmddfldir = argv[1]; break; case MMCHANDIR: chndfldir = argv[1]; break; case MMDLVRDIR: mldfldir = argv[1]; break; case MMLCKDIR: lckdfldir = argv[1]; break; case MMTEMPT: tquedir = argv[1]; break; case MMADDRQ: aquedir = argv[1]; break; case MMMSGQ: mquedir = argv[1]; break; case MMQUEPROT: sscanf (argv[1], "%o", &queprot); break; case MMMSGLOG: retval = tai_log (argc, &argv[1], &msglog); break; case MMCHANLOG: retval = tai_log (argc, &argv[1], &chanlog); break; case AUTHLOG: retval = tai_log (argc, &argv[1], &authlog); break; case AUTHREQUEST: authrequest = argv[1]; break; case MMWARNTIME: warntime = atoi (argv[1]); break; case MMFAILTIME: failtime = atoi (argv[1]); break; case MMMAXSORT: maxqueue = atoi (argv[1]); break; case MMSLEEP: mailsleep = atoi (argv[1]); break; case MMSUBMIT: retval = tai_pgm (argc, &argv[1], &namsubmit, &pathsubmit); break; case MMDELIVER: retval = tai_pgm (argc, &argv[1], &namdeliver, &pathdeliver); break; case MMPICKUP: retval = tai_pgm (argc, &argv[1], &nampkup, &pathpkup); break; case MMV6MAIL: retval = tai_pgm (argc, &argv[1], &nammail, &pathmail); break; case MMMBXPROT: sscanf (argv[1], "%o", &sentprotect); break; case MMMBXNAME: mldflfil = argv[1]; break; case MMMBXPREF: delim1 = argv[1]; break; case MMMBXSUFF: delim2 = argv[1]; break; case MMDLVFILE: dlvfile = argv[1]; break; case MMAXHOPS: maxhops = atoi(argv[1]); break; case MADDID: mgt_addid = atoi(argv[1]); break; case MLISTSIZE: lnk_listsize = atoi(argv[1]); break; case UUxstr: Uuxstr = argv[1]; break; case MMTBL: retval = tb_tai (argc, &argv[1]); break; case MMDFLCHAN: ch_dflnam = argv[1]; break; case MMCHAN: retval = ch_tai (argc, &argv[1]); break; case MMMAILIDS: /* enable/disable use of Mail-Ids */ mid_enable = atoi(argv[1]); break; case MMDOMAIN: retval = dm_tai (argc, &argv[1]); break; case NIQUEDIR: pn_quedir = argv[1]; break; case ALIAS: al_tai (argc, &argv[1]); break; } #ifdef DEBUG ll_log (logptr, LLOGFTR, "m_tai (ret=%d)", retval); #endif return (retval); } /**/ extern Table **tb_list; extern int tb_maxtables; extern int tb_numtables; #define CMDTBASE 1 #define CMDTNAME 2 #define CMDTFILE 3 #define CMDTSHOW 4 #define CMDTFLAGS 5 #define CMDTNOOP 6 LOCVAR Cmd cmdtbl[] = { "base", CMDTBASE, 1, "name", CMDTNAME, 1, "file", CMDTFILE, 1, "show", CMDTSHOW, 1, "flags", CMDTFLAGS, 1, "", CMDTNOOP, 0, 0, 0, 0 }; #define CMDTFFILE 1 #define CMDTFDBM 2 #define CMDTFNS 3 #define CMDTFDOMAIN 4 #define CMDTFCHANNEL 5 #define CMDTFROOT 6 #define CMDTFPARTIAL 7 LOCVAR Cmd tbflags [] = { "file", CMDTFFILE, 0, "dbm", CMDTFDBM, 0, "ns", CMDTFNS, 0, "domain", CMDTFDOMAIN, 0, "channel", CMDTFCHANNEL, 0, "root", CMDTFROOT, 0, "partial", CMDTFPARTIAL, 0, 0, 0, 0 }; tb_tai (argc, argv) int argc; char *argv[]; { int ind; register Table *tbptr; #ifdef DEBUG ll_log (logptr, LLOGFTR, "tb_tai (%s: %d)", argv[0], argc); #endif if (tb_numtables >= tb_maxtables) { tai_error ("exceed table size", argv[0], argc, argv); return (NOTOK); } #ifdef DEBUG if (logptr -> ll_level == LLOGFTR) { ll_log (logptr, LLOGFTR, "old tables (%d)", tb_numtables); for (ind = 0; tb_list[ind] != (Table *) 0; ind++) ll_log (logptr, LLOGFTR, "tb(%d) '%s'", ind, tb_list[ind] -> tb_name); } #endif /*NOSTRICT*/ tb_list[tb_numtables++] = tbptr = (Table *) malloc ((unsigned) (sizeof (Table))); tb_list[tb_numtables] = (Table *) 0; tbptr -> tb_name = "notablename"; tbptr -> tb_show = (char *) 0; tbptr -> tb_file = (char *) 0; tbptr -> tb_fp = (FILE *) NULL; tbptr -> tb_pos = 0L; tbptr -> tb_flags = TB_FILE; for (ind = 0; ind < argc; ind++) { if (!lexequ (argv[ind], "=")) { if (ind == 0) { /* ok to default first field only */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "defaulting base '%s'", argv[ind]); #endif goto dobase; } else { #ifdef DEBUG tai_error ("unassignable, bare data field", argv[ind], argc, argv); #endif continue; } } else { ind += 2; #ifdef DEBUG ll_log (logptr, LLOGFTR, "tb '%s'(%d)", argv[ind - 1], argc - ind); #endif switch (cmdsrch (argv[ind - 1], argc - ind, cmdtbl)) { case CMDTBASE: dobase: tbptr -> tb_name = argv[ind]; tbptr -> tb_show = argv[ind]; tbptr -> tb_file = argv[ind]; break; case CMDTNAME: tbptr -> tb_name = argv[ind]; break; case CMDTFILE: tbptr -> tb_file = argv[ind]; break; case CMDTSHOW: tbptr -> tb_show= argv[ind]; break; case CMDTFLAGS: #ifdef DEBUG ll_log (logptr, LLOGFTR, "table flag '%s'", argv[ind]); #endif switch (cmdsrch (argv[ind], 0, tbflags)) { case CMDTFFILE: tbptr -> tb_flags |= TB_FILE; break; case CMDTFDBM: tbptr -> tb_flags |= TB_DBM; break; case CMDTFNS: tbptr -> tb_flags |= TB_NS; break; case CMDTFDOMAIN: tbptr -> tb_flags |= TB_DOMAIN; break; case CMDTFCHANNEL: tbptr -> tb_flags |= TB_CHANNEL; break; case CMDTFROOT: tbptr -> tb_flags |= TB_ROOT; break; case CMDTFPARTIAL: tbptr -> tb_flags |= TB_PARTIAL; break; default: #ifdef DEBUG tai_error ("unknown table flag", argv[ind-1], argc, argv); #endif continue; } break; case CMDTNOOP: break; /* noop */ default: #ifdef DEBUG tai_error ("unknown table parm", argv[ind], argc, argv); #endif break; } } } #ifdef DEBUG ll_log (logptr, LLOGBTR, "tb_tai (ret=YES)"); ll_log (logptr, LLOGFTR, "name=%s, ref=%s, file=%s, flags=%0o", tbptr -> tb_name, tbptr -> tb_show, tbptr -> tb_file, tbptr -> tb_flags); #endif return (YES); } /**/ #include "dm.h" extern Domain **dm_list; extern int dm_maxtables; extern int dm_numtables; #define CMDDBASE 1 #define CMDDNAME 2 #define CMDDDMN 3 #define CMDDSHOW 4 #define CMDDTABLE 5 #define CMDDNOOP 7 #define CMDDLNAME 8 LOCVAR Cmd dmntbl[] = { "base", CMDDBASE, 1, "name", CMDDNAME, 1, "dmn", CMDDDMN, 1, "show", CMDDSHOW, 1, "table", CMDDTABLE, 1, "lname", CMDDLNAME, 1, "", CMDDNOOP, 0, 0, 0, 0 }; dm_tai (argc, argv) int argc; char *argv[]; { int ind; int tbind, showind; char *tbname; register Domain *dmnptr; #ifdef DEBUG ll_log (logptr, LLOGFTR, "dm_tai (%s: %d)", argv[0], argc); #endif if (dm_numtables >= dm_maxtables) { tai_error ("exceed domain table size", argv[0], argc, argv); return (NOTOK); } #ifdef DEBUG if (logptr -> ll_level == LLOGFTR) { ll_log (logptr, LLOGFTR, "old domains (%d)", dm_numtables); for (ind = 0; dm_list[ind] != (Domain *) 0; ind++) ll_log (logptr, LLOGFTR, "dm(%d) '%s'", ind, dm_list[ind] -> dm_name); } #endif /*NOSTRICT*/ dm_list[dm_numtables++] = dmnptr = (Domain *) malloc ((unsigned) (sizeof (Domain))); dm_list[dm_numtables] = (Domain *) 0; dmnptr -> dm_name = "nodomainname"; dmnptr -> dm_domain = (char *) 0; dmnptr -> dm_lname = (char *) 0; dmnptr -> dm_show = (char *) 0; dmnptr -> dm_table = (Table *) 0; tbind = -1; showind = -1; for (ind = 0; ind < argc; ind++) { if (!lexequ (argv[ind], "=")) { if (ind == 0) { /* ok to default first field only */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "defaulting base '%s'", argv[ind]); #endif goto dobase; } else { #ifdef DEBUG tai_error ("unassignable, bare data field", argv[ind], argc, argv); #endif continue; } } else { ind += 2; #ifdef DEBUG ll_log (logptr, LLOGFTR, "dm '%s'(%d)", argv[ind - 1], argc - ind); #endif switch (cmdsrch (argv[ind - 1], argc - ind, dmntbl)) { case CMDDBASE: dobase: dmnptr -> dm_name = argv[ind]; dmnptr -> dm_domain = argv[ind]; showind = ind; break; case CMDDNAME: dmnptr -> dm_name = argv[ind]; break; case CMDDDMN: dmnptr -> dm_domain = argv[ind]; break; case CMDDSHOW: showind = ind; break; case CMDDTABLE: tbind = ind; break; case CMDDLNAME: dmnptr -> dm_lname = argv[ind]; break; case CMDDNOOP: break; /* noop */ default: argv[ind + 1] = (char *) 0; /* eliminate rest of args */ #ifdef DEBUG tai_error ("unknown domain parm", argv[ind-1], argc, argv); #endif break; } } } tbname = (tbind >= 0) ? argv[tbind] : dmnptr -> dm_name; if ((dmnptr -> dm_table = tb_nm2struct (tbname)) == (Table *) NOTOK) { /* table does not already exist */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "'%s' unknown domain table", tbname); #endif if (tb_numtables >= tb_maxtables) { #ifdef DEBUG tai_error ("exceed tb_list table size", tbname, argc, argv); #endif } else { /*NOSTRICT*/ tb_list[tb_numtables++] = dmnptr -> dm_table = (Table *) malloc ((unsigned) (sizeof (Table))); tb_list[tb_numtables] = (Table *) 0; #ifdef DEBUG ll_log (logptr, LLOGBTR, "allocating dm_table addr (%o)", dmnptr -> dm_table); #endif dmnptr -> dm_table -> tb_name = tbname; dmnptr -> dm_table -> tb_file = tbname; dmnptr -> dm_table -> tb_fp = (FILE *) NULL; dmnptr -> dm_table -> tb_pos = 0L; dmnptr -> dm_table -> tb_show = (showind >= 0 ? argv[showind] : tbname); } } #ifdef DEBUG ll_log (logptr, LLOGBTR, "dm_table addr (%o)", dmnptr -> dm_table); #endif dmnptr -> dm_show = (showind >= 0 ? argv[showind] : dmnptr -> dm_name); if (dmnptr -> dm_lname == (char *) 0) dmnptr -> dm_lname = locname; #ifdef DEBUG ll_log (logptr, LLOGBTR, "dm_tai (ret=YES)"); ll_log (logptr, LLOGFTR, "domain '%s'('%s'), table='%s'(%s)", dmnptr -> dm_show, dmnptr -> dm_name, dmnptr -> dm_table -> tb_show, dmnptr -> dm_table -> tb_name); ll_log (logptr, LLOGBTR, "dm_table addr (%o)", dmnptr -> dm_table); #endif return (YES); } /**/ extern Alias *al_list; al_tai (argc, argv) int argc; char *argv[]; { int ind; register Alias *alptr, *palptr; #ifdef DEBUG ll_log (logptr, LLOGFTR, "al_tai (%s: %d)", argv[0], argc); #endif /*NOSTRICT*/ alptr = (Alias *) malloc ((unsigned) (sizeof (Alias))); alptr -> al_flags = 0; alptr -> al_next = NULL; for (ind = 0; ind < argc; ind++) { if (!lexequ (argv[ind], "=")) { if (lexequ (argv[ind], "trusted")) alptr->al_flags |= AL_TRUSTED; else if (lexequ (argv[ind], "nobypass")) alptr->al_flags |= AL_NOBYPASS; else { #ifdef DEBUG tai_error ("unassignable, alias field", argv[ind], argc, argv); #endif continue; } } else { ind += 2; #ifdef DEBUG ll_log (logptr, LLOGFTR, "al '%s'(%d)", argv[ind - 1], argc - ind); #endif if (lexequ(argv[ind-1], "table")) { if ((alptr->al_table = tb_nm2struct(argv[ind])) == (Table *)NOTOK) { #ifdef DEBUG ll_log (logptr, LLOGBTR, "'%s' unknown alias table", argv[ind]); #endif if (tb_numtables >= tb_maxtables) { #ifdef DEBUG tai_error ("exceed tb_list table size", argv[ind], argc, argv); #endif } else { /*NOSTRICT*/ tb_list[tb_numtables++] = alptr->al_table = (Table *) malloc ((unsigned) (sizeof (Table))); tb_list[tb_numtables] = (Table *) 0; alptr -> al_table -> tb_name = argv[ind]; alptr -> al_table -> tb_file = argv[ind]; alptr -> al_table -> tb_fp = (FILE *) NULL; alptr -> al_table -> tb_pos = 0L; alptr -> al_table -> tb_show = argv[ind]; } } } else { argv[ind + 1] = (char *) 0; /* eliminate rest of args */ #ifdef DEBUG tai_error ("unknown alias parm", argv[ind-1], argc, argv); #endif break; } } } if (al_list == (Alias *)0) al_list = alptr; else { for (palptr = al_list; palptr->al_next != NULL; palptr = palptr->al_next); palptr->al_next = alptr; } #ifdef DEBUG ll_log (logptr, LLOGBTR, "al_tai (ret=YES)"); #endif return (YES); } /**/ extern Chan **ch_tbsrch; extern int ch_maxchans; extern int ch_numchans; #define CMDCBASE 1 #define CMDCNAME 2 #define CMDCSHOW 3 #undef Gonzo 4 /* OBSOLETE */ #define CMDCQUE 5 #define CMDCTBL 6 #define CMDCPGM 7 #define CMDCHOST 8 #define CMDCUSER 9 #define CMDCSCRIPT 10 #define CMDCPOLL 11 #define CMDCACCESS 12 #define CMDCNOOP 13 #define CMDCLNAME 14 #define CMDCLDOMN 15 #define CMDCLISRC 16 #define CMDCLOSRC 17 #define CMDCLIDEST 18 #define CMDCLODEST 19 #define CMDCKNOWN 20 #define CMDCCONFSTR 21 #define CMDCAP 22 #define CMDCAUTH 23 #define CMDCTRANS 24 #define CMDCTTL 25 #define CMDCLLOG 26 #define CMDCLLEV 27 LOCVAR Cmd cmdchan[] = { "base", CMDCBASE, 1, "name", CMDCNAME, 1, "show", CMDCSHOW, 1, "que", CMDCQUE, 1, "tbl", CMDCTBL, 1, "pgm", CMDCPGM, 1, "host", CMDCHOST, 1, "user", CMDCUSER, 1, "scr", CMDCSCRIPT, 1, "trn", CMDCTRANS, 1, "poll", CMDCPOLL, 1, "mod", CMDCACCESS, 1, "lname", CMDCLNAME, 1, "ldomain", CMDCLDOMN, 1, "insrc", CMDCLISRC, 1, "outsrc", CMDCLOSRC, 1, "indest", CMDCLIDEST, 1, "outdest", CMDCLODEST, 1, "known", CMDCKNOWN, 1, "confstr", CMDCCONFSTR,1, "ap", CMDCAP, 1, "auth", CMDCAUTH, 1, "ttl", CMDCTTL, 1, "log", CMDCLLOG, 1, "level", CMDCLLEV, 1, "", CMDCNOOP, 0, 0, 0, 0 }; #define CMDPREG 1 #define CMDPBAK 2 #define CMDPPSV 3 #define CMDPDID 4 #define CMDPIMM 5 #define CMDPPICK 6 #define CMDPSEND 7 #define CMDPNOOP 8 LOCVAR Cmd parmchan[] = { "reg", CMDPREG, 0, "bak", CMDPBAK, 0, "psv", CMDPPSV, 0, "imm", CMDPIMM, 0, "pick", CMDPPICK, 0, "send", CMDPSEND, 0, "", CMDPNOOP, 0, 0, 0, 0 }; #define CMDASAME 1 #define CMDA733 2 #define CMDA822 3 #define CMDABIG 4 #define CMDANODOTS 5 #define CMDAJNT 6 LOCVAR Cmd parmap [] = { "same", CMDASAME, 0, "733", CMDA733, 0, "822", CMDA822, 0, "big", CMDABIG, 0, "nodots", CMDANODOTS, 0, "jnt", CMDAJNT, 0, 0, 0, 0 }; #define CMDTFREE 0 #define CMDTINLOG 1 #define CMDTINWARN 2 #define CMDTINBLOCK 3 #define CMDTOUTLOG 4 #define CMDTOUTWARN 5 #define CMDTOUTBLOCK 6 #define CMDTHAU 7 #define CMDTDHO 8 LOCVAR Cmd authmap [] = { "free", CMDTFREE, 0, "inlog", CMDTINLOG, 0, "inwarn", CMDTINWARN, 0, "inblock", CMDTINBLOCK, 0, "outlog", CMDTOUTLOG, 0, "outwarn", CMDTOUTWARN, 0, "outblock", CMDTOUTBLOCK, 0, "hau", CMDTHAU, 0, "dho", CMDTDHO, 0, 0, 0, 0 }; ch_tai (argc, argv) int argc; char *argv[]; { int ind; int tbind, showind; char *tbname; register Chan *chptr; #ifdef DEBUG ll_log (logptr, LLOGFTR, "ch_tai (%s: %d)", argv[0], argc); #endif if (ch_numchans >= ch_maxchans) { #ifdef DEBUG tai_error ("exceed ch table size", argv[0], argc, argv); #endif return (NOTOK); } #ifdef DEBUG if ( logptr -> ll_level == LLOGFTR ) { ll_log (logptr, LLOGFTR, "old channels (%d)", ch_numchans); for (ind = 0; ch_tbsrch[ind] != (Chan *) 0; ind++) ll_log (logptr, LLOGFTR, "ch(%d) '%s'", ind, ch_tbsrch[ind] -> ch_name); } #endif /*NOSTRICT*/ ch_tbsrch[ch_numchans++] = chptr = (Chan *) malloc ((unsigned) (sizeof (Chan))); ch_tbsrch[ch_numchans] = (Chan *) 0; chptr -> ch_name = "nochanname"; chptr -> ch_show = (char *) 0; chptr -> ch_table = (Table *) 0; chptr -> ch_queue = (char *) 0; chptr -> ch_access = 0; /* == DLVRREG */ chptr -> ch_ppath = (char *) 0; chptr -> ch_lname = locname; chptr -> ch_ldomain = locdomain; chptr -> ch_host = (char *) NORELAY; chptr -> ch_login = (char *) NOLOGIN; chptr -> ch_poltime = (char) 8; /* every two hours */ chptr -> ch_script = (char *) 0; chptr -> ch_trans = (char *) DEFTRANS; chptr -> ch_insource = (Table *) 0; chptr -> ch_outsource = (Table *) 0; chptr -> ch_indest = (Table *) 0; chptr -> ch_outdest = (Table *) 0; chptr -> ch_confstr = (char *) 0; chptr -> ch_apout = AP_SAME; chptr -> ch_known = (Table *) 0; chptr -> ch_auth = CH_FREE; chptr -> ch_dead = (Cache *) 0; chptr -> ch_ttl = (time_t)7200; /* dead cache TimeToLive, two hours */ chptr -> ch_logfile = chanlog.ll_file; chptr -> ch_loglevel = chanlog.ll_level; tbind = -1; showind = -1; for (ind = 0; ind < argc; ind++) { if (!lexequ (argv[ind], "=")) { /* not an 'assignment' parameter */ if (ind == 0) { #ifdef DEBUG ll_log (logptr, LLOGBTR, "defaulting base '%s'", argv[ind]); #endif goto dobase; /* default first field only */ } else { #ifdef DEBUG tai_error ("unassignable, bare channel field", argv[ind], argc, argv); #endif continue; } } else { ind += 2; #ifdef DEBUG ll_log (logptr, LLOGFTR, "ch '%s'(%d)", argv[ind - 1], argc - ind); #endif switch (cmdsrch (argv[ind - 1], argc - ind, cmdchan)) { case CMDCBASE: dobase: chptr -> ch_name = argv[ind]; chptr -> ch_queue = argv[ind]; chptr -> ch_ppath = argv[ind]; break; case CMDCNAME: chptr -> ch_name = argv[ind]; break; case CMDCSHOW: showind = ind; break; case CMDCQUE: chptr -> ch_queue = argv[ind]; break; case CMDCTBL: tbind = ind; break; case CMDCPGM: chptr -> ch_ppath = argv[ind]; break; case CMDCHOST: chptr -> ch_host = argv[ind]; break; case CMDCUSER: chptr -> ch_login = argv[ind]; break; case CMDCSCRIPT: chptr -> ch_script = argv[ind]; break; case CMDCTRANS: chptr -> ch_trans = argv[ind]; break; case CMDCPOLL: chptr -> ch_poltime = atoi (argv[ind]); break; case CMDCTTL: /*NOSTRICT*/ chptr -> ch_ttl = (time_t) atoi (argv[ind]) * 60L; break; case CMDCLLOG: chptr -> ch_logfile = argv[ind]; break; case CMDCLLEV: if ((chptr -> ch_loglevel = tai_llev (argc, &argv[ind])) < 0) chptr -> ch_loglevel = chanlog.ll_level; break; case CMDCACCESS: #ifdef DEBUG ll_log (logptr, LLOGFTR, "ch parm '%s'", argv[ind]); #endif switch (cmdsrch (argv[ind], 0, parmchan)) { case CMDPREG: chptr -> ch_access |= DLVRREG; break; case CMDPBAK: chptr -> ch_access |= DLVRBAK; break; case CMDPPSV: chptr -> ch_access |= DLVRPSV; break; case CMDPIMM: chptr -> ch_access |= DLVRIMM; break; case CMDPPICK: chptr -> ch_access |= CH_PICK; break; case CMDPSEND: chptr -> ch_access |= CH_SEND; break; case CMDPNOOP: break; /* noop */ default: #ifdef DEBUG tai_error ("unknown access parm", argv[ind-1], argc, argv); #endif continue; } break; case CMDCNOOP: break; /* noop */ case CMDCLNAME: chptr -> ch_lname = argv[ind]; break; case CMDCLDOMN: chptr -> ch_ldomain = argv[ind]; break; case CMDCLISRC: if ((chptr -> ch_insource = tb_nm2struct (argv[ind])) == (Table *) NOTOK) { #ifdef DEBUG tai_error ("unknown source table", argv[ind], argc, argv); #endif continue; } break; case CMDCLOSRC: if ((chptr -> ch_outsource = tb_nm2struct (argv[ind])) == (Table *) NOTOK) { #ifdef DEBUG tai_error ("unknown source table", argv[ind], argc, argv); #endif continue; } break; case CMDCLIDEST: if ((chptr -> ch_indest = tb_nm2struct (argv[ind])) == (Table *) NOTOK) { #ifdef DEBUG tai_error ("unknown dest table", argv[ind], argc, argv); #endif continue; } break; case CMDCLODEST: if ((chptr -> ch_outdest = tb_nm2struct (argv[ind])) == (Table *) NOTOK) { #ifdef DEBUG tai_error ("unknown dest table", argv[ind], argc, argv); #endif continue; } break; case CMDCKNOWN: if ((chptr -> ch_known = tb_nm2struct (argv[ind])) == (Table *) NOTOK) { #ifdef DEBUG tai_error ("unknown known table", argv[ind], argc, argv); #endif continue; } break; case CMDCCONFSTR: chptr -> ch_confstr = argv [ind]; break; case CMDCAP: #ifdef DEBUG ll_log (logptr, LLOGFTR, "Ap style '%s'", argv[ind]); #endif switch (cmdsrch (argv[ind], 0, parmap)) { case CMDASAME: chptr -> ch_apout = AP_SAME; break; case CMDA733: chptr -> ch_apout = AP_733; break; case CMDA822: chptr -> ch_apout = AP_822; break; case CMDABIG: chptr -> ch_apout |= AP_BIG; break; case CMDANODOTS: chptr -> ch_apout |= AP_NODOTS; break; case CMDAJNT: chptr -> ch_apout = (AP_733 | AP_BIG); break; default: #ifdef DEBUG tai_error ("unknown address style", argv[ind], argc, argv); #endif continue; } break; case CMDCAUTH: #ifdef DEBUG ll_log (logptr, LLOGFTR, "Authorisation '%s'", argv[ind]); #endif switch (cmdsrch (argv[ind], 0, authmap)) { case CMDTFREE: chptr -> ch_auth |= CH_FREE; break; case CMDTINLOG: chptr -> ch_auth |= CH_IN_LOG; break; case CMDTINWARN: chptr -> ch_auth |= CH_IN_WARN; break; case CMDTINBLOCK: chptr -> ch_auth |= CH_IN_BLOCK; break; case CMDTOUTLOG: chptr -> ch_auth |= CH_OUT_LOG; break; case CMDTOUTWARN: chptr -> ch_auth |= CH_OUT_WARN; break; case CMDTOUTBLOCK: chptr -> ch_auth |= CH_OUT_BLOCK; break; case CMDTHAU: chptr -> ch_auth |= CH_HAU; break; case CMDTDHO: chptr -> ch_auth |= CH_DHO; break; default: #ifdef DEBUG tai_error ("unknown auth type", argv[ind], argc, argv); #endif continue; } break; default: #ifdef DEBUG tai_error ("unknown channel parm", argv[ind], argc, argv); #endif break; } } } tbname = (tbind >= 0) ? argv[tbind] : chptr -> ch_name; if ((chptr -> ch_table = tb_nm2struct (tbname)) == (Table *) NOTOK) { /* table does not already exist */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "'%s' unknown table", tbname); #endif if (tb_numtables >= tb_maxtables) { #ifdef DEBUG tai_error ("exceed tb table size", tbname, argc, argv); #endif return (NOTOK); } else { /*NOSTRICT*/ tb_list[tb_numtables++] = chptr -> ch_table = (Table *) malloc ((unsigned) (sizeof (Table))); tb_list[tb_numtables] = (Table *) 0; chptr -> ch_table -> tb_name = tbname; chptr -> ch_table -> tb_file = tbname; chptr -> ch_table -> tb_fp = (FILE *) NULL; chptr -> ch_table -> tb_pos = 0L; chptr -> ch_table -> tb_show = (showind >= 0 ? argv[showind] : tbname); } } chptr -> ch_show = (showind >= 0 ? argv[showind] : chptr -> ch_name); for (ind = 0; ch_tbsrch[ind] != (Chan *) 0; ind++) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "ch(%d) '%s'", ind, ch_tbsrch[ind] -> ch_name); #endif if (ch_tbsrch[ind] == chptr) continue; if (lexequ(ch_tbsrch[ind] -> ch_name, chptr -> ch_name)) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "Replacing channel"); #endif ch_tbsrch[ind] = chptr; ch_tbsrch[--ch_numchans] = (Chan *) 0; } } #ifdef DEBUG ll_log (logptr, LLOGBTR, "ch_tai (ret=YES)"); ll_log (logptr, LLOGFTR, "nam=%s, tbl=%s, que=%s, acc=0%o, path=%s", chptr -> ch_name, chptr -> ch_show, chptr -> ch_queue, chptr -> ch_access, chptr -> ch_ppath); ll_log (logptr, LLOGFTR, "lnam=%s, ldomain=%s, host=%s", chptr -> ch_lname, chptr -> ch_ldomain, chptr -> ch_host); ll_log (logptr, LLOGFTR, "login=%s, pol=%d, script=%s", chptr -> ch_login, (int) (chptr -> ch_poltime), chptr -> ch_script); ll_log (logptr, LLOGFTR, "ipcs=%s, ap=%d", chptr -> ch_confstr, chptr -> ch_apout); #endif return (YES); } /**/ /* The following is one of those ugly buggers that has to play * hairy games, just to do something simple. It uses the full * log-tailoring call, just to convert a logging-level specification * into a number. Yuch. (Dave Crocker) */ tai_llev (argc, argv) /* return logging level value */ int argc; char *argv[]; { LLog faklog; char *fakargv[4]; #ifdef DEBUG ll_log (logptr, LLOGFTR, "tai_llev (%s : %d)", argv[0], argc); #endif fakargv[0] = "="; fakargv[1] = "level"; fakargv[2] = (char *) 0; fakargv[3] = (char *) 0; if (argc <= 0) return (NOTOK); fakargv[2] = argv[0]; /* assign the user's string as the 'value' */ faklog.ll_level = LLOGFST; /* something reasonable? */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "tai_llev before (%d)", faklog.ll_level); #endif if (tai_log (3, fakargv, &faklog) < 0) { #ifdef DEBUG tai_error ("bad log level", argv[0], argc, argv); #endif return (NOTOK); } #ifdef DEBUG ll_log (logptr, LLOGFTR, "tai_llev after (%d)", faklog.ll_level); #endif return (faklog.ll_level); } FTR, "Authorisation '%s'", argv[ind]); #endif switch (cmdsrch (argv[ind], 0, authmap)) { case CMDTFREE: chptr -> ch_auth |= CH_FREE; break; case CMDTINLOG: chptr -> ch_auth |= CH_IN_LOG; break; case CMDTINWARN: chptr -> ch_auth |= CH_IN_WARN; break; case CMDTINBLOCK: chptr -> ch_auth |= CH_IN_BLOCK; mmdf/lib/mmdf/ml_send.c 444 0 12 20131 3671073154 10252 #include "util.h" #include "mmdf.h" /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ /* send a piece of mail, using Unix mail command */ /* Basic sequence is: * * ml_init (YES, NO, "My Name", "The Subject); * ml_adr ("destination address 1"); * ml_adr ("destination address 2"); * ... * ml_aend (); * ml_tinit (); * ml_txt ("Some opening text"); * ml_txt ("maybe some more text"); * ml_file (file-stream-descriptor-of-file-to-include); * if (ml_end (OK)) != OK) * { error-handling code } * * Arguments that are to be defaulted should be zero. * * ml_init's arguments specify a) whether return-mail (to the sender * should be allowed, b) whether a Sender field should be used to * specify the correct sender (contingent on next argument), c) text * for the From field, and d) text for the Subject field. If (b) is * NO, then (c)'s text will be followed by the correct sender * information. * * ml_to and ml_cc are used to switch between To and CC addresses. * Normally, only To addresses are used and, for this, no ml_to call is * needed. * * An "address" is whatever is valid for your system, as if you were * typing it to the mail command. * * You may freely mix ml_txt and ml_file calls. They just append text * to the message. The text must contain its own newlines. * * Note that a special version of the mail command is used, to handle all * the extra arguments. If its sources weren't included with the * distribution of this file, you probably have a problem. */ #include <signal.h> extern struct ll_struct *logptr; extern char *strdup (); extern char *pathmail, /* location of mail command */ *nammail; extern char *cmddfldir; extern int numfds; LOCVAR FILE * ml_fp; /* handle on output to mail command */ LOCVAR int ml_childid; /* process id of mail child */ LOCVAR short ml_curarg; /* index of next argument */ LOCVAR char *ml_argv[100]; /* arguments to pass to execv */ /**/ ml_init (ret, sndr, from, sub) /* set-up for using mail command */ int ret, /* allow return mail to sender? */ sndr; /* include Sender field? */ char *sub, /* subject line */ *from; /* from field */ { ml_argv[0] = strdup ("mail"); if (ret) /* allow return to sender */ ml_curarg = 1; else { /* disable return to sender */ ml_argv[1] = strdup ("-r"); ml_curarg = 2; } if (from != 0) { /* user-specified From field */ ml_argv[ml_curarg++] = strdup ((sndr) ? "-f" : "-g"); /* f => Sender field needed */ ml_argv[ml_curarg++] = strdup (from); } if (sub != 0) { /* user-specified Subject field */ ml_argv[ml_curarg++] = strdup ("-s"); ml_argv[ml_curarg++] = strdup (sub); } return (ml_to ()); /* set-up for To: addresses */ } /**/ ml_to () /* ready to specify To: address */ { ml_argv[ml_curarg++] = strdup ("-t"); return (OK); } ml_cc () /* ready to specify CC: address */ { ml_argv[ml_curarg++] = strdup ("-c"); return (OK); } ml_adr (address) /* a destination for the mail */ char address[]; { ml_argv[ml_curarg++] = strdup (address); return (OK); } ml_aend () /* end of addrs */ { ml_argv[ml_curarg] = 0; return (OK); } /**/ ml_tinit () /* ready to send text */ { Pip pipdes; /* output pipe */ char temppath[128]; register short c; #ifdef DEBUG ll_log (logptr, LLOGBTR, "ml_send arguments:"); for (c = 0; c < ml_curarg; c++) ll_log (logptr, LLOGBTR, "arg(%d) = '%s'", c, ml_argv[c]); #endif if (pipe (pipdes.pipcall)) /* for output to mail */ return (NOTOK); ml_childid = fork (); switch (ml_childid) { case NOTOK: /* bad day all around */ (void) close (pipdes.pip.prd); (void) close (pipdes.pip.pwrt); return (NOTOK); case 0: /* this is the child */ (void) close (0); dup (pipdes.pip.prd); for (c = numfds-1; c > 0; c--) (void) close (c); open ("/dev/null", 1); /* give Submit a place to send msgs */ getfpath (pathmail, cmddfldir, temppath); execv (temppath, ml_argv); exit (NOTOK); } /* BELOW HERE is the parent */ (void) close (pipdes.pip.prd); ml_fp = fdopen (pipdes.pip.pwrt, "w"); /* initialize the stdio for output */ while (--ml_curarg >= 0) /* give the argument list back */ free (ml_argv[ml_curarg]); return (OK); } /**/ ml_file (infp) /* send a file to the message */ register FILE *infp; /* input stdio file stream pointer */ { register short len; char buffer[BUFSIZ]; if ((int) ml_fp == EOF || (int) ml_fp == NULL) return (OK); while ((len = fread (buffer, sizeof (char), sizeof(buffer), infp )) > 0) if (fwrite (buffer, sizeof (char), len, ml_fp) != len) { /* do raw i/o */ ml_end (NOTOK); return (NOTOK); } if (len < OK) { ml_end (NOTOK); return (NOTOK); } return (OK); } ml_txt (text) /* some text for the body */ char text[]; /* the text */ { if (ml_fp == (FILE *) EOF || ml_fp == (FILE *) NULL) return (OK); fputs (text, ml_fp); if (ferror (ml_fp)) { ml_end (NOTOK); return (NOTOK); } return (OK); } /**/ ml_end (type) /* message is finished */ int type; /* normal ending or not */ { short retval; /* wait return value */ switch (ml_childid) { case OK: case NOTOK: return (OK); default: switch ((int)ml_fp) { case OK: case NOTOK: break; default: if (ferror (ml_fp) || type == NOTOK) kill (ml_childid, SIGKILL); fclose (ml_fp); ml_fp = OK; } retval = pgmwait (ml_childid); ml_childid = OK; return ((retval != 0) ? NOTOK : OK); } } /**/ ml_1adr (ret, sndr, from, sub, adr) /* all set-up overhead in 1 proc */ int ret, /* allow return mail to sender? */ sndr; /* include Sender field? */ char *sub, /* subject line */ *from, /* from field */ *adr; /* the one address to receive msg */ { if (ml_init (ret, sndr, from, sub) != OK || ml_adr (adr) != OK || ml_aend () != OK || ml_tinit () != OK) return (NOTOK); return (OK); } ] = strdup (sub); } return (ml_to ()); /* set-up for To: addresses */ } /**/ ml_to () /* ready to specify To: address */ { ml_argv[ml_curarg++] = strdup ("-t"); return (OK); } ml_cc () /* ready to specify CC: address */ { ml_argv[ml_curarg++] = strdup ("-c"); return (OK); } ml_adr (address) /* a destination for the mail */ char address[]; { mmdf/lib/mmdf/gen 555 0 12 57 3620510373 7102 make -f ../../Makefile.com -f Makefile.real $* eturn (OK); } ml_txt (text) /* some text for the body */ char text[]; /* the text */ { if (ml_fp == (FILE *) EOF || ml_fp == (FILE *) NULL) return (OK); fputs (text, ml_fp); if (ferror (ml_fp)) { ml_end (NOTOK); return (NOTOK); } return (OK); } /**/ ml_end (type) /* message is finished */ int type; /* normal mmdf/lib/mmdf/post_tai.c 444 0 12 566 3620510373 10416 #include "util.h" #include "mmdf.h" extern LLog *logptr; /* fake garbage collection of unused external tailor info */ post_tai (argc, argv) int argc; char *argv[]; { char errline[LINESIZE]; arg2vstr (0, sizeof errline, errline, argv); ll_log (logptr, LLOGGEN, "Unprocessed tailor entry '%s'", errline); return (YES); /* we like everthing */ } ; } return (OK); } /**/ ml_end (type) /* message is finished */ int type; /* normal mmdf/lib/mmdf/rtn_proc.c 444 0 12 27250 3671073156 10472 #include "util.h" #include "mmdf.h" /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ /* send return mail to author */ /* Jun 81 D. Crocker rtn_list had non-stdio code testing read-return * Dec 81 D. Crocker rtn_intro did not calculate time-in-queue right * Aug 82 D. Crocker have signature include hostname in name part */ #include "msg.h" #include "adr_queue.h" #define DOWARN 1 /* indicate delay */ #define DOFAIL 0 /* indicate final timeout failure */ extern FILE *mq_rfp; #ifdef line time_t curtime; /* keep lint happy - defined in other module */ #else extern time_t curtime; #endif extern struct ll_struct *logptr; extern long mq_lststrt; extern int warntime; /* initial failure time */ extern int failtime; /* final failure time */ extern char msg_sender[]; extern char *mmdflogin; extern char *mquedir; extern char *aquedir; extern char *locname; /* name of host */ extern char *deadletter; /* Where dead letters go */ extern char *delim1; extern char *delim2; LOCVAR char rtn_nosnd[] = "Failed mail", rtn_yetsnd[] = "Waiting mail"; /**/ rtn_error (themsg, retadr, adrptr, reason) /* problem with an address */ Msg *themsg; char retadr[]; /* return address */ struct adr_struct *adrptr; /* the address */ char reason[]; /* the problem */ { char linebuf[2*LINESIZE], /* one for each line printed */ theaddr[LINESIZE]; /* canonical address */ static char noreason[] = "(Reason not known)"; #ifdef DEBUG ll_log (logptr, LLOGPTR, "rtn_error (%o, '%s@%s', '%s')", msg_cite (themsg -> mg_stat), adrptr->adr_local, adrptr->adr_host ? adrptr->adr_host : "(null)", reason); #endif ll_close (logptr); sprintf (linebuf, "%s (%s)", rtn_nosnd, themsg -> mg_mname); if (rtn_mlinit (linebuf, retadr) != OK) return (NOTOK); /* set up for returning */ rtn_pnam (adrptr, theaddr); sprintf (linebuf, "%s\n'%s' %s: '%s'\n\n", " Your message could not be delivered to", theaddr, "for the following\nreason", (isstr (reason) ? reason : noreason)); ml_txt (linebuf); if (msg_cite (themsg -> mg_stat)) rtn_cite (themsg -> mg_mname); else rtn_all (themsg -> mg_mname); return (ml_end (OK)); } /**/ rtn_warn (themsg, retadr) /* notify of delay in delivery */ Msg *themsg; char *retadr; { char subject[32]; #ifdef DEBUG ll_log (logptr, LLOGPTR, "rtn_warn ()"); #endif sprintf (subject, "%s (%s)", rtn_yetsnd, themsg -> mg_mname); if (rtn_mlinit (subject, retadr) != OK) return (NOTOK); /* set up for returning */ rtn_intro (DOWARN, themsg); /* do a warning introduction */ rtn_cite (themsg -> mg_mname); /* include message citation */ return (ml_end (OK)); } /**/ rtn_time (themsg, retadr) /* notify of delay in delivery */ Msg *themsg; /* just cite the text */ char *retadr; { char subject[32]; #ifdef DEBUG ll_log (logptr, LLOGPTR, "rtn_time (%s)", themsg -> mg_mname); #endif sprintf (subject, "%s (%s)", rtn_nosnd, themsg -> mg_mname); if (rtn_mlinit (subject, retadr) != OK) return (NOTOK); /* set up for returning */ rtn_intro (DOFAIL, themsg); /* do a failure introduction */ if (msg_cite (themsg -> mg_stat)) /* return some part of the message */ rtn_cite (themsg -> mg_mname); else rtn_all (themsg -> mg_mname); return (ml_end (OK)); } /**/ LOCFUN rtn_intro (dowarn, themsg) /* print failure intro */ int dowarn; /* warning or full failure */ Msg *themsg; { int days; int msghour; char linebuf[LINESIZE]; msghour = (int) ((curtime - themsg -> mg_time) / 3600); /* number of hours already in queue */ days = (msghour + 23) / 24; /* round up to nearest whole day */ sprintf (linebuf, " After %d %s (%d hours), your message %s\nfully delivered.", days, ((days == 1) ? "day" : "days"), msghour, (dowarn ? "has not yet been" : "could not be")); ml_txt (linebuf); /* introductory line */ if (dowarn) { ml_txt (" Attempts to deliver the message will continue\n"); days = ((failtime - msghour) + 23) / 24; sprintf (linebuf, "for %d more days.", days < 0 ? 0 : days); ml_txt (linebuf); /* send apologetic nonsense */ ml_txt (" No further action is required by you.\n\n"); ml_txt (" Delivery attempts are still pending for the following address(es):\n\n"); } else ml_txt ("\n\n It failed to be received by the following address(es):\n\n"); rtn_list (FALSE); /* list who hasn't got it yet */ #ifdef NVRCOMPIL Due to popular outrage, I am removing the second list, which shows successful transmissions, after having shown unsuccessful (or not-yet-successfull) ones. In the case of large address lists, this can be obnoxiously long. The text is being retained for reference. (dhc) ml_txt ("\n A copy HAS been sent to the following addressee(s):\n\n"); rtn_list (TRUE); /* list who DID get it */ #endif ml_txt ("\n Problems usually are due to service interruptions at the receiving\n"); ml_txt ("machine. Less often, they are caused by the communication system.\n"); } /**/ LOCFUN rtn_mlinit (subject, retadr) char *subject; char *retadr; { extern char *sitesignature; char fromline[LINESIZE]; #ifdef DEBUG ll_log (logptr, LLOGBTR, "rtn_mlinit (%s)", subject); #endif if (!isstr (retadr)) /* no return address */ { printx ("no return address, "); ll_log (logptr, LLOGTMP, "no return address"); return (NOTOK); } sprintf (fromline, "%s %s <%s@%s>", locname, sitesignature, mmdflogin, locname); /* From field ignores uid */ if (ml_1adr (NO, YES, fromline, subject, retadr) != OK) { /* no return, make Sender field */ ll_err (logptr, LLOGTMP, "ml_1adr"); return (NOTOK); } return (OK); } /**/ LOCFUN rtn_list (dodone) /* list addresses not yet delivered */ short dodone; /* list the ones delivered */ { struct adr_struct adrbuf; int numlist; /* number of addresses listed */ char linebuf[LINESIZE], theaddr[LINESIZE]; #ifdef DEBUG ll_log (logptr, LLOGBTR, "rtn_list (%d)", dodone); #endif for (numlist = 0, mq_setpos (0L); mq_radr (&adrbuf) == OK; ) { /* list all failed recipients */ rtn_pnam (&adrbuf, theaddr); #ifdef DEBUG ll_log (logptr, LLOGFTR, "(%c) '%s'", adrbuf.adr_delv, theaddr); #endif if ((dodone && adrbuf.adr_delv == ADR_DONE) || (!dodone && adrbuf.adr_delv != ADR_DONE) ) { /* format address and print it */ sprintf (linebuf, "\t%s\n", theaddr); #ifdef DEBUG ll_log (logptr, LLOGFTR, "\tlisted"); #endif ml_txt (linebuf); numlist++; } } if (numlist == 0) ml_txt ("\t(none)\n"); /* empty list */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "end of return list"); #endif } /**/ rtn_pnam (adrinfo, buffer) /* print the name of an addressee */ register struct adr_struct *adrinfo; register char *buffer; { #ifdef DEBUG ll_log (logptr, LLOGBTR, "rtn_pnam"); #endif if (adrinfo -> adr_host[0] == '\0') (void) strcpy (buffer, adrinfo -> adr_local); else /* have a host part */ sprintf (buffer, "%s (host: %s) (queue: %s)", adrinfo -> adr_local, adrinfo -> adr_host, adrinfo -> adr_que); } LOCFUN rtn_all (mname) char mname[]; { FILE *msg_tfp; char file[FILNSIZE]; #ifdef DEBUG ll_log (logptr, LLOGBTR, "rtn_all ()"); #endif ml_txt ("\n Your message follows:\n\n"); sprintf (file, "%s%s", mquedir, mname); msg_tfp = fopen (file, "r"); ml_file (msg_tfp); fclose (msg_tfp); } /**/ rtn_cite (mname) char mname[]; { short lines; char linebuf[LINESIZE]; FILE *msg_tfp; char file[FILNSIZE]; #ifdef DEBUG ll_log (logptr, LLOGBTR, "rtn_cite (%s)", mname); #endif ml_txt ("\n Your message begins as follows:\n\n"); sprintf (file, "%s%s", mquedir, mname); if ((msg_tfp = fopen (file, "r")) == NULL) { ll_err (logptr, LLOGTMP, "can't open '%s' msg file '%s'", mname, file); ml_txt (" (Could not open message file.)\n"); return; } while (fgets (linebuf, sizeof linebuf, msg_tfp) != NULL) { ml_txt (linebuf); /* do headers */ if (linebuf[0] == '\n') break; } if (ferror (msg_tfp)) { fclose (msg_tfp); ll_err (logptr, LLOGTMP, "Error reading text for return-to-msg_sender."); return; } if (!feof (msg_tfp)) { for (lines = 3; --lines > 0 && fgets (linebuf, sizeof linebuf, msg_tfp) != NULL;) { if (linebuf[0] == '\n') lines++; /* truly blank lines don't count */ ml_txt (linebuf); } if (!feof (msg_tfp)) /* if more, give an elipses */ ml_txt ("...\n"); } fclose (msg_tfp); } /**/ dead_letter (msgname, reason) char *msgname; char *reason; { FILE *dfp; /* The deadletter file pointer */ FILE *mfp; /* Message related file pointers */ char file[FILNSIZE]; char buf[BUFSIZ]; int nread; #ifdef DEBUG ll_log (logptr, LLOGFAT, "dead_letter (%s, %s)", msgname, reason); #endif if ((dfp = fopen(deadletter, "a")) == NULL) { printx("can't open '%', mail lost.\n", deadletter); #ifdef DEBUG ll_log (logptr, LLOGFAT, "can't open %s", deadletter); #endif return; } fputs (delim1, dfp); /* Message separator */ fprintf (dfp, "Reason: %s\n", reason); sprintf (file, "%s%s", mquedir, msgname); if ((mfp = fopen(file, "r")) == NULL) { fprintf (dfp, "Can't open message file %s\n", file); } else { while ((nread = fread(buf, 1, BUFSIZ, mfp)) > 0) fwrite(buf, 1, nread, dfp); } fputs ("-------End of deadletter, start address list-------\n", dfp); sprintf (file, "%s%s", aquedir, msgname); if ((mfp = fopen(file, "r")) == NULL) { fprintf (dfp, "Can't open address list file %s\n", file); } else { while ((nread = fread(buf, 1, BUFSIZ, mfp)) > 0) fwrite(buf, 1, nread, dfp); } fputs ("-------End of address list-------\n", dfp); fputs (delim2, dfp); /* Message separator */ fclose (dfp); } ne); #endif for (numlist = 0, mq_setpos (0L); mq_radr (&adrbuf) == OK; ) { /* list all failed recipients */ rtn_pnam (&adrbuf, theaddr); #ifdef DEBUG ll_log (logptr, LLOGFTR, "(%c) '%s'", adrbuf.adr_delv, theaddr); #endif if ((dodone && adrbuf.adr_delv == ADR_DONE) || (!dodone && adrbuf.mmdf/lib/mmdf/fnput.c 444 0 12 670 3620510373 7724 #include "util.h" #include "mmdf.h" /* stdio output of n characters, skipping nulls */ fnput (str, count, fp) register char *str; register int count; register FILE *fp; { char *base = str; for (; count-- > 0; str++) { if (isnull (*str)) { if ((str - base) > 0) fwrite(base, sizeof(char), str - base, fp); base = str+1; } } if ((str - base) > 0) fwrite(base, sizeof(char), str - base, fp); } if ((dodone && adrbuf.adr_delv == ADR_DONE) || (!dodone && adrbuf.mmdf/lib/mmdf/getmailid.c 444 0 12 1303 3671073156 10552 /* * G E T M A I L I D . C * * This module is used to get the "mailid" for a user given * his username. Use of the mail-id tables is controlled * by the variable mid_enable. */ #include "util.h" #include "mmdf.h" #include "ch.h" #include <pwd.h> extern LLog *logptr; extern int mid_enable; char * getmailid (username) char *username; { static char buf[MAILIDSIZ+1]; extern Table tb_users; if (mid_enable) { if (tb_k2val (&tb_users, TRUE, username, buf) != OK) return (NULL); buf[MAILIDSIZ] = '\0'; /* Paranoid */ } else (void) strcpy (buf, username); #ifdef DEBUG ll_log (logptr, LLOGFTR, "getmailid returns '%s'", buf); #endif return (buf); } dif ml_txt (linebuf); numlist++; } } if (numlist == 0) ml_txt ("\t(none)\n"); /* empty list */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "end of return list"); #endif } /**/ rtn_pnam (adrinfo, buffer) /* print the name of an addressee */ register struct admmdf/lib/mmdf/getpwmid.c 444 0 12 1536 3640046121 10427 /* * G E T P W M I D . C * * Given a mailid, get the /etc/passwd info for * the user who is associated with that mailid. * If mid_enable is nonzero, then the mailid cannot * be interpreted as a login id, we must lookup * the mailid in a mailid->loginid table. Once * we have the login id, we just do a getpwnam(); * For regular systems, then we assume that the * mailid must be a login id and do a getpwnam(); */ #include "util.h" #include "mmdf.h" #include "ch.h" #include <pwd.h> struct passwd * getpwmid (mailid) char *mailid; { extern struct passwd *gpwlnam(); extern Table tb_mailids; extern int mid_enable; char buf[ADDRSIZE]; if (mid_enable) { if (tb_k2val (&tb_mailids, TRUE, mailid, buf) != OK) return (NULL); return (gpwlnam(buf)); } else return (gpwlnam(mailid)); } o a getpwnam(); * For regular systems, then we assume that the * mailid must be a login id and do a getpwnam(); */ #include "util.h" #include "mmdf.hmmdf/lib/mmdf/cache.c 444 0 12 5016 3620510374 7653 #include "util.h" #include "mmdf.h" #include "ch.h" /* cache some information */ extern Llog *logptr; extern char *strdup(); extern char *malloc(); ca_add (cachep, hostid, value, ttl) /* add entry to cache */ Cache **cachep; register char *hostid; int value; time_t ttl; /* time to live */ { register Cache *cap, *pcap; time_t timenow; #ifdef DEBUG ll_log (logptr, LLOGGEN, "cache add (%s %o %d)", hostid, value, ttl); #endif cap = *cachep; pcap = cap; time(&timenow); for (; cap != NULL; ) { if (lexequ (cap->ca_hostid, hostid)) { /* Already in the list, reset values */ cap->ca_value = value; cap->ca_expire = timenow + ttl; #ifdef DEBUG ll_log (logptr, LLOGPTR, "cache reset (%s, %s, %d)", hostid, rp_valstr (value), ttl); #endif return (OK); } else if (cap->ca_expire <= timenow) { register Cache *tcap = cap; /* Look for deadwood, and expired record */ #ifdef DEBUG ll_log (logptr, LLOGGEN, "cache clear (%s)", cap->ca_hostid); #endif if (cap == pcap) *cachep = pcap = cap = cap->ca_next; else pcap->ca_next = cap = cap->ca_next; free (tcap->ca_hostid); free (tcap); } else { pcap = cap; cap = cap->ca_next; } } /* Not in the list, so we must add */ /*NOSTRICT*/ if ((cap = (Cache *)malloc(sizeof (struct cache))) == NULL) return (NOTOK); cap->ca_hostid = strdup (hostid); cap->ca_expire = timenow + ttl; cap->ca_value = value; cap->ca_next = *cachep; /* Insert at head */ *cachep = cap; return (OK); } /**/ ca_find (cachep, hostid) /* is this host in the cache */ Cache **cachep; register char *hostid; /* key to data in cache */ { register Cache *cap, *pcap; time_t timenow; #ifdef DEBUG ll_log (logptr, LLOGBTR, "ca_find (%s)", hostid); #endif cap = *cachep; pcap = cap; time(&timenow); for (; cap != NULL; ) { /* Look for deadwood */ if (cap->ca_expire <= timenow) { register Cache *tcap = cap; /* Expired record */ #ifdef DEBUG ll_log (logptr, LLOGGEN, "cache clear (%s)", cap->ca_hostid); #endif if (cap == pcap) *cachep = pcap = cap = cap->ca_next; else pcap->ca_next = cap = cap->ca_next; free (tcap->ca_hostid); free (tcap); } else if (lexequ (cap->ca_hostid, hostid)) { /* Bingo! */ #ifdef DEBUG ll_log (logptr, LLOGPTR, "cache hit (%s)", hostid); #endif if (cap != pcap) { /* Move to the head of the class */ pcap->ca_next = cap->ca_next; cap->ca_next = *cachep; *cachep = cap; } return (cap->ca_value); } else { pcap = cap; cap = cap->ca_next; } } return (0); } turn; } if (!feof (msg_tfp)) { for (lines = 3; --lines > 0 && fgets (linebuf, sizeof linebuf, msg_tfp) != NULL;) { if (linebuf[0] == '\n') lines++; /* truly blank lines don't count */ ml_txt (linebuf); } if (!feof (msg_tfp)) /* if more, give an elipses */ ml_txt ("...\n"); } fclose (msg_tfp); } /**/ dead_letter (msgname, reason) char *msgname; char *reason; { FILE *dfp; /* The deadletter fimmdf/lib/mmdf/amp_hdr.c 444 0 12 31631 3671073160 10247 /* Translate off-net address */ /* Apr 81 K. Harrenstein Initially written for SRI * Apr 81 J. Pickens Defensive initialization, blank line after header * May 81 A. Knutsen May have done more SRI Hacking * Jun 81 D. Crocker Changed indenting of data to preserve same * offset (after the colon) as was in input line * minor additional cleanup * Sep 81 A. Knutsen +itmlen >= MAXLEN, rather than > * Apr 82 D. Crocker reset 'fail' at each call to amp_hdr * Jan 83 D. Kingston fixed handling of io with ap_1adr(). * Jun 83 D. Kingston Changed handling of bad addresses. */ #include "util.h" #include "mmdf.h" #include "ap.h" #include "ap_lex.h" #include "ch.h" extern LLog *logptr; extern char *locname; extern char *locdomain; extern char *locmachine; extern int ap_outtype; /* 733 or 822 */ extern int ap_perlev; extern int getach (); extern AP_ptr findap (); #if DEBUG > 1 extern char *namtab[], *typtab[]; /* In ap_1adr.c */ extern char debug; /* In ap_1adr.c */ char tdebug; /* Top-level debug flag */ #endif #define MAXCOL 78 /* # of col positions to use */ #define MAXLEN 20 /* Max length of field name */ LOCVAR int pcol; /* Printing col position */ LOCVAR int nadrs; /* # of addresses hacked */ LOCVAR int nonempty; /* true if we have printed something on * on this line besides the itemname and * and indent whitespace */ LOCVAR int savech; /* Char to re-read, else -1 */ LOCVAR char aread; /* Set when reading address line in header */ LOCVAR int lastch; /* Last char read */ LOCVAR int gotfrom; /* have we got a source yet? */ LOCVAR short itmind; /* indent for continuation lines */ LOCVAR short itmlen; LOCVAR char itmbuf[MAXLEN]; LOCVAR char *itmptr; /* normally == itmbuf except for Resent */ LOCVAR char *afldtab[] = { /* Fields which contain addresses to translate */ "To", "CC", "bcc", "From", "Sender", "Reply-to", "Resent-From", "Resent-Sender", "Resent-To", "Resent-Cc", "Resent-Bcc", /* Archaic */ "Resent-By", "Remailed-From", "Remailed-To", "Remailed-By", "Redistributed-From", "Redistributed-To", "Redistributed-By", 0, }; LOCVAR FILE * xin, *out; LOCVAR long curpos; /* current char position in file */ /* needed because ftell not in v6 */ long amp_hdr (chanptr, ain, aout) Chan *chanptr; /* channel that does not need mapping */ FILE *ain, *aout; { char fromsite[FILNSIZE]; char *ourdomain; register AP_ptr aptmp; int amp_fail; int res; #ifdef DEBUG ll_log (logptr, LLOGBTR, "amp_hdr ()"); #endif xin = ain; out = aout; /* Set global streams */ curpos = 0L; /* reset the file position hack */ gotfrom = FALSE; if (chanptr == (Chan *) 0) { (void) strcpy (fromsite, locname); ourdomain = locdomain; } else { (void) strcpy (fromsite, chanptr -> ch_lname); ourdomain = chanptr -> ch_ldomain; } #ifdef DEBUG ll_log (logptr, LLOGFTR, "otyp=%d, from='%s.%s'", ap_outtype, fromsite, ourdomain); #endif savech = lastch = -1; /* defensive initialization */ aread = FALSE; while (getaitm ()) /* Scan header lines */ { #if DEBUG > 1 if (tdebug) fprintf (stderr, "Parse: "); #endif ap_clear(); amp_fail = FALSE; for (aread = TRUE, out_init (itmlen); ; ) { if (ap_pinit (getach) == (AP_ptr) NOTOK) { /* problem during parse */ ll_log (logptr, LLOGTMP, "problem parsing message"); fprintf (out, "PROBLEM PARSING MESSAGE (%s): ;\n", fromsite); fflush (out); return (NOTOK); } res = ap_1adr (); /* Parse one addr */ if (res == DONE) /* DONE */ { #if DEBUG > 1 if (tdebug) fprintf (stderr, ", DONE."); ll_log (logptr, LLOGBTR, "amp 1adr done"); #endif break; } else if (res == NOTOK) { amp_fail = TRUE; /* pass the garbage and generate warning */ #if DEBUG > 1 ll_log (logptr, LLOGBTR, "amp reject '%s'", namtab[ap_llex]); if (tdebug) fprintf (stderr, "\nReject: %s\n", namtab[ap_llex]); #endif } else /* process the good part */ { #if DEBUG > 1 if (tdebug) { fprintf (stderr, "\n\nAccept\n"); pretty (ap_pstrt); fprintf (stderr, "\n\n"); } #endif /* Here do the check to find sending site, for use with any address that * lacks a host spec. This won't work for multi-level addressing (should * copy ALL site specs seen) but we can worry about that later. The * current algorithm (JRP) is to pick up the first valid hostname field * from either from: or sender:, whichever comes first. The algorithm * really needs two passes, which it doesn't currently have.. If, for * example, a to: field comes before the from: field, then patching will * not occur. Another problem happens when from: comes after sender:, and * from: has no host. In this case from: gets the host of the sender. * Despite these deficiencies, the mapping should work most of the time. * * DPK@BRL, 25 Oct 84 * We now properly handle the use of resent lines. When you get a resent * line, it will be necessary stop using old ourdomain, instead we need to * pickup the sending send from the resent-from line and use that site * to fix resent-{to,cc} lines that lack domains. */ if (prefix("resent-", itmbuf)) { itmptr += 7; gotfrom = FALSE; } else if (prefix("remailed-", itmbuf)) { itmptr += 9; gotfrom = FALSE; } else if (prefix("redistributed-", itmbuf)) { itmptr += 14; gotfrom = FALSE; } /* Fall into ... */ if ((!gotfrom) && (lexequ (itmptr, "from") || lexequ (itmptr, "sender"))) { #if DEBUG > 1 if (tdebug) fprintf (stderr, "Checking fromsite: \""); #endif if (aptmp = findap (ap_pstrt, APV_DOMN)) { gotfrom = TRUE; (void) strcpy (fromsite, aptmp -> ap_obvalue); ourdomain = (char *) 0; /* SEK ap_normalize will add a suitable */ /* domain if not already there */ } #if DEBUG > 1 if (tdebug) fprintf (stderr, "%s\"\n", fromsite); #endif } } res = out_adr (chanptr, fromsite, ourdomain, ap_pstrt); ap_sqdelete (ap_pstrt, (AP_ptr) 0); ap_free (ap_pstrt); /* delete full string */ ap_pstrt = (AP_ptr) 0; if(res == MAYBE) return( (long)res); } (void) putc ('\n', out); if (ap_perlev) { /* If still nested */ #if DEBUG > 1 if (tdebug) fprintf (stderr, "Still nested at level %d!\n", ap_perlev); #endif ll_log (logptr, LLOGTMP, "amp_hdr nested, level %d", ap_perlev); amp_fail++; } if (amp_fail) #ifndef UCL fprintf (out, "MMDF-Warning: Parse error in original version of preceding line at %s.%s\n", locname, locdomain); #else fprintf (out, "MMDF-Warning: Parse error in original version of preceding line at %s.%s.%s\n", locmachine, locname, locdomain); #endif aread = FALSE; } (void) putc ('\n', out); /* Put a blank line after header */ fflush (out); #ifdef DEBUG ll_log (logptr, LLOGBTR, "amp parse done"); #endif switch ((int)ap_pstrt) { case OK: case NOTOK: break; default: ap_sqdelete (ap_pstrt, (AP_ptr) 0); ap_free (ap_pstrt); /* delete full string */ ap_pstrt = (AP_ptr) 0; } return ((ferror(out) || ferror(xin)) ? NOTOK : curpos); } int getach () { register int c; if (savech >= 0) { if(aread && lastch == '\n') return (lastch = 0); /* Double end of file for addrlist */ c = savech; savech = -1; return (lastch = c); } if (feof (xin)) /* in case we are called after getting eof */ return (0); c = getc (xin); if (feof (xin)) /* ended now */ return (0); curpos++; if (aread) if (lastch == '\n' && !(c == ' ' || c == '\t')) { #if DEBUG > 1 ll_log (logptr, LLOGFTR, "getach end of field"); #endif savech = c; /* Save for copying */ return (0); } return (lastch = c); /* xfer one char */ } /* Return true when starting to read from an address field. * itmbuf and itmlen will be set. * Return false when ready to copy message text. */ getaitm () { register int c; register char *cp, **tabv; #ifdef DEBUG ll_log (logptr, LLOGFTR, "getaitm ()"); #endif again: itmptr = itmbuf; /* RESET possible fiddling above */ for (itmlen = 0, cp = itmbuf, itmind = 0; ;) { /* Collect field identifier */ if ((c = getach ()) == (char ) -1) goto err; switch (c) { case '\n': if (itmlen != 0) break; case '\0': return (FALSE); } (void) putc (c, out); /* Continuous "echo" */ switch (c) { case ':': /* Field name collected */ if (itmlen == 0) goto err; *cp = 0; #ifdef DEBUG ll_log (logptr, LLOGFTR, "hdr: '%s'", itmbuf); #endif for (itmind = itmlen + 1; isspace (c = getach ()) && c != '\n'; itmind++) (void) putc (c, out); /* echo the initial white space */ savech = c; /* re-read the non-space */ lastch = 0; /* See if addr-type */ for (tabv = afldtab; *tabv; tabv++) if (lexequ (*tabv, itmbuf)) { if (savech == '\n') { (void) putc (' ', out); itmind++; } return (TRUE); } goto flsh; case ' ': if (itmlen != 0) continue; goto err; /* this should never happen */ default: if (iscntrl (c)) /* not sure why, but... (dhc) */ goto err; *cp++ = c; if (++itmlen >= MAXLEN) /* Fieldname too big, so just */ goto flsh; /* flush and restart */ } } flsh: aread = TRUE; while ((c = getach ()) != 0) (void) putc (c, out); aread = FALSE; goto again; err: #ifdef DEBUG ll_log (logptr, LLOGBTR, "amp getfield fail on char '%c'", c); #endif #if DEBUG > 1 if (tdebug) fprintf (stderr, "Getfield failure, char '%c'\n", c); #endif return (0); } /* * Search parse list for specific object type, returns ptr thereto. * Works if given zero pointer, so findap(ap->ap_chain, FOO) (ie to * find next occurrence of FOO) won't blow up even if * already at end of list. */ AP_ptr findap (aap, nval) AP_ptr aap; { register AP_ptr ap; for (ap = aap; ap && (ap -> ap_obtype != APV_NIL); ap = ap -> ap_chain) if (ap -> ap_obtype == nval) return (ap); else if (ap -> ap_ptrtype == APP_NIL) break; return ((AP_ptr) 0); } #if DEBUG > 1 pretty (ap) register AP_ptr ap; { register int depth; ll_log (logptr, LLOGFTR, "pretty ()"); for (depth = 1;; ap = ap -> ap_chain) { switch (ap -> ap_obtype) { case APV_NIL: ll_log (logptr, LLOGBTR, "end on null ap_obvalue"); fprintf (stderr, "End on null ap_obvalue\n"); return; case APV_EGRP: case APV_EPER: depth -= 2; break; } ll_log (logptr, LLOGFTR, "%.*s%-9s%s%s", depth, " ", typtab[ap -> ap_obtype], ap -> ap_obvalue, (ap -> ap_ptrtype == APP_NXT) ? "\t(NXT)" : ""); fprintf (stderr, "%.*s%-9s%s%s\n", depth, " ", typtab[ap -> ap_obtype], ap -> ap_obvalue, (ap -> ap_ptrtype == APP_NXT) ? "\t(NXT)" : ""); switch (ap -> ap_obtype) { case APV_NGRP: case APV_NPER: depth += 2; } if (ap -> ap_ptrtype == APP_NIL) { ll_log (logptr, LLOGBTR, "end on null pointer"); fprintf (stderr, "End on null pointer\n"); break; } } } #endif out_init (len) short len; { #ifdef DEBUG ll_log (logptr, LLOGBTR, "out_init ()"); #endif nadrs = 0; nonempty = FALSE; pcol = len + 1; } out_adr (chanptr, dflsite, dfldomain, ap) Chan *chanptr; char *dflsite, *dfldomain; register AP_ptr ap; { int len; char *addrp; AP_ptr lrval; #ifdef DEBUG ll_log (logptr, LLOGFTR, "out_adr ()"); #endif if (ap -> ap_obtype == APV_NIL) return(OK); /* Ignore null stuff */ if (nadrs != 0) /* not the first address on the line */ { fputs (", ", out); pcol += 2; } ap = ap_normalize (dflsite, dfldomain, ap, chanptr); if(ap == (AP_ptr)MAYBE) return(MAYBE); #if DEBUG > 1 if (tdebug) pretty (ap); #endif lrval = ap_t2s (ap, &addrp); /* format it */ if(lrval == (AP_ptr)MAYBE) return(MAYBE); else if(lrval == (AP_ptr)NOTOK) return(NOTOK); #ifdef DEBUG ll_log (logptr, LLOGFTR, "out: '%s'", addrp); #endif if ((len = strlen (addrp)) > 0) { /* print it */ if ((pcol += len) >= MAXCOL && nonempty) { pcol = itmind + len; fprintf (out, "\n%.*s", itmind, " "); } fputs (addrp, out); nadrs++; nonempty = TRUE; } free (addrp); return(OK); } se '\0': return (FALSE); } (void) putc (c, out); /* Continuous "echo" */ switch (c) mmdf/lib/mmdf/Makefile.real 444 0 12 12140 3652616275 11056 # # Makefile for general MMDF utility routines # MODULES = phs_note aliasfetch \ ml_send qu_rdmail qu_io qu_fakrply \ hd_format amp_hdr rtn_proc \ mq_rdmail mm_rdmail mm_wtmail mm_io \ ch_struct getmailid getpwmid \ interrupt fnput \ mmdf_init mmdf_fdinit mm_tai uip_tai post_tai \ err_abrt rp_valstr logptr cache ch_llinit alias OBJECTS = phs_note.o aliasfetch.o \ ml_send.o qu_rdmail.o qu_io.o qu_fakrply.o \ hd_format.o amp_hdr.o rtn_proc.o \ mq_rdmail.o mm_rdmail.o mm_wtmail.o mm_io.o \ ch_struct.o getmailid.o getpwmid.o \ interrupt.o fnput.o \ mmdf_init.o mmdf_fdinit.o mm_tai.o uip_tai.o post_tai.o \ err_abrt.o rp_valstr.o logptr.o cache.o \ ch_llinit.o conf.o chan.o alias.o COBJECTS = phs_note.c aliasfetch.c \ ml_send.c qu_rdmail.c qu_io.c qu_fakrply.c \ hd_format.c amp_hdr.c rtn_proc.c \ mq_rdmail.c mm_rdmail.c mm_wtmail.c mm_io.c \ ch_struct.c getmailid.c getpwmid.c \ interrupt.c fnput.c \ mmdf_init.c mmdf_fdinit.c mm_tai.c uip_tai.c post_tai.c \ err_abrt.c rp_valstr.c logptr.c cache.c \ ch_llinit.c ../../conf/$(HOST)/conf.c ../../conf/$(HOST)/chan.c \ alias.c real-default: ../mmdf-made ../mmdf-made: $(OBJECTS) $(AR) r ../libmmdf.a $(OBJECTS) -touch ../mmdf-made -@echo "mmdf routines built normally" # # Special case dependencies # conf.o: ../../conf/$(HOST)/conf.c ../../h/util.h ../../h/mmdf.h \ ../../h/ap_norm.h $(CC) -c $(CFLAGS) ../../conf/$(HOST)/conf.c chan.o: ../../conf/$(HOST)/chan.c ../../h/util.h ../../h/mmdf.h \ ../../h/ch.h ../../h/dm.h $(CC) -c $(CFLAGS) ../../conf/$(HOST)/chan.c lint: $(LINT) -Cmmdf $(LFLAGS) $(COBJECTS) clean: -rm -f *.o x.c makedep eddep make.out errs cachetest llib-lmmdf* # DO NOT DELETE THIS LINE -- make depend uses it phs_note.o: phs_note.c phs_note.o: ../../h/util.h phs_note.o: ../../h/mmdf.h phs_note.o: /usr/include/sys/stat.h phs_note.o: ../../h/ch.h phs_note.o: ../../h/phs.h aliasfetch.o: aliasfetch.c aliasfetch.o: ../../h/util.h aliasfetch.o: ../../h/mmdf.h aliasfetch.o: ../../h/ch.h ml_send.o: ml_send.c ml_send.o: ../../h/util.h ml_send.o: ../../h/mmdf.h ml_send.o: /usr/include/signal.h qu_rdmail.o: qu_rdmail.c qu_rdmail.o: ../../h/util.h qu_rdmail.o: ../../h/mmdf.h qu_rdmail.o: ../../h/adr_queue.h qu_io.o: qu_io.c qu_io.o: ../../h/util.h qu_io.o: ../../h/mmdf.h qu_io.o: /usr/include/sys/stat.h qu_io.o: ../../h/adr_queue.h qu_io.o: ../../h/ch.h qu_io.o: ../../h/phs.h qu_io.o: ../../h/ap.h qu_fakrply.o: qu_fakrply.c qu_fakrply.o: ../../h/util.h qu_fakrply.o: ../../h/mmdf.h qu_fakrply.o: ../../h/chan.h hd_format.o: hd_format.c hd_format.o: ../../h/util.h hd_format.o: ../../h/mmdf.h hd_format.o: ../../h/ch.h amp_hdr.o: amp_hdr.c amp_hdr.o: ../../h/util.h amp_hdr.o: ../../h/mmdf.h amp_hdr.o: ../../h/ap.h amp_hdr.o: ../../h/ap_lex.h amp_hdr.o: ../../h/ch.h rtn_proc.o: rtn_proc.c rtn_proc.o: ../../h/util.h rtn_proc.o: ../../h/mmdf.h rtn_proc.o: ../../h/msg.h rtn_proc.o: ../../h/adr_queue.h mq_rdmail.o: mq_rdmail.c mq_rdmail.o: ../../h/util.h mq_rdmail.o: ../../h/mmdf.h mq_rdmail.o: /usr/include/signal.h mq_rdmail.o: /usr/include/sys/stat.h mq_rdmail.o: ../../h/ch.h mq_rdmail.o: ../../h/msg.h mq_rdmail.o: ../../h/adr_queue.h mm_rdmail.o: mm_rdmail.c mm_rdmail.o: ../../h/util.h mm_rdmail.o: ../../h/mmdf.h mm_wtmail.o: mm_wtmail.c mm_wtmail.o: ../../h/util.h mm_wtmail.o: ../../h/mmdf.h mm_io.o: mm_io.c mm_io.o: ../../h/nexec.h mm_io.o: ../../h/util.h mm_io.o: ../../h/mmdf.h ch_struct.o: ch_struct.c ch_struct.o: ../../h/util.h ch_struct.o: ../../h/mmdf.h ch_struct.o: ../../h/ch.h getmailid.o: getmailid.c getmailid.o: ../../h/util.h getmailid.o: ../../h/mmdf.h getmailid.o: ../../h/ch.h getmailid.o: /usr/include/pwd.h getpwmid.o: getpwmid.c getpwmid.o: ../../h/util.h getpwmid.o: ../../h/mmdf.h getpwmid.o: ../../h/ch.h getpwmid.o: /usr/include/pwd.h interrupt.o: interrupt.c interrupt.o: ../../h/util.h interrupt.o: ../../h/mmdf.h interrupt.o: /usr/include/signal.h fnput.o: fnput.c fnput.o: ../../h/util.h fnput.o: ../../h/mmdf.h mmdf_init.o: mmdf_init.c mmdf_init.o: ../../h/util.h mmdf_init.o: ../../h/mmdf.h mmdf_init.o: ../../h/ch.h mmdf_fdinit.o: mmdf_fdinit.c mmdf_fdinit.o: ../../h/util.h mmdf_fdinit.o: ../../h/mmdf.h mm_tai.o: mm_tai.c mm_tai.o: ../../h/util.h mm_tai.o: ../../h/mmdf.h mm_tai.o: ../../h/cmd.h mm_tai.o: ../../h/ch.h mm_tai.o: ../../h/ap.h mm_tai.o: ../../h/dm.h uip_tai.o: uip_tai.c uip_tai.o: ../../h/util.h uip_tai.o: ../../h/mmdf.h post_tai.o: post_tai.c post_tai.o: ../../h/util.h post_tai.o: ../../h/mmdf.h err_abrt.o: err_abrt.c err_abrt.o: ../../h/util.h err_abrt.o: ../../h/mmdf.h rp_valstr.o: rp_valstr.c rp_valstr.o: ../../h/util.h rp_valstr.o: ../../h/mmdf.h logptr.o: logptr.c logptr.o: ../../h/util.h logptr.o: ../../h/mmdf.h cache.o: cache.c cache.o: ../../h/util.h cache.o: ../../h/mmdf.h cache.o: ../../h/ch.h ch_llinit.o: ch_llinit.c ch_llinit.o: ../../h/util.h ch_llinit.o: ../../h/mmdf.h ch_llinit.o: ../../h/ch.h alias.o: alias.c alias.o: /usr/include/stdio.h alias.o: ../../h/util.h alias.o: ../../h/mmdf.h # DEPENDENCIES MUST END AT END OF FILE # IF YOU PUT STUFF HERE IT WILL GO AWAY # see make depend above pe == nval) return (ap); else if (ap -> ap_ptrtype == APP_NIL) break; return ((AP_ptr) 0); } #if DEBUG > 1 pretty (ap) register AP_ptr ap; { register int depth; ll_log (logptr, LLOGFTR, "pretty ()"); for (depth = 1;; ap = ap -> ap_chain) { switch (ap -> ap_obtype) { case APV_NIL: ll_log (logptr, LLOGBTR, "end on null ap_obvalue"); fprintf (stderr, "End on null apmmdf/lib/mmdf/ch_llinit.c 444 0 12 1213 3664425140 10554 /* * ch_llinit() * * Initialize per channel logging. Called after mmdf_init and * determination of current channel by each channel program * to activate channel specific logging. */ #include "util.h" #include "mmdf.h" #include "ch.h" extern Llog *logptr; extern char *logdfldir; extern char *dupfpath(); ch_llinit (curchan) Chan *curchan; { logptr -> ll_file = curchan -> ch_logfile; if (logdfldir != (char *) 0) logptr -> ll_file = dupfpath (logptr -> ll_file, logdfldir); if (logptr -> ll_level < curchan -> ch_loglevel) logptr -> ll_level = curchan -> ch_loglevel; ll_close (logptr); /* Will cause new values to take effect */ } hd_format.o: ../../h/mmdf.h hd_format.o: ../../h/ch.h amp_hdr.o: amp_hdr.c amp_hdr.o: ../../h/util.h amp_hdr.o: ../../h/mmdf.h amp_hdr.o: ../../h/ap.h amp_hdr.o: ../../h/ap_lex.h amp_hdr.o: ../../h/ch.h rtn_proc.o: rtn_proc.c rtn_proc.o: ../../h/util.h rtn_proc.o: ../../h/mmdf.h rtn_proc.o: ../../h/msg.h rtn_proc.o: ../../h/adr_queue.h mq_rdmail.o: mq_rdmail.c mq_rdmailmmdf/lib/mmdf/mmdf_fdinit.c 444 0 12 710 3620510375 11045 #include "util.h" #include "mmdf.h" extern char *malloc(); int *regfdary; int numfds = 16; /* init to 16 as a failsafe value, -DPK- */ mmdf_fdinit() { register int i; #ifdef V4_2BSD numfds = getdtablesize(); #else #ifdef _NFILE numfds = _NFILE; #endif _NFILE #endif V4_2BSD /*NOSTRICT*/ regfdary = (int *)malloc(numfds * sizeof(int)); regfdary[0] = 0; regfdary[1] = 1; for (i = numfds-1; i > 1; i--) regfdary[i] = NOTOK; } logptr -> ll_file, logdfldir); if (logptr -> ll_level <mmdf/lib/mmdf/aliasfetch.c 444 0 12 1261 3620510375 10712 /* * A L I A S F E T C H . C * * This module hides the multiple file nature of the alias * data. Only submit should need to bypass this. */ #include "util.h" #include "mmdf.h" #include "ch.h" extern Alias *al_list; aliasfetch (first, key, dest, bypass) int first; char *key; char *dest; int bypass; { register Alias *alp; register int badretval = NOTOK; register int ret; for (alp = al_list; alp != (Alias *)0; alp = alp->al_next) { if (bypass && !(alp->al_flags & AL_NOBYPASS)) continue; if ((ret = tb_k2val (alp->al_table, first, key, dest)) == OK) return (OK); if (ret != NOTOK) badretval = ret; /* NS timeout? */ } return (badretval); } .o: ../../h/ch.h amp_hdr.o: amp_hdr.c amp_hdr.o: ../../h/util.h amp_hdr.o: ../../h/mmdf.h amp_hdr.o: ../../h/ap.h amp_hdr.o: ../../h/ap_lex.h amp_hdr.o: ../../h/ch.h rtn_proc.o: rtn_proc.c rtn_proc.o: ../../h/util.h rtn_proc.o: ../../h/mmdf.h rtn_proc.o: ../../h/msg.h rtn_proc.o: ../../h/adr_queue.h mq_rdmail.o: mq_rdmail.c mq_rdmailmmdf/lib/mmdf/alias.c 444 0 12 14422 3671073162 7727 #include <stdio.h> #include "util.h" #include "mmdf.h" #define BSIZE 1024 struct ALIAS { char *key; int cntr; char *names; struct ALIAS *next_int; } *als, alias_ptr, *old_als; int aflag; char *adrptr; char *malloc(); aliasinit( aliasfile ) char *aliasfile; { char alsfilename[128]; FILE *afd; char *akey, *list; char tmpbuf[BSIZE]; aflag = 1; (void) strcpy( alsfilename, aliasfile ); /* ** Open the alias file and store the keys and lists ** in core */ afd = fopen( alsfilename, "r" ); if( afd <= NULL ) { aflag = 0; perror(" Can't open alias file "); } als = &alias_ptr; old_als = NULL; while(1) { if(fgets(tmpbuf, sizeof(tmpbuf), afd) != NULL) { get_key(tmpbuf, &akey, &list); if( old_als > 0 ) { if(( als = (struct ALIAS *) malloc(sizeof(alias_ptr))) <= 0) { perror( "MALLOC ERROR!"); break; } old_als->next_int = als; } als->key = akey; als->cntr = 0; als->names = list; old_als = als; } else { old_als = NULL; break; } } } int linelen, destlen, addrlen; aliasmap(dest, src, thehost) char *dest, *src, thehost[]; { char addr[BSIZE], name[ADDRSIZE], address[ADDRSIZE], temphost[FILNSIZE], defhost[FILNSIZE], hostname[FILNSIZE]; char *ptr; char *sadrptr; int sub_flag; if (thehost != 0) /* save official version of default */ (void) strcpy (defhost, thehost); for (ptr = dest, linelen = 0; *ptr != '\0'; ptr++, linelen++) if (*ptr == '\n') /* get length of last line of header */ linelen = -1; /* make it zero for next character */ destlen = ptr - dest; for (adrptr = src;; ) /* adrptr is state variable, used in */ { /* next_address */ switch (m_next_address (addr)) { case -1: /* all done */ /* clean markers in aliases structure */ als = &alias_ptr; while(als != NULL ){ als->cntr = 0; als = als->next_int; } return; case 0: /* empty address */ continue; } name[0] = address[0] = temphost[0] = '\0'; parsadr (addr, name, address, temphost); if (address[0] == '\0') { /* change the default host */ if (temphost[0] != '\0') (void) strcpy (defhost, temphost); continue; } if( aflag && temphost[0]==0){ als = &alias_ptr; sub_flag = 0; while( als != NULL ){ if( lexequ(address, als->key) && als->cntr == 0 ){ als->cntr++; sadrptr = adrptr; aliasmap(dest, als->names, thehost); adrptr = sadrptr; sub_flag = 1; break; } else if( lexequ(address, als->key) && als->cntr != 0 ){ sub_flag = 1; break; } als = als->next_int; } if(sub_flag == 1) continue; } if (temphost[0] == '\0') (void) strcpy (hostname, defhost); else (void) strcpy (hostname, temphost); if (name[0] != '\0') /* put into canonical form */ { /* name <mailbox@host> */ for (ptr = hostname; !isnull (*ptr); ptr++) *ptr = uptolow (*ptr); /* force hostname to lower */ for (ptr = name; ; ptr++) { /* is quoting needed for specials? */ switch (*ptr) { default: continue; case '\0': /* nope */ sprintf (addr, "%s <%s@%s>", name, address, hostname); goto doaddr; case '<': /* quoting needed */ case '>': case '@': case ',': case ';': case ':': sprintf (addr, "\"%s\" <%s@%s>", name, address, hostname); goto doaddr; } } } else { sprintf (addr, "%s@%s", address, hostname); } doaddr: addrlen = strlen (addr); if ((destlen + addrlen) < (BSIZE - 4)) { /* it will fit into dest buffer */ if (destlen == 0) (void) strcpy (dest, addr); else /* monitor line length */ { if ((linelen + addrlen) < 70) sprintf (&dest[destlen], ", %s", addr); else { sprintf (&dest[destlen], ",\n %s", addr); linelen = 0; destlen += 1; } destlen += 2; linelen += 2; } destlen += addrlen; linelen += addrlen; } else { fprintf(stderr,"WARNING: Header line exceeds buffer size!\n"); fprintf(stderr,"\tIt was truncated to legal length.\n"); break; } } } get_key( s, okey, olist ) char *s, **okey, **olist; { char tmps[512]; char *ptmps; int len_list; /* ** This routine takes the input string s and divides it into two parts, ** the first of which consists of the first word in the original string. ** It is returned in okey. The second part is a list of the remaining names. ** They are returned in the olist. Malloc is used to allocate the necessary ** storage for the sublists. */ /* throw away initial white space */ while( *s == ' ' || *s == '\t' ) *s++; /* get the first word. It may be terminated by spaces, tabs, commas, */ /* newlines or end-of-file */ ptmps = tmps; while( *s != ' ' && *s != '\t' && *s != ',' && *s != '\n' && *s != EOF){ *ptmps++ = *s++; } *ptmps = 0; len_list = strlen(tmps); if(( *okey = malloc( (unsigned) (len_list) + 1 )) <= 0 ) { perror("MALLOC ERROR!"); return(-1); } (void) strcpy( *okey, tmps ); /* and now the rest of the list */ ptmps = s; while( *s != '\n' && *s != EOF && *s != '\0' ) *s++; *s = 0; len_list = strlen( ptmps ); if(len_list <= 0 ) return( -1 ); /* no list - ERROR - ERROR */ if((*olist = malloc( (unsigned) (len_list) +1 )) <= 0) { perror("MALLOC ERROR!"); return( -1 ); } (void) strcpy( *olist, ptmps ); return(0); } m_next_address (addr) char *addr; { int i = -1; /* return -1 = end; 0 = empty */ for (;;) switch (*adrptr) { default: addr[++i] = *adrptr++; continue; case '"': for (addr[++i] = *adrptr++; !isnull (*adrptr); ) { addr[++i] = *adrptr; if (*adrptr++ == '"') break; } continue; case '(': for (addr[++i] = *adrptr++; !isnull (*adrptr); ) { addr[++i] = *adrptr; if (*adrptr++ == ')') break; } continue; case '\n': case ',': addr[++i] = '\0'; adrptr++; return (i); case '\0': if (i >= 0) addr[++i] = '\0'; return (i); } } st, temphost); continue; } if( aflag && temphost[0]==0){ als = &alias_ptr; sub_flag = 0; while( als != NULL ){ if( lexequ(address, als->key) && als->cntr == 0 ){ als->cntr++; sadrptr = adrptr; aliasmap(dest, almmdf/lib/dial/ 755 0 12 0 3671352204 6347 mmdf/lib/dial/d_coding.c 444 0 12 17246 3620510376 10376 # include "util.h" # include "d_proto.h" # include "d_returns.h" # define TAB '\11' /* tab */ # define LINEFEED '\12' /* line feed */ # define FORMFEED '\14' /* form feed */ # define CR '\15' /* carriage return */ /* speed optimization */ #define D_TESTBIT(b,v) ((!isascii(b))||((v)[(b)>>4]&(1<<((b)&017)))) /* * D_BLDCVEC * * this routine builds a code vector from a hex digit string which * specifies 'illegal' characters for transmission to a host. each * of the digits in the input vector to be sure that it is a legal * hex digit. * * hexvec -- pointer to a 32 character string of hex digits. * * codevec -- pointer to an 8 word bit map table for the output. */ d_bldcvec(hexvec, codevec) register char *hexvec; unsigned short codevec[]; { extern int d_errno; register int word, digit; unsigned short bits; int value; /* each word of the code vector is constructed from 4 hex digits */ for (word = 0; word <= 7; word++) { bits = 0; for (digit = 0; digit <= 3; digit++) { if ((value = d_fromhex(*hexvec++)) < 0) { #ifdef D_LOG d_log("d_bldcvec", "bad hex digit in path illegal character field"); #endif D_LOG d_errno = D_PACKERR; return(D_FATAL); } bits |= (value & 017) << (4 * digit); } codevec[word] = bits; } return(D_OK); } /* * D_BLDHVEC * * this routine takes a code vector and builds a string of 32 hex * digits for transmission in a RESET packet. * * codevec -- pointer to the code vector to be converted * * hexvec -- pointer to place to load the 32 hex digits */ d_bldhvec(codevec, hexvec) unsigned short codevec[]; register char *hexvec; { register unsigned bits, digit; int word; for (word = 0; word <= 7; word++) { bits = codevec[word]; for (digit = 0; digit <= 3; digit++) { *hexvec++ = d_tohex(bits & 017); bits >>= 4; } } *hexvec = '\0'; return(D_OK); } /* * D_DECODE * * this routine takes encoded strings and forms the plaintext * output. * * input -- input string to be decoded * * ninput -- the number of characters in the input string * * output -- pointer to place for the decoded output * * the routine returns the number of output characters. */ d_decode(input, ninput, output) register char *input, *output; register int ninput; { extern char d_rcvesc; extern int d_errno; int chigh, clow, noutput; noutput = 0; /* cycle through the input string. if a character isn't the escape, just */ /* copy it to the output. */ while (ninput) { if (*input != d_rcvesc) { *output++ = *input++; ninput--; noutput++; continue; } /* must be the escape character. if the next one is the escape also */ /* then load one of them into the output. otherwise switch for the */ /* special cases. */ input++; if (*input == d_rcvesc) { *output++ = *input++; ninput -= 2; noutput++; continue; } switch (*input) { case 'I': *output++ = TAB; break; case 'J': *output++ = LINEFEED; break; case 'L': *output++ = FORMFEED; break; case 'M': *output++ = CR; break; default: chigh = d_fromhex(*input++); clow = d_fromhex(*input++); if ((chigh < 0) || (clow < 0)) { #ifdef D_LOG d_log("d_decode", "illegal character encoding"); #endif D_LOG d_errno = D_PACKERR; return(D_FATAL); } *output++ = (chigh << 4) | (clow & 017); noutput++; ninput -= 3;; continue; } input++; ninput -= 2; noutput++; } return(noutput); } /* * D_CCODE * * this routine computes the encoding for a single character, if any, * based on the bit vector which specifies whether the target host * can receive the character without undesired side effects. * * c -- the character to be encoded * * string -- place to put the encoding */ d_ccode(c, string) register unsigned c; register char *string; { extern unsigned short d_lcvec[]; extern char d_snesc; /* if we're encoding the escape character itself, just double it. */ if (c == d_snesc) { *string++ = d_snesc; *string = d_snesc; return(2); } /* if the bit corresponding to the character is on in the vector, then we */ /* have to encode it. switch for special cases, otherwise use hex digits */ if (D_TESTBIT(c, d_lcvec) == 0) { *string = c; return(1); } *string++ = d_snesc; switch (c) { case TAB: *string = 'I'; return(2); case LINEFEED: *string = 'J'; return(2); case FORMFEED: *string = 'L'; return(2); case CR: *string = 'M'; return(2); default: *string++ = d_tohex(c >> 4); *string++ = d_tohex(c & 017); return(3); } } /* * D_TESTBIT * * this routine is called to test if a given bit is on in a bit vector * the routine returns 1 if it is, 0 otherwise. * * bit -- the bit to test * * bitvector -- the bit vector */ d_testbit(bit, bitvector) register int bit; register unsigned short bitvector[]; { return(D_TESTBIT(bit,bitvector)); } /* * D_SETBIT * * this routine is called to set a bit in a bit vector. * * bit -- the bit to set * * bitvector -- the bit vector to set it in */ d_setbit(bit, bitvector) register int bit; register unsigned short bitvector[]; { bit &= 0177; bitvector[bit >> 4] |= (1 << (bit & 017)); return(D_OK); } /* * D_ORBITVEC * * this routine computes a word by word logical or of the input bit * vectors * * a, b -- the input bit vector * * out -- the bit vector which becomes the bit-wise 'or' of a and b */ d_orbitvec(a, b, out) register unsigned short a[], b[], out[]; { int word; for (word = 0; word < 8; word++) out[word] = a[word] | b[word]; return(D_OK); } /* * D_TOHEX * * routine which returns the value of its argument as a single hex * character * * value -- the value to be converted */ d_tohex(value) register int value; { if ((value >= 0) && (value <= 9)) return(value + '0'); if ((value >= 10) && (value <= 15)) return(value - 10 + 'A'); return(-1); } /* * D_FROMHEX * * routine which returns the value of the hex character given as the * argument. * * c -- the character whose value is desired */ d_fromhex(c) register int c; { if ((c >= '0') && (c <= '9')) return(c - '0'); if ((c >= 'A') && (c <= 'F')) return(c - 'A' + 10); return(-1); } /* * D_SETILL * * this routine is used to copy the illegal character bit vectors * * in -- pointer to the source bit vector * * out -- pointer to the destination bit vector */ d_setill(in, out) register unsigned short in[], out[]; { register int word; for (word = 0; word < 8; word++) out[word] = in[word]; return(D_OK); } high = d_fromhex(*input++); clow = d_fromhex(*input++); if ((chigh < 0) || (clow < 0)) { #ifdef D_LOG d_log("d_decode", "illegal character encoding"); #endif D_LOG d_errno = D_PACKERR; return(D_FATAL); } *output++ = (chigh << 4) | (clow &mmdf/lib/dial/d_assign.c 444 0 12 2374 3671073163 10377 # /* * D _ A S S I G N . C * * Doug Kingston, BRL, 4 August 81 * * Takes the name of an assignable device as an argument. * The function calles the program /bin/assign to get a device * for exclusive use. */ #include "d_returns.h" #ifdef DOASSIGN struct { char lobyte; char hibyte; }; extern int d_errno; d_assign (devname) char *devname; { int status; if ((status = d_tryfork()) == -1) { d_log ("d_assign", "couldn't fork"); d_errno = D_FORKERR; return( D_FATAL ); } if (status == 0) { /* CHILD */ (void) close (1); open ("/dev/null", 1); execl ("/bin/assign", "mmdf-assign", devname, (char *)0); exit (99); } else { /* PARENT */ wait (&status); return (status.hibyte == 0 ? D_OK : D_NO); } } d_deassign (devname) char *devname; { int status; if ((status = d_tryfork()) == -1) { d_log ("d_deassign", "couldn't fork"); return( D_NO ); } if( status == 0 ) { /* CHILD */ (void) close (1); open ("/dev/null", 1); execl ("/bin/assign", "mmdf-assign", "-", devname, (char *)0); exit (99); } else { /* PARENT */ wait (&status); return (status.hibyte == 0 ? D_OK : D_NO); } } #else /* Fake routine for systems without "assign" */ d_assign() { return( D_OK ); } d_deassign() { return( D_OK ); } #endif t; register unsigned short bitvector[]; { return(D_TESTBIT(bit,bitvector)); } /* * D_SETBIT * * this routine is called to set a bit in a bit vector. * * bit -- the bit to set * * bitvector -- the bit vector to set it in */ mmdf/lib/dial/d_script2.c 444 0 12 7455 3671073163 10506 # # include <stdio.h> # include "d_proto.h" # include "d_returns.h" # include "d_structs.h" # define MAXFILES 15 extern char *strcpy (); extern char *strdup (); extern unsigned d_scline; extern char d_scfile[]; extern int d_nfields; extern char *d_fields[]; extern int errno; FILE *d_scfp; /* file pointer for script file */ static struct opfile *d_files[MAXFILES]; static int d_nopen; /* * D_SCOPEN * * this routine opens the script file and sets up a few globals for * use by later routines. * * scriptfile -- path name of the script file to be used. */ d_scopen (scriptfile, nfields, fields) int nfields; char scriptfile[], *fields[]; { char *calloc(); register int index, i; /* Don't let them open so many files that they overrun my array */ if (d_nopen > MAXFILES) { #ifdef D_LOG d_log ("d_scopen", "Too many files opened"); #endif D_LOG return (D_FATAL); } /* If this is any other than the first file to be opened, * then I alloc space for it and save status info. */ if (d_nopen != 0) { index = d_nopen - 1; /*NOSTRICT*/ d_files[index] = (struct opfile *)calloc (1, sizeof (struct opfile)); if (d_files[index] == 0) { d_log ("d_scopen", "couldn't alloc space for struct"); return (D_FATAL); } d_files[index]->o_line = d_scline; d_files[index]->o_chan = d_scfp; (void) strcpy (d_files[index]->o_fname, d_scfile); d_files[index]->o_nfields = d_nfields; for (i=0; i < d_nfields; i++) { d_files[index]->o_fields[i] = d_fields[i]; free(d_fields[i]); } d_dbglog ("d_scopen", "Saving %d fields; loading %d new ones", d_nfields, nfields); for (i=0; i < nfields; i++) d_fields[i] = strdup(fields[i+1]); d_nfields = nfields-1; } d_nopen++; if ((d_scfp = fopen(scriptfile, "r")) == NULL) { #ifdef D_LOG d_log ("d_scopen", "Can't open script file '%s' (errno %d)", scriptfile, errno); #endif D_LOG /* restore the old file setup incase this was part of a failed * alternate. */ d_scclose(); return (D_FATAL); } d_scline = 0; (void) strcpy (d_scfile, scriptfile); #ifdef D_LOG d_log ("d_scopen", "script file '%s' being used", scriptfile); #endif D_LOG return (D_OK); } d_scclose() { register int index, i; if (d_nopen <= 0) return (0); fclose (d_scfp); #ifdef D_LOG d_log ("d_scclose", "Closing script file '%s'", d_scfile); #endif D_LOG if (--d_nopen > 0) { index = d_nopen - 1; d_scfp = d_files[index]->o_chan; (void) strcpy (d_scfile, d_files[index]->o_fname); d_scline = d_files[index]->o_line; d_nfields = d_files[index]->o_nfields; for (i = 0; i < d_nfields; i++) d_fields[i] = d_files[index]->o_fields[i]; free (d_files[index]); #ifdef D_LOG d_log ("d_scclose", "Resuming use of script file '%s'", d_scfile); #endif D_LOG return (d_nopen); } return (0); } /* * D_SCRGETLINE * * this routine is called to read a line in from the script file. it * handles the possibility of an escaped newline. * * linebuf -- place to load the line */ d_scgetline(linebuf, channel) char *linebuf; FILE *channel; { register int c, count; register char *cp; count = 0; cp = linebuf; d_scline++; while ((c = getc(channel)) != EOF) { if (c == '\n') { /* if newline, see if it's escaped */ if ((cp > linebuf) && (cp[-1] == '\\')) { cp--; count--; d_scline++; continue; } break; } if (++count > MAXSCRLINE) { /* line is too long */ d_scerr("line too long"); return(D_FATAL); } *cp++ = c; } /* The line parsing routine actually needs two nulls at the * end to guarentee that it doesn't skip it. */ *cp++ = '\0'; *cp = '\0'; return(count); } == NULL) { #ifdef D_LOG d_log ("d_scopen", "Can't open script file '%s' (errno %d)", scriptfile, errno); #endif D_LOG /* restore the old file setup incase this was part of a failed * alternate. mmdf/lib/dial/d_control.c 444 0 12 3621 3620510377 10564 # # include "d_proto.h" # include "d_returns.h" /* * D_CONTROL * * This routine tries to do something with unexpected * packet types. If 'rdport' is looking for one packet * type and gets another, then this routine is called. * This allows certain control type packets to be sent * and processed without prior arrangements. If the * packet is not one of these control packets, then an * error is returned. * * packet - a pointer to the start of the packet. * */ d_control (packet, length) char *packet; int length; { int dig1, dig2; extern int d_nbuff, d_errno, d_rcvseq; /* switch out to the code to process this packet */ switch (packet[TYPEOFF]) { case NBUFF: /* This is a control packet indicating whether buffering * is allowed. */ dig1 = d_fromhex(packet[TEXTOFF]); dig2 = d_fromhex(packet[TEXTOFF + 1]); /* This should stylistically be done elsewhere, but nobody * else will know we got this packet. */ d_rcvseq = d_incseq (d_rcvseq); /* Make sure the characters are reasonable */ if ((dig1 < 0) || (dig2 < 0) ) { #ifdef D_LOG d_log ("d_control", "NBUFF: Bad hex char in packet ('%c%c')", packet[LHEADER], packet[LHEADER+1]); #endif D_LOG return (D_NONFATAL); } /* transfer to the buffer counter */ d_nbuff = (dig1 << 4) | dig2; return (D_OK); case QUIT: #ifdef D_LOG d_log ("d_control", "received QUIT packet"); #endif D_LOG if (length == LQUIT) { d_errno = D_RCVQUIT; return (D_FATAL); } #ifdef D_LOG d_log ("d_control", "Quit packet bad length (%d)", length); #endif D_LOG return (D_NONFATAL); default: /* If the packet type is not one of the special control * packets, then just log it as an error. */ #ifdef D_LOG d_log ("d_control", "Bad packet type '%s'", packet); #endif D_LOG return (D_NONFATAL); } } error is returned. * * packet - a pointer to the start of the packet. * */ d_control (packet, length) chammdf/lib/dial/mktran.c 444 0 12 13567 3641232116 10122 # /* Program to help make a transcript file */ #include <stdio.h> #include "d_returns.h" #include "ll_log.h" #include "d_proto.h" #include "d_script.h" #define SCRIPT "Script" #define TRANFILE "Tranfile" #define LOGFILE "Log" struct ll_struct log = { "Log", "mktran log", '\0177', 100, 0, 0, 0, }; FILE *input, *output; char *sfile; main (argc, argv) int argc; char **argv; { register int command; char linebuf[MAXSCRLINE +2], *fields[MAXFIELDS]; register int result; int nfields; /* init various things: logging, files, etc. */ init (argc, argv); /* run through that part of the script which already exists */ printf("Starting to process existing script.\n"); if ((result = doscript ()) < 0) { printf("Problem encountered in processing existing script.\n"); printf("Return value of %d\n", result); printf("Aborting.\n"); exit(-1); } printf("Existing script processed successfully\n"); /* Now add more to it */ for (;;) { /* Read a new command line from the terminal */ printf ("Enter command: "); fflush (stdout); command = d_cmdget (linebuf, &nfields, fields, stdin); if (command < 0) { /* may be an error; may also be the command to turn * the line around. Check it. */ if (turnar (linebuf)) doread(); else { printf ("Unrecognized command. getcmd returns %d\n", command); printf ("Try again\n"); } continue; } /* write the new line to the script */ wrtscr (); /* process the new line */ result = d_cmdproc (command, nfields, fields); /* check on the results of the processing */ switch (result) { case D_CONTIN: continue; case D_OK: quit (); case D_FATAL: printf ("Fatal error on command line. Abort\n"); quit (); } } } init (argc, argv) int argc; char **argv; { extern int errno; extern FILE *d_scfp; extern int d_master, d_snseq, d_rcvseq; extern int d_debug; register int result; register int word; printf("Usage: %s [<script file> [<debug value> [<transcript file>]]]\n", argv[0]); /* First, determine the name of the script file */ if (argc > 1) sfile = argv[1]; else sfile = SCRIPT; /* open it for appending */ output = fopen (sfile, "a"); if (output == NULL) { printf ("Can't open '%s': errno %d - ", sfile, errno); fflush (stdout); perror (""); exit (-1); } fseek (output, 0L, 2); /* check for debugging */ if (argc > 2) d_debug = atoi (argv[2]); else d_debug = 0; /* open the log and transcript files */ if ((result = d_opnlog (&log)) < 0) { printf("Couldn't open log file; d_opnlog returns %d\n", result); exit(-1); } d_init (); /* check to see if a transcript is wanted */ if (argc > 3) if (atoi (argv[3]) != 0) if ((result = d_tsopen (TRANFILE)) < 0) return (result); d_master = 1; d_snseq = 3; d_rcvseq = 3; /* at last, more or less done */ return (0); } wrtscr () { extern char d_origlin[]; register int length, nwrote; length = strlen (d_origlin); nwrote = fwrite (d_origlin, sizeof (char), length, output); if (nwrote != length) { printf ("fwrite tried %d, but returned %d\n", length, nwrote); printf ("Couldn't write to script file: "); fflush (stdout); perror (""); fflush (stderr); exit (-1); } return; } doscript () { extern FILE *d_scfp; extern int errno; int nselect, result; char linebuf[MAXSCRLINE + 2], *fields[MAXFIELDS]; /* open the script file */ if ((result = d_scopen (sfile, 0, (char **) 0)) < 0) { printf ("Couldn't open '%s': errno %d - ", sfile, errno); fflush (stdout); perror (""); fflush (stderr); exit (-1); } /* now process it as a normal script with the exception that * an EOF means read from the standard input instead of stop. */ for (;;) { result = d_scrblk (); switch (result) { case D_OK: return (D_OK); case D_EOF: case D_NFIELDS: case D_QUOTE: case D_UNKNOWN: case D_FATAL: /* a fatal error */ return (D_FATAL); case D_EOF: /* a normal termination since we expect the user to * add more immediately. */ return (0); case D_NONFATAL: /* An error that may be recoverable. If there is * an alternate, use it. */ if (nselect <= 0) /* Not within a select block; therefore, not alt */ return (D_FATAL); /* We are within a select block; go to the alternate */ if ((result = d_nxtalt ()) < 0) return (result); switch (result) { case S_ALT: continue; case S_SELEND: /* no more alternates; return error */ return (D_FATAL); default: d_scerr ("Bad syntax scriptfile"); return (D_FATAL); } case S_SELST: /* The next line had better be an alternate */ if (d_cmdget (linebuf, &nfields, fields, d_scfp) != S_ALT) { d_scerr ("Missing 'alternate' after 'begin'"); return (D_FATAL); } nselect++; continue; case S_SELEND: /* verify that there was a select block being * looked at. If so, then the last alternate * was the successful one. Continue normally. */ if (nselect-- <= 0) { d_scerr ("Inappropriate select end"); return (D_FATAL); } continue; case S_ALT: /* First, make sure the context was correct */ if (nselect <= 0) { d_scerr ("Inappropriate alt\n"); return (D_FATAL); } /* If we get here, then an alternate within a select * was completed successfully. Move on. */ if ((result = d_selend ()) < 0) return (result); nselect--; switch (result) { case S_SELEND: continue; default: d_scerr ("error in format of script file"); return (D_FATAL); } } } } quit () { printf ("Quitting\n"); exit (0); } open (TRANFILE)) < 0) return (result); d_master = 1; d_snseq = 3; d_rcvseq = 3; /* at last, more or less done */ mmdf/lib/dial/d_port.c 444 0 12 22315 3664425142 10114 # include "util.h" # include "d_proto.h" # include "d_returns.h" # include <signal.h> # include "d_syscodes.h" # include <stdio.h> # include "d_structs.h" /* Jun 81 D. Crocker Major reworking of alarm usage, trying to * deal with fact that there may be multiple * calls to d_rdport trying to get one good packet * d_rdport fixed to handle 8th bit on * Converted some d_dbglogs to d_log * Jul 81 D. Crocker Skip initial non-hex text on line. * * Sep 83 M. Laubach added procedure d_waitprompt to interface * to HP and IBM software prompting * * Mar 84 D. Rockwell Added select call to d_wrtport, removed alarm */ extern int d_errno, errno; extern int d_prompt; /* prompt char from script */ LOCVAR unsigned d_alrmtim; /* time to wait for i/o to complete */ d_alarm (thetime) unsigned thetime; { d_alrmtim = thetime; } /* * * D_RDPORT * * this routine is called to read a packet from the port. the packets * are checked for length errors, bad checksums, and to make sure that * we aren't receiving packets that we have sent ourselves. * * packet -- place to load te incoming packet. should be large enough to * contain a packet one character longer than the maximum. the * newline is replaced by a null. * * returns the length of the incoming packet, minus the newline. * * D_FATAL -- fatal error from read call * * D_NONFATAL -- eof from port */ d_rdport (packet) char *packet; { extern FILE * d_prtfp; extern int d_master, d_rcvseq; int flags, seqnum; register char *op; register int count; register int c; if (d_alrmtim == 0) { #ifdef D_LOG d_log("d_rdport", "*** no more alarm time left"); #endif D_LOG d_errno = D_TIMEOUT; return(D_INTRPT); } if (setjmp (timerest)) { #ifdef D_LOG d_log("d_rdport", "read alarm"); #endif D_LOG d_errno = D_TIMEOUT; return(D_INTRPT); } for (s_alarm (d_alrmtim); ; ) { /* get valid packet */ for (op = packet, count = 0; count <= MAXPACKET + 1; ) { /* get a line */ if ((c = d_getc (d_prtfp)) == EOF) { /* truly an end of file? */ if (ferror (d_prtfp)) { d_alrmtim = s_alarm (0); if (errno == EINTR) { #ifdef D_LOG d_log("d_rdport", "read alarm"); #endif D_LOG d_errno = D_TIMEOUT; return(D_INTRPT); } #ifdef V4_2BSD /* * This is a special for 4.2 systems -- a bug in * the tty driver causes EWOULDBLOCK to be returned * instead of an EOF on the tty. So, we fake it. * The messages are distinguishable. */ else if (errno == EWOULDBLOCK) { #ifdef D_LOG d_log("d_rdport", "port read EOF"); #endif D_LOG d_errno = D_PORTEOF; return(D_NONFATAL); } #endif V4_2BSD else { #ifdef D_LOG d_log("d_rdport", "port read errno %d", errno); #endif D_LOG d_errno = D_PORTRD; return(D_FATAL); } } if (feof (d_prtfp)) { d_alrmtim = s_alarm (0); #ifdef D_LOG d_log("d_rdport", "port read eof"); #endif D_LOG d_errno = D_PORTEOF; return(D_NONFATAL); } } c = toascii (c); /* make sure high bit is off */ switch (c) { case NULL: /* nulls are simply skipped */ case '\r': /* carriage returns are skipped */ case '\177': /* DELs are simply skipped */ continue; case '\n': /* end of line => end of packet */ d_tscribe (packet, count); d_tscribe ("\n", 1); *op = '\0'; break; default: /* add to packet buffer */ if (op == packet && !isxdigit (c)) { #ifdef D_DBGLOG d_dbglog ("d_rdport", "noise '%c'", c); #endif D_DBGLOG } else { /* packets begin with hex digits */ *op++ = c; count++; } continue; } if (count <= MAXPACKET + 1) break; /* so far, so good, now check form */ #ifdef D_LOG d_log ("d_rdport", "rcv too long (%dc) '%s'", count, packet); #endif D_LOG d_tscribe (packet, count); } /* make sure that the packet is at least as long as the shortest one. */ /* this is done to make sure that when we use fixed offsets for the */ /* fields that we're not reading junk. check the checksum. */ if (count < MINPACKET) { #ifdef D_LOG if (count > 0) /* ignore blank lines */ d_log ("d_rdport", "rcv len err (%d)", count); #endif D_LOG continue; } if (d_cscheck (packet, count) == D_NO) { #ifdef D_LOG d_log ("d_rdport", "rcv checksum err '%s'", packet); #endif D_LOG continue; } /* make sure that we didn't send it. the other guy may not have echo off */ flags = d_fromhex (packet[FLAGOFF]); if ((((flags & MASTERBIT) && d_master) || ((flags & MASTERBIT) == 0) && (d_master == 0))) { #ifdef D_LOG d_log ("d_rdport", "rcv from self"); #endif D_LOG continue; } /* acknowledge the packet */ d_alrmtim = s_alarm (0); /* ackpack uses the alarm clock */ seqnum = flags & 03; #ifdef D_DBGLOG d_dbglog ("d_rdport", "Received packet '%s'", packet); #endif D_DBGLOG switch (d_ackpack (packet[TYPEOFF], seqnum)) { case D_FATAL: return (D_FATAL); case D_NONFATAL: s_alarm (d_alrmtim); continue; default: return (count); } } } /* * * D_WAITPROMPT * * this routine is called to wait for a prompt character from the * port. If prompt is not received within a receive timeout period * then fall through. Do not return an error. * */ d_waitprompt () { extern FILE * d_prtfp; register int c; int promptalrm = 10; /* timeout delay in seconds */ char ch; if (d_prompt == 0) return(0); for (alarm (promptalrm); ; ) { if ((c = d_getc (d_prtfp)) == EOF) { /* truly an end of file? */ if (ferror (d_prtfp)) { d_alrmtim = alarm (0); if (errno == EINTR) { if (d_prompt == toascii(c)) return(0); #ifdef D_LOG d_log("d_waitprompt","prompt alarm for %d",d_prompt); #endif D_LOG d_errno = D_TIMEOUT; return(D_INTRPT); } else { #ifdef D_LOG d_log("d_waitprompt","port read errno %d", errno); #endif D_LOG d_errno = D_PORTRD; return(D_FATAL); } } if (feof (d_prtfp)) { d_alrmtim = alarm (0); #ifdef D_LOG d_log("d_waitprompt", "port read eof"); #endif D_LOG d_errno = D_PORTEOF; return(D_NONFATAL); } } ch = toascii(c); c = toascii(c); d_tscribe((char *) &ch, 1); /* put chars into transcript log */ if (d_prompt == c) { /* hoo-rah, break out the champaign */ promptalrm = alarm(0); /* cancel alarm */ return(D_OK); } } } /**/ /* * D_WRTPORT * * this routine writes a packet on the port. * * text -- pointer to packet to be sent. must have line terminator * already attached. * * length -- number of bytes in text * * timeout -- number of seconds to wait for write to complete * * * return values: * * OK -- no errors * * D_FATAL -- fatal error writing on port * * D_NONFATAL -- incomplete packet written on port. usually means disconnect. * * INTERRUPT -- write timer went off */ d_wrtport (text, length) register char *text; register int length; { extern FILE * d_prtfp; register int result; d_tscribe (text, length); /* * Added for prompt wait. Always wait before sending packet * 9/28/83 laubach@udel-relay */ if (d_prompt != 0) d_waitprompt(); /* Set up an alarm so we won't hang * indefintiely if something goes wrong */ if (setjmp (timerest)) { d_errno = D_TIMEOUT; #ifdef D_LOG d_log ("d_wrtport", "write alarm"); #endif D_LOG return (D_INTRPT); } s_alarm (XMITWAIT); result = write (fileno (d_prtfp), text, length); s_alarm (0); if (result == length) return (D_OK); if (result >= 0) { d_errno = D_PORTEOF; return (D_NONFATAL); } if (errno == EINTR) { d_errno = D_TIMEOUT; #ifdef D_LOG d_log ("d_wrtport", "write alarm"); #endif D_LOG return (D_INTRPT); } d_errno = D_PORTWRT; return (D_FATAL); } /* * D_BRKPORT * * This routine does its best to cause a BREAK on the line. * */ d_brkport() { #ifdef V4_2BSD #include <sys/ioctl.h> ioctl (fileno (d_prtfp), TIOCSBRK, 0); sleep ((unsigned) 1); ioctl (fileno (d_prtfp), TIOCCBRK, 0); #else #ifdef SYS5 #include <termio.h> ioctl (fileno(d_prtfp), TCSBRK, 0); #else #include <sgtty.h> struct sgttyb ttybuf; int spdsave; ioctl (fileno (d_prtfp), TIOCGETP, &ttybuf); spdsave = ttybuf.sg_ospeed; ttybuf.sg_ospeed = B150; ioctl (fileno (d_prtfp), TIOCSETP, &ttybuf); d_wrtport("\0\0\0\0\0", 5); ttybuf.sg_ospeed = spdsave; ioctl (fileno (d_prtfp), TIOCSETP, &ttybuf); #endif SYS5 #endif V4_2BSD } ackpack uses the alarm clock */ seqnum = flags & 03; #ifdef D_DBGLOG d_dbglog ("d_rdport", "Received packet '%s'", packet); #endif D_DBGLOG switch (d_ackpack (packet[TYPEOFF], seqnum)) { case D_FATAL: return (D_FATAL); case D_NONFATAL: s_alarm (d_alrmtim); continue; default: mmdf/lib/dial/d_tai.c 444 0 12 12507 3664425143 7710 #include "util.h" #include "ll_log.h" #include "d_proto.h" #include "d_structs.h" #include "cmd.h" /* tailor the dial package */ /* * Feb 82 Dan Long Added type field in dports tailor * Mar 82 Dan Long Fixed access setting for d_lines and * added NULL'ing of next item in structs * */ extern char *def_trn; extern struct ll_struct ph_log; #ifdef lint char *tai_eptr; /* keep lint happy -- defined in ../mmdf/mm_tai.c */ #else extern char *tai_eptr; #endif extern int errno; extern struct dialports *d_prts; extern int d_numprts; extern int d_maxprts; extern struct directlines *d_lines; extern int d_numlines; extern int d_maxlines; extern char *d_calllog; extern unsigned short d_lrill[]; extern unsigned short d_lxill[]; extern struct speedtab d_spdtab[]; #define CMDDPORT 1 #define CMDDLINE 2 #define CMDDACCT 3 #define CMDDBADIN 4 #define CMDDBADOUT 5 #define CMDDBAD 6 #define CMDPHLOG 7 #define DEFTRAN 8 LOCVAR Cmd cmddial[] = { "d_port", CMDDPORT, 4, "dport", CMDDPORT, 4, "dprt", CMDDPORT, 4, "port", CMDDPORT, 4, "d_line", CMDDLINE, 4, "dline", CMDDLINE, 4, "dlin", CMDDLINE, 4, "line", CMDDLINE, 4, "daccount", CMDDACCT, 1, "dacct", CMDDACCT, 1, "acct", CMDDACCT, 1, "dbadin", CMDDBADIN, 8, "dbadout", CMDDBADOUT, 8, "dbad", CMDDBAD, 8, "phlog", CMDPHLOG, 1, "deftran", DEFTRAN, 1, 0, 0, 0 }; #define CMDPACCESS 1 #define CMDPPREF 2 #define CMDPPOST 3 LOCVAR Cmd cmddparm[] = { "pref", CMDPPREF, 1, "suff", CMDPPOST, 1, "access", CMDPACCESS, 1, #ifdef NVRCOMPIL "post", CMDPPOST, 1, #endif 0, 0, 0 }; d_tai (argc, argv) /* tailor some dial parameter */ int argc; /* number of values */ char *argv[]; /* list of values */ { register int ind; int tspeed; switch (cmdsrch (argv[0], argc - 1, cmddial)) { case CMDPHLOG: return (tai_log (argc, &argv[1], &ph_log)); case DEFTRAN: def_trn = argv[1]; return(YES); case CMDDPORT: d_prts[d_numprts].p_port = argv[1]; d_prts[d_numprts].p_lock = argv[2]; d_prts[d_numprts].p_acu = argv[3]; sscanf (argv[4], "%o", &(d_prts[d_numprts].p_speed)); d_prts[d_numprts].p_ltype = argv[5]; for (ind = 6; ind < argc; ind++) if (!lexequ (argv[ind], "=")) { errno = EINVAL; tai_eptr = argv[ind]; argv[ind + 1]; return (NOTOK); } else { ind += 2; switch (cmdsrch (argv[ind - 1], argc - ind, cmddparm)) { case CMDPPREF: d_prts[d_numprts].p_acupref = argv[ind]; break; case CMDPPOST: d_prts[d_numprts].p_acupost = argv[ind]; break; case CMDPACCESS: d_prts[d_numprts].p_access = argv[ind]; break; default: errno = EINVAL; tai_eptr = argv[ind - 1]; argv[ind + 1]; return (NOTOK); } } if (++d_numprts > d_maxprts) { d_prts[--d_numprts].p_port = NULL; errno = ENFILE; tai_eptr = argv[0]; return (NOTOK); } d_prts[d_numprts].p_port = NULL; return (YES); case CMDDLINE: d_lines[d_numlines].l_name = argv[1]; d_lines[d_numlines].l_tty = argv[2]; d_lines[d_numlines].l_lock = argv[3]; sscanf (argv[4], "%d", &tspeed); for (ind=0; d_spdtab[ind].s_speed != 0; ind++) { if (tspeed == d_spdtab[ind].s_index || strcmp (argv[4], d_spdtab[ind].s_speed) == 0) break; } if (d_spdtab[ind].s_speed == 0) { errno = EINVAL; tai_eptr = argv[4]; return(NOTOK); } d_lines[d_numlines].l_speed = d_spdtab[ind].s_index; for (ind = 5; ind < argc; ind++) { if (lexequ (argv[ind], "=")) { if ((ind += 2) >= argc) { errno = EINVAL; tai_eptr = argv[ind - 1]; argv[ind + 1]; return (NOTOK); } if (lexequ (argv[ind - 1], "access")) d_lines[d_numlines].l_access = argv[ind]; else { errno = EINVAL; tai_eptr = argv[ind - 1]; argv[ind + 1]; return (NOTOK); } } else { errno = EINVAL; tai_eptr = argv[ind]; argv[ind + 1]; return (NOTOK); } } if (++d_numlines > d_maxlines) { d_lines[--d_numlines].l_tty = NULL; errno = ENFILE; tai_eptr = argv[0]; return (NOTOK); } d_lines[d_numlines].l_tty = NULL; return (YES); case CMDDACCT: d_calllog = argv[1]; return (YES); case CMDDBADOUT: /* can't send bare */ return (d_vec (argc, &argv[1], d_lxill)); case CMDDBADIN: /* can't receive bare */ return (d_vec (argc, &argv[1], d_lrill)); case CMDDBAD: /* send/receive list the same */ if (d_vec (argc, &argv[1], d_lxill) == YES && d_vec (argc, &argv[1], d_lrill) == YES) return (YES); } return (NO); /* not a dial parameter */ } /**/ d_vec (argc, argv, vec) int argc; char *argv[]; unsigned short vec[]; { register int ind; int tmp; if (argc > 8) argc = 8; /* only 128 bits @ 16 bits/word */ for (ind = 0; ind < argc; ind++) { sscanf (argv[ind], "%o", &tmp); /* convert to short */ vec[ind] = (unsigned) tmp; } return (YES); } timeout -- number of seconds to wait for write to complete * * * return values: * * OK -- no errors * * D_FATAL -- fatal error writing on port * * D_NONFATAL --mmdf/lib/dial/d_utils.c 444 0 12 11266 3671073164 10274 # include "util.h" # include "d_returns.h" # include <pwd.h> # include <stdio.h> # include "d_proto.h" # include "d_structs.h" /* 4 Sept 81 D. Krafft (Cornell) & D. Crocker * getuser log calls didn't name routine * also converted to using getpwuid() */ /* * routine which reads a line from the password file and pulls out the * user's name */ d_getuser(uid, name, path) int uid; char *name, *path; { extern struct passwd *getpwuid (); register struct passwd *pwdptr; /* if we did this once, just used the stuff we saved */ if ((pwdptr = getpwuid (uid)) == 0) { #ifdef D_LOG d_log("d_getuser", "couldn't find user in password file.\n"); #endif D_LOG return(D_FATAL); } if (name) (void) strcpy(name, pwdptr -> pw_name); if (path) { if (strlen(pwdptr -> pw_dir) > 64) { #ifdef D_LOG d_log("d_getuser", "user's default path too long '%s'\n", pwdptr -> pw_dir); #endif D_LOG return(D_FATAL); } (void) strcpy(path, pwdptr -> pw_dir); } return(D_OK); } /* * D_CANON * * routine which translates a C style string specification into * internal format. it understands 'escape' characters equivalents * for certain control characters: newline ('\n'), carriage return ('\r'), * tab ('\t'), form feed ('\f'), and delete ('\x'). in addition, any * character can be specified with a backslash followed by up to 3 octal * digits. * * in -- pointer to canonical format string * * out -- pointer to buffer for internal format equivalent. * * the number of characters in the (null-terminated) output string is * returned. a return value of -1 indicates an error. */ d_canon(in, out) register char *in, *out; { int value, count, place; char c; count = 0; /* check for obvious bad format */ if (*in++ != '"') return(-1); /* loop through all the characters. lots of special cases */ while (1) { c = *in++; switch (c) { default: *out++ = c; break; case '\0': return(-1); case '"': *out = '\0'; return(count); case '\\': /* handle the escapes */ c = *in++; switch (c) { case '"': *out++ = '"'; break; case '\0': return(-1); case '\\': *out++ = '\\'; break; case 'r': *out++ = '\15'; break; case 'n': *out++ = '\12'; break; case 't': *out++ = '\11'; break; case 'x': *out++ = '\377'; break; case 'b': *out++ = '\10'; break; case '#': *out++ = '\376'; break; /* allow the specification of characters by up to 3 octal digits after a */ /* backslash */ default: if (!d_isodigit(c)) { *out++ = c; break; } value = c & 07; for (place = 0; place <= 1; place++) { c = *in++; if (d_isodigit(c)) value = (value << 3) | (c & 07); else { in--; break; } } *out++ = value; } } count++; } } /* * D_ISODIGIT * * this routine returns a non-zero value if the argument is an octal * digit, ie. between 0 and 7. * * c -- the character to be tested */ d_isodigit(c) char c; { if ((c >= '0') && (c <= '7')) return(1); else return(0); } /* * D_TRYFORK * * the purpose of this routine is to fork, retrying 10 times if necessary * -1 is returned if failure after all retries */ d_tryfork() { register int try, pid; for (try = 0; try <= 9; try++) { pid = fork(); if (pid != -1) return(pid); } return(-1); } /* * D_MINIMUM * * this routine returns the minimum of its two arguments * * a, b -- the minimum of these values is returned */ d_minimum(a, b) register int a, b; { if (a < b) return(a); else return(b); } d_log("d_getuser", "couldn't find user in password file.\n"); #endif D_LOG return(D_FATAL); } if (name) (void) strcpy(name, pwdptr -> pw_name); if (path) { if (strlen(pwdptr -> pw_dir) > 64) { #ifdef D_LOG d_log("d_getuser", "user's default path too long '%s'\n", pwdptr -> pw_dir); mmdf/lib/dial/d_tscript.c 444 0 12 4030 3671073165 10574 # include <stdio.h> # include "d_returns.h" /* maintain a transcript of data i/o over the port. * the transcript is * not buffered, so that it will reflect the * latest activity, for * debugging. dhc. */ static int d_tsfd; /* file descriptor for transcript file */ /* * D_TSOPEN * * this routine creates the transcript file. * * filename -- name of the transcript file to be created. if this arg * is zero, then the standard output is used. */ d_tsopen(filename) register char *filename; { extern int d_errno, errno; if (filename == 0) { #ifdef D_LOG d_log("d_tsopen", "transcript started on standard output"); #endif D_LOG d_tsfd = 1; return(D_OK); } if ((d_tsfd = creat(filename, 0662)) < 0) { #ifdef D_LOG d_log("d_tsopen", "can't create transcript file '%s'", filename); #endif D_LOG d_errno = D_TSOPEN; return(D_FATAL); } chmod (filename, 0662); #ifdef D_LOG d_log("d_tsopen", "transcript file '%s' created", filename); #endif D_LOG return(D_OK); } /**/ /* * D_TSCLOSE * * this routine is called to stop transcribing */ d_tsclose() { /* do nothing if transcribing is not active */ if (d_tsfd <= 0) return(D_OK); if (d_tsfd != 1) (void) close(d_tsfd); #ifdef D_LOG d_log("d_tsclose", "transcript stopped"); #endif D_LOG return(D_OK); } /* * D_TSCRIBE * * routine which writes on the transcript file * * text -- pointer to byets to be written * * length -- number of bytes to be written */ d_tscribe(text, length) register char *text; register int length; { extern int d_errno; /* if there's a transcript file and something to write, do it */ if ((d_tsfd > 0) && (length > 0)) { if (write(d_tsfd, text, length) != length) { #ifdef D_LOG d_log("d_tscribe", "error writing on transcript file"); #endif D_LOG d_errno = D_TSWRITE; return(D_NONFATAL); } } return(D_OK); } *out++ = '\\'; break; case 'r': *out++ = '\15'; break; case 'n': *out++ = '\12'; break; case 't': *out++ = '\11'; break; case 'x': *out++ = '\377'; break; case 'b': *out++ = '\10'; break; case '#': *out++ = '\mmdf/lib/dial/d_sndstream.c 444 0 12 3323 3620510400 11066 # include "d_returns.h" extern char d_xqueue[], *d_xqpt; extern int d_xqcnt, d_maxtext; /* * D_SNDSTREAM * * this routine is called to send data * to the other side without necessarily * implying any packet boundaries. * * buffer -- pointer to the data to be sent * * nbytes -- number of bytes to be sent */ d_snstream(buffer, nbytes) char *buffer; int nbytes; { register int length, result; register char *bp; #ifdef D_DBGLOG d_dbglog("d_snstream", "sending '%.*s'", nbytes, buffer); #endif D_DBGLOG bp = buffer; while (nbytes--) { length = d_ccode(*bp & 0177, d_xqpt); /* if the encoding of this character will overflow the text, send off what */ /* we've already got. */ if ((length + d_xqcnt) > d_maxtext) { /* Note that this does not actually send the character that * would overflow the queue. It never increments the pointer. * It overwrites the queued char(s) with a null. It resets * (adds 1 to) the nbyte counter. The continue causes it * to requeue the same char. */ *d_xqpt = '\0'; if ((result = d_sndata(0)) < 0) return(result); nbytes++; continue; } /* otherwise adjust the count and pointers */ d_xqcnt += length; d_xqpt += length; bp++; } return(D_OK); } /* * D_SNDEOT * * this routine is called to flush the transmit buffer and mark the * packet with an EOT */ d_sneot() { register int result; *d_xqpt = '\0'; #ifdef D_DBGLOG d_dbglog("d_sneot", "packet '%s'", d_xqueue); #endif D_DBGLOG result = d_sndata(1); return(result); } if there's a transcript file and something to write, do it */ if ((d_tsfd > 0) && (length > 0)) { if (write(d_tsfd, text, length) != length) { #ifdef D_LOG d_log("d_tscribe", "error writing on transcript file"); #endif D_LOG d_errno = D_TSWRITE; return(D_NONFATAL); } } mmdf/lib/dial/d_setbuff.c 444 0 12 2023 3620510400 10520 # # include "d_proto.h" # include "d_returns.h" # include <stdio.h> # include "d_structs.h" /* * D_SETBUFF * * Sends the control packet indicating how deeply packets * can be buffered. Actually, at the moment the only * relevant information is whether buffering is allowed or * not. The depth of buffering is irrelavent. However, this * information can be included cheaply, and it may make later * modifications easier, so it is given anyway. * * nbuff - the depth of the buffering. * This number is currently 0 to indicate no buffering * (i.e., all packets must be ACKed immediately) or 1 * to indicate that one outstanding packet is allowed. * */ d_setbuff (nbuff) int nbuff; { char tbuff[3], packet[MAXPACKET + 2]; int length; extern int d_snseq; tbuff[0] = d_tohex ((nbuff >> 4) & 017); tbuff[1] = d_tohex ((nbuff & 017)); tbuff[2] = '\0'; d_snseq = d_incseq (d_snseq); length = d_bldpack (NBUFF, d_snseq, 1, tbuff, packet); return (d_snpkt(NBUFF, packet, length)); } t resets * (adds 1 to) the nbyte counter. The continue causes it * to requeue the same char. */ *d_xqpt = '\0'; if ((result = d_sndata(0)) < 0) return(result); nbytes++; continue; } /* otherwise adjust the count and pointers */ d_xqcnt += length; d_xqpt += length; bp++; } return(D_OK); } /* * D_SNDEOT * * this routine is called to flush the transmit buffer and mark the * packet withmmdf/lib/dial/d_script.c 444 0 12 64120 3664425147 10441 # include "util.h" # include <signal.h> # include "d_syscodes.h" # include "d_proto.h" # include "d_returns.h" # include <stdio.h> # include <strings.h> # include "d_structs.h" # include "d_script.h" /* Jun 81 D. Crocker rgetc() tossed out almost all the control * characters. changed to toss only null & DEL * Sep 83 G.B. Reilly added support for script command for prompt * M. Laubach recognition. */ extern char *dupfpath(); extern char *tbldfldir; extern int errno; extern FILE *d_scfp; extern unsigned short d_lxill[], d_lrill[]; extern int d_baudrate; extern int d_errno; extern int d_lxmax; extern int d_lrmax; extern int d_debug; extern int d_xretry; extern int d_toack; extern int d_todata; extern int d_wpack; #ifdef SYS5 extern unsigned short d_prbitc, d_prbiti, d_prbito, d_prbitl; extern unsigned short d_scbitc, d_scbiti, d_scbito, d_scbitl; #else extern int d_pron, d_proff, d_scon, d_scoff; #endif SYS5 extern int d_nbuff; extern int d_didial; extern FILE * d_prtfp; /*** prompt varibable ******************** reilly@udel-relay ***/ int d_prompt = 0; /* global prompt var. =0 means disabled */ /* <>0 is ascii value of prompt character*/ #define MAXNUMS 5 #define NOPARSE -1 #define IGNORE -2 #define ANYARGS -3 /* structure defining the legal script file commands, their type, * and legal number of fields. */ struct scrcmds { char *s_cmdname; /* command string */ int s_cmdtype; /* command type number */ int s_cmdminfields; /* min number of fields for command */ int s_cmdmaxfields; /* max number of fields allowed */ } d_sccmds[] = { "dial", S_DIAL, 2, 2, "conn", S_DIAL, 2, 2, "phone", S_DIAL, 2, 2, "xillegal", S_XILL, 2, 2, "rillegal", S_RILL, 2, 2, "xmitill", S_XILL, 2, 2, "recvill", S_RILL, 2, 2, "xmitpack", S_XPCK, 2, 2, "recvpack", S_RPCK, 2, 2, "xmitsize", S_XPCK, 2, 2, "recvsize", S_RPCK, 2, 2, "transmit", S_XMIT, 2, 2, "send", S_XMIT, 2, 2, "xmit", S_XMIT, 2, 2, "receive", S_RECV, 3, 3, "recv", S_RECV, 3, 3, "start", S_GO, 1, 1, "go", S_GO, 1, 1, "end", S_END, 1, 1, "stop", S_END, 1, 1, "bye", S_END, 1, 1, "nrexmit", S_RETR, 2, 2, "waitack", S_TOAK, 2, 2, "waitdata", S_TODA, 2, 2, "{", S_SELST, 1, 1, "}", S_SELEND, 1, 1, "alternate", S_ALT, 1, 1, "case", S_ALT, 1, 1, "cmnt", S_REM, IGNORE, IGNORE, "rem", S_REM, IGNORE, IGNORE, "rem:", S_REM, IGNORE, IGNORE, "com", S_REM, IGNORE, IGNORE, "com:", S_REM, IGNORE, IGNORE, "#", S_REM, IGNORE, IGNORE, "log", S_LOG, 2, 2, "abort", S_ABORT, 1, 1, "bill", S_BILL, 3, 3, "replay", S_REPLAY, 1, 1, "mark", S_MARK, 1, 1, #ifdef SYS5 "stty-pr", S_PRTTY, 3, 5, "stty-sc", S_SCTTY, 3, 5, #else "stty-pr", S_PRTTY, 3, 3, "stty-sc", S_SCTTY, 3, 3, #endif SYS5 "window", S_DBLBUF, 3, 3, "use", S_USEFILE, 2, ANYARGS, /*** prompt command has one format "prompt <val>" reilly@udel-relay ***/ "prompt", S_PROMPT, 2, 2, 0, 0, 0, 0, }; char d_scfile[82], /* script file path name */ d_rawq[MATCHLEN], /* pushback queue for string matcher */ *d_prawq; /* pointer to next available character */ /* in 'd_rawq'. */ unsigned d_scline; /* current line number in script file */ int d_nfields = 0; /* current number of "use" fields */ char *d_fields[MAXFIELDS]; /* current "use" fields */ int d_ntries = 2, /* number of times to try to dial numbers */ d_wait = 15, /* number of seconds to wait between dial */ /* attempts. */ d_nrawq; /* number of available characters in 'd_rawq' */ int d_dodrop; /* non zero if a dial has been made */ LOCVAR int d_gotxill = 0; /* non zero if xill command received */ LOCVAR int d_gotrill = 0; /* non zero if rill command received */ /* * D_SCRIPT * * this routine reads lines from the script file, parses them up, and * then initiates the requested actions. */ d_script () { register int nselect, result; char linebuf[MAXSCRLINE + 2], *fields[MAXFIELDS]; int nfields; nselect = 0; d_dodrop = 0; for (;;) { result = d_scrblk (); switch (result) { case D_OK: return(D_OK); case D_FATAL: case D_EOF: case D_NFIELDS: case D_QUOTE: case D_UNKNOWN: return (D_FATAL); case D_NONFATAL: /* An error that may be recoverable. If there is * an alternate, use it. */ if (nselect <= 0) /* Not within a select block; therefore, not alt */ return (D_FATAL); /* We are within a select block; go to the alternate */ if ((result = d_nxtalt ()) < 0) return (result); switch (result) { case S_ALT: continue; case S_SELEND: /* no more alternates; return error */ return (D_FATAL); default: d_scerr ("Bad syntax scriptfile"); return (D_FATAL); } case S_SELST: /* The next line had better be an alternate */ if (d_cmdget (linebuf, &nfields, fields, d_scfp) != S_ALT) { d_scerr ("Missing 'alternate' after 'begin'"); return (D_FATAL); } nselect++; continue; case S_SELEND: /* verify that there was a select block being * looked at. If so, then the last alternate * was the successful one. Continue normally. */ if (nselect-- <= 0) { d_scerr ("Inappropriate select end"); return (D_FATAL); } continue; case S_ALT: /* First, make sure the context was correct */ if (nselect <= 0) { d_scerr ("Inappropriate alt\n"); return (D_FATAL); } /* If we get here, then an alternate within a select * was completed successfully. Move on. */ if ((result = d_selend ()) < 0) return (result); nselect--; switch (result) { case S_SELEND: continue; default: d_scerr ("error in format of script file"); return (D_FATAL); } default: if (result < 0) return (result); d_scerr ("d_script", "Internal error: result = %d", result); return (D_FATAL); } } } d_scrblk () { char linebuf[MAXSCRLINE + 2], *fields[MAXFIELDS]; int command, result, nfields; for (;;) { /* read the command */ command = d_cmdget (linebuf, &nfields, fields, d_scfp); if (command < 0) return (command); result = d_cmdproc(command, nfields, fields); if (result == D_CONTIN) continue; return (result); } } d_cmdproc (command, nfields, fields) int command, nfields; char *fields[]; { register int result, word, i; register char *ptr; /* switch out on the command type. for most of the commands, * there is a routine to handle them. */ switch (command) { case S_EOF: /* Not really a script command. This is returned to * indicate an unexpected EOF on the script input file. * Try to revert to a previous script file. If there is * none, then this really is an unexpected EOF. Return * an error to indicate so. */ if (d_scclose() > 0) return (D_CONTIN); return (D_EOF); case S_DBLBUF: /* Can use 'd_nbuff' as a counter of the number of * outstanding packets to allow if we later mod the * code to allow more than double buffering. * See the file 'd_packet.c' */ result = atoi (fields[1]); #ifdef D_LOG if ((result != 1) && (result != 2) ) d_log("d_cmdproc", "Illegal buffer number, %d", result); else #endif D_LOG d_nbuff = result; /* This variable will be non-zero if the master startup * routine should transmit a packet telling the other * side about the buffering window. This packet can not * be transmitted to sites running the old code. */ d_wpack = atoi (fields[2]); return (D_CONTIN); case S_USEFILE: /* takes additional input from another file until * that file is exhausted. */ fields[1] = dupfpath(fields[1], tbldfldir); for (i = 2; i <= nfields; i ++) { if (*fields[i] == '"') fields[i]++; if (ptr = rindex(fields[i], '"')) *ptr = '\0'; } if (d_scopen (fields[1], nfields, &fields[0]) < 0) return (D_FATAL); return (D_CONTIN); case S_PRTTY: #ifdef SYS5 sscanf(fields[1], "%o", &d_prbitc); sscanf(fields[2], "%o", &d_prbiti); sscanf(fields[3], "%o", &d_prbito); sscanf(fields[4], "%o", &d_prbitl); #ifdef D_LOG d_log("d_cmdproc", "%s %o %o %o %o", fields[0], d_prbitc, d_prbiti, d_prbito, d_prbitl); #endif D_LOG #else /* SYS5 */ /* Could use fields[1&2], but let's check consistancy */ sscanf(fields[1], "%o", &d_pron); sscanf(fields[2], "%o", &d_proff); #ifdef D_LOG d_log("d_cmdproc", "%s %o %o", fields[0], d_pron, d_proff); #endif D_LOG #endif SYS5 return (D_CONTIN); case S_SCTTY: #ifdef SYS5 sscanf(fields[1], "%o", &d_scbitc); sscanf(fields[2], "%o", &d_scbiti); sscanf(fields[3], "%o", &d_scbito); sscanf(fields[4], "%o", &d_scbitl); #ifdef D_LOG d_log("d_cmdproc", "%s %o %o %o %o", fields[0], d_scbitc, d_scbiti, d_scbito, d_scbitl); #endif D_LOG #else sscanf(fields[1], "%o", &d_scon); sscanf(fields[2], "%o", &d_scoff); #ifdef D_LOG d_log("d_cmdproc", "%s %o %o", fields[0], d_scon, d_scoff); #endif D_LOG #endif SYS5 return (D_CONTIN); case S_MARK: d_mark (); return (D_CONTIN); case S_REPLAY: d_replay (); return (D_CONTIN); case S_LOG: /* Force this to always log */ d_plog (-1, "%s", fields[1]); return (D_CONTIN); case S_BILL: d_didial = 1; /* pretend we're dialport for dial_log purposes */ d_cstart(fields[1], fields[2]); return (D_CONTIN); case S_REM: return (D_CONTIN); case S_ABORT: return (D_FATAL); case S_ALT: case S_SELST: case S_SELEND: return (command); case S_DIAL: /* get line & make raw mode for matching */ /* Make sure any previous connections are cleaned up */ if (d_dodrop != 0) { d_dodrop = 0; d_drop (); } /* Don't let input from last dial get in this one's queue */ d_mark (); if (d_scdial (fields[1]) < 0) return (D_NONFATAL); d_dodrop = 1; return (D_CONTIN); case S_XMIT: if ((result = d_scxmit (fields[1])) < 0) return (result); return (D_CONTIN); case S_RECV: if ((result = d_screcv (fields[1], fields[2])) < 0) return (result); return (D_CONTIN); case S_GO: /* regular tty mode during session */ /*** expanded to call and log ttscript when prompt active ***/ /*** necessary to get raw mode for control chars ***/ /*** 9/27/83 laubach@udel-relay ***/ if (d_prompt == 0) { if (d_ttproto (d_baudrate) < 0) return (D_NONFATAL); } else { if (d_ttscript ( d_baudrate) < 0) return (D_NONFATAL); #ifdef D_DBGLOG d_dbglog("d_cmdproc","ttscript selected for raw prompt mode"); #endif D_DBGLOG } return (D_OK); case S_END: return (D_OK); case S_XPCK: result = atoi (fields[1]); if ((result < MINPATHSIZ) || (result > MAXPACKET)) { d_scerr ("illegal max transmit packet length: %d", result); d_errno = D_SCRERR; return (D_NONFATAL); } #ifdef D_DBGLOG d_dbglog ("d_script", "setting 'd_lxmax' to %d", result); #endif D_DBGLOG d_lxmax = result; return (D_CONTIN); case S_RPCK: result = atoi (fields[1]); if ((result < MINPATHSIZ) || (result > MAXPACKET)) { d_scerr ("illegal max receive packet length"); d_errno = D_SCRERR; return (D_NONFATAL); } #ifdef D_DBGLOG d_dbglog ("d_script", "setting 'd_lrmax' to %d", result); #endif D_DBGLOG d_lrmax = result; return (D_CONTIN); case S_XILL: if (!d_gotxill) { /* zero the vector on first S_XILL command */ d_gotxill = 1; #ifdef D_DBGLOG d_dbglog("d_script", "zeroing xill vector"); #endif D_DBGLOG for (word = 0; word < 8; word++) d_lxill[word] = 0; } if ((result = d_scill (fields[1], d_lxill)) < 0) return (result); #ifdef D_DBGLOG d_dbglog ("d_script", "d_lxill = %o %o %o %o %o %o %o %o", d_lxill[0], d_lxill[1], d_lxill[2], d_lxill[3], d_lxill[4], d_lxill[5], d_lxill[6], d_lxill[7]); #endif D_DBGLOG return (D_CONTIN); case S_RILL: if (!d_gotrill) { /* zero the vector on first S_RILL command */ d_gotrill = 1; #ifdef D_DBGLOG d_dbglog("d_script", "zeroing rill vector"); #endif D_DBGLOG for (word = 0; word < 8; word++) d_lrill[word] = 0; } if ((result = d_scill (fields[1], d_lrill)) < 0) return (result); #ifdef D_DBGLOG d_dbglog ("d_script", "d_lrill = %o %o %o %o %o %o %o %o", d_lrill[0], d_lrill[1], d_lrill[2], d_lrill[3], d_lrill[4], d_lrill[5], d_lrill[6], d_lrill[7]); #endif D_DBGLOG return (D_CONTIN); case S_RETR: result = atoi (fields[1]); if (result <= 0) d_scerr("d_script", "Bad retry value: %d", result); else d_xretry = result; return (D_CONTIN); case S_TOAK: result = atoi (fields[1]); if (result <= 0) d_scerr ("d_script", "Bad ack time out value: %d", result); else d_toack = result; return (D_CONTIN); case S_TODA: result = atoi (fields[1]); if (result <= 0) d_scerr ("d_script", "Bad data time out value: %d", result); else d_todata = result; return (D_CONTIN); /*** prompt has one field which is the decimal ascii value of the prompt ***/ /*** character reilly@udel-relay ***/ case S_PROMPT: d_prompt = atoi (fields[1]); #ifdef D_DBGLOG d_dbglog ("d_script", "Prompt ASCII %d (decimal)", d_prompt); #endif D_DBGLOG return (D_CONTIN); default: d_scerr ("internal error -- unknown command %d", command); return (D_FATAL); } } d_cmdget (linebuf, nfields, fields, channel) int *nfields; char *linebuf, *fields[]; FILE *channel; { register int result; char *nstart; int command, nwantmin, nwantmax; register struct scrcmds *cmdpt; /* read a line from the script file */ again: if ((result = d_scgetline (linebuf, channel)) < 0) return (result); if (result == 0) return (S_EOF); #ifdef D_DBGLOG d_dbglog ("d_scrproc", "line %d - '%s'", d_scline, linebuf); #endif D_DBGLOG /* First, seperate off the first field of the input line */ switch (d_getword(linebuf, &fields[0], &nstart)) { case -1: /* end of line. I.e, no line. Try again */ goto again; case -2: /* unterminated quote. */ d_scerr ("quoted string missing end quote"); return (D_QUOTE); case 0: /* Found a word OK */ break; default: d_scerr ("unknown return from d_getword"); return (D_FATAL); } /* search the command list for the string in the first field * of the line. */ cmdpt = d_sccmds; for(;;) { if (cmdpt -> s_cmdname == 0) { d_scerr ("command not known"); return (D_UNKNOWN); } if (strcmp (fields[0], cmdpt -> s_cmdname) != 0) { cmdpt++; continue; } command = cmdpt -> s_cmdtype; nwantmin = cmdpt -> s_cmdminfields; nwantmax = cmdpt -> s_cmdmaxfields; break; } /* if this is meant to be tossed, go get another line */ if (nwantmin == IGNORE) goto again; /* if this is not meant to be parsed further, don't */ if (nwantmin == NOPARSE) { fields[1] = nstart; return (command); } /* parse the rest of the line and make sure the right number * of fields are present. */ *nfields = d_linparse (&fields[1], nstart, MAXFIELDS-1); if (*nfields < 0) switch (*nfields) { case -1: d_scerr ("too many fields"); return (D_NFIELDS); case -2: d_scerr ("quoted string missing end quote"); return (D_QUOTE); case -3: d_scerr ("backquote appeared at end of line"); return (D_QUOTE); case -4: d_scerr ("invalid $ variable used"); return (D_FATAL); case -5: d_scerr ("line too long"); return (D_FATAL); default: d_scerr ("unknown error from line parser"); return (D_FATAL); } if ( (nwantmin == ANYARGS || *nfields >= (nwantmin - 1)) && (nwantmax == ANYARGS || *nfields <= (nwantmax - 1)) ) return (command); /* If it gets here, then this command had wrong # of fields */ d_scerr ("command has %d fields", (*nfields+1)); return (D_NFIELDS); } d_selend () { register int command; char linebuf[MAXSCRLINE + 2], *fields[MAXFIELDS]; int nfields; for (;;) { command = d_cmdget (linebuf, &nfields, fields, d_scfp); if (command < 0) return (command); switch (command) { default: continue; case S_SELEND: return (S_SELEND); case S_SELST: d_selend (); continue; } } } d_nxtalt () { register int command; char linebuf[MAXSCRLINE + 2], *fields[MAXFIELDS]; int nfields; for (;;) { command = d_cmdget (linebuf, &nfields, fields, d_scfp); if (command < 0) return (command); switch (command) { default: continue; case S_SELEND: case S_ALT: return (command); case S_SELST: d_selend (); continue; } } } /* * D_SCRDIAL * * this routine is called to do the dial command in the script file. * it just calls lower level routines to parse the number string and * do the actual dialing. * * number -- number specification string from the script file */ d_scdial (number) register char *number; { register int nnumbs, result; struct telspeed telnumbs[MAXNUMS]; #ifdef D_DBGLOG d_dbglog ("d_scdial", "attempting to call '%s'", number); #endif D_DBGLOG /* parse the number spec, do the dialing, and set the port to raw mode */ if ((nnumbs = d_numparse (number, telnumbs, MAXNUMS, d_scfile, d_scline)) < 0) return (nnumbs); if ((result = d_connect (telnumbs, nnumbs, d_ntries, d_wait)) < 0) return (result); return (D_OK); } /* * D_SCRXMIT * * this routine is called to transmit a string given in the script file. * the string is converted to standard internal format and then passed * to a lower transmission routine. * * string -- string to be transmitted in 'C' style */ d_scxmit (string) register char *string; { register int length; char canonstr[MAXSCRLINE]; if (*string == '"') if ((length = d_canon (string, canonstr)) < 0) { d_scerr ("transmit string format error"); return (D_FATAL); } if (d_xstring (canonstr, length) < 0) return (D_FATAL); return (D_OK); } /* * D_SCRRECV * * this routine is called to wait until a given string is received on * the port. the routine will return when the sting has been identified. * there is also a timer that is set to cause the routine to return with * an error if the string is not received within the specified interval. * * string -- the string to be watched for * * timestr -- a timeout value, in seconds, given as an ascii string */ d_screcv (string, timestr) char *string, *timestr; { register unsigned int timeout; register int length, result; char canonstr[MAXSCRLINE]; #ifdef D_DBGLOG d_dbglog ("d_screcv", "looking for '%s' in '%s' seconds", string, timestr); #endif D_DBGLOG /* convert the transmit string, check its length, and convert the timeout */ /* value. */ if ((length = d_canon (string, canonstr)) < 0) { d_scerr ("receive string format error"); return (D_FATAL); } if (length > MATCHLEN) { d_scerr ("receive string too long"); return (D_FATAL); } if ((result = atoi (timestr)) < 1) { d_scerr ("illegal timeout value"); return (D_FATAL); } timeout = result; /* convert to unsigned */ /* set up the timer */ if (setjmp (timerest)) { d_scerr ("no match for '%s' after %d seconds", string, timeout); d_errno = D_TIMEOUT; return (D_NONFATAL); } s_alarm ((unsigned) timeout); /* do the matching */ while ((result = d_scmatch (canonstr)) == D_NO) d_rgetc (); s_alarm (0); switch (result) { case D_OK: return (D_OK); case D_NONFATAL: d_scerr ("eof on port while trying match"); d_errno = D_PORTEOF; return (D_NONFATAL); case D_FATAL: d_scerr ("read error on port while trying match"); d_errno = D_PORTRD; return (D_FATAL); case D_INTRPT: d_scerr ("no match for '%s' after %d seconds", string, timeout); d_errno = D_TIMEOUT; return (D_NONFATAL); } d_errno = D_SYSERR; return (D_FATAL); } /* * D_SCILL * * this routine is called to translate a string specifying illegal * characters for either transmission or recption, and set the bits * corresponding to those characters in the given bit vector. * * string -- pointer to character specification string in canonical * format * * bitvector -- pointer to bit vector */ d_scill (string, bitvector) char *string; unsigned short bitvector[]; { register int bit, length; register char *cp; char canonstr[MAXSCRLINE]; /* translate the character string */ if ((length = d_canon (string, canonstr)) < 0) { d_scerr ("error in illegal character specification"); return (D_FATAL); } /* run through the bytes in the converted string and set the bit */ /* corresponding to each one */ cp = canonstr; for (bit = 0; bit < length; bit++) d_setbit (*cp++, bitvector); return (D_OK); } /* * D_SCMATCH * * this routine checks for a match between the string and the input * stream. * * string -- string to be matched in the input stream * * * return values: * * D_OK -- the string has been found * * D_NO -- the string cannot be matched with the current starting * character. delete the first character and try again. * * D_NONFATAL -- eof on port while reading characters * * D_FATAL -- read error on port * * D_INTRPT -- no match after the specified time */ d_scmatch (string) char *string; { register int c, result; if (*string == '\0') return (D_OK); if ((c = d_rgetc ()) < D_OK) return (c); if (c == *string) { result = d_scmatch (++string); if (result < D_OK) d_unrgetc (c); return (result); } else { d_unrgetc (c); return (D_NO); } } /* * D_RGETC * * this routine is used to read characters from the port in raw mode. * to allow matching of strings in the input stream, this function * implements a look behind facility. the port is read only if the * push back list is empty. */ d_rgetc () { register int result; int theval; char c; /* if the queue isn't empty, use what it has */ if (d_nrawq) { d_nrawq--; return (*d_prawq--); } /* have to read the port. watch for timeout interrupts */ while ((theval = d_getc (d_prtfp)) != EOF) { c = toascii (theval); /* filter out some of the junk characters */ if ((result = d_tscribe ((char *) &c, 1)) < 0) return (result); switch (c) { /* let controls go by, except */ case '\000': /* skip null & DEL, because they are */ case '\177': /* almost always just noise */ continue; } return (c); } if (feof (d_prtfp)) return (D_NONFATAL); if (errno == EINTR) return (D_INTRPT); return (D_FATAL); } /* * D_UNRGETC * * this routine pushes back a character onto the look behind queue * used by the 'd_rgetc' routine * * c -- the character to be pushed back */ d_unrgetc (c) char c; { /* set up the queue if it isn't ready */ if (d_prawq <= 0) { d_prawq = d_rawq; *d_prawq = c; d_nrawq = 1; return; } /* otherwise, stick it on the end */ *++d_prawq = c; d_nrawq++; } /* * D_XSTRING * * this routine is called to transmit strings on the port. If the * string contains octal 377, then the function pauses for 1 second * before continuing. * * string -- pointer to string * * length -- number of characters in string to be sent. */ d_xstring (string, length) register char *string; int length; { register int result; #ifdef D_DBGLOG d_dbglog ("d_xstring", "sending '%s', length %d", string, length); #endif D_DBGLOG sleep ((unsigned) 2); /* permit any needed line-settling */ for ( ; length--; string++) { /* NOSTRICT */ if ((int)*string == -1) sleep ((unsigned) 1); else if ((int)*string == -2) d_brkport(); else if ((result = d_wrtport (string, 1)) < 0) break; if (*string == '\r') sleep ((unsigned) 1); /* may nned line-settling */ } sleep ((unsigned) 2); /* permit any needed line-settling */ return (result); } /* * D_SCRERR * * routine which is called to log errors in the script file. the * purpose of this is so the name of the file and the line number * can be tacked on in one place. * * format -- 'd_log' type format string * * a, b, c, d, ... -- variables to be logged */ /* VARARGS1 */ d_scerr (format, a, b, c, d, e, f, g, h) char *format; unsigned a, b, c, d, e, f, g, h; { #ifdef D_LOG char tmpfmt[256]; sprintf (tmpfmt, "%s%s", "Error in %s, line %d: ", format); d_log ("d_scerr", tmpfmt, d_scfile, d_scline, a, b, c, d, e, f, g, h); #endif D_LOG d_errno = D_SCRERR; } tor); return (D_OK); } /* * D_SCMATCH * * this routine checks for a match between the string and the input * stream. * * string -- string to be matched in the input stream * * * return values: * * D_OK -- the string has been found * * D_NO -- the string cannot be matched with the current starting * character. delete the first character and try again. * mmdf/lib/dial/d_rcvdata.c 444 0 12 3121 3620510401 10507 # include "d_returns.h" /* * D_RCVDATA * * this routine is called to read data bytes from the other side. * if there are currently any unread characters in the internal * read queue, they are returned before another packet is read. * * buffer -- place to load the data * * nbytes -- the maximum number of bytes that should be returned * if an oet is found, then not all that the caller * requests may be returned. * * eot -- pointer to a variable that is set if the packet, or the * one queued, contained an EOT */ d_rcvdata(buffer, nbytes, eot) register char *buffer; register int nbytes; int *eot; { extern char *d_rdqpt; extern int d_rdqcnt, d_rdeot; register int count; int result; count = 0; while (1) { /* transfer data until the max is reached or this packet * is emptied. */ while (nbytes && d_rdqcnt) { *buffer++ = *d_rdqpt++; count++; nbytes--; d_rdqcnt--; } /* If there are no more packets available, return EOT */ if ((d_rdqcnt == 0) && d_rdeot) { *eot = 1; d_rdeot = 0; return(count); } /* If there is no more room to transfer to, just return */ if (nbytes == 0) { *eot = 0; return(count); } /* Else we come here. No more data in this packet, but there * are more packets to read. Do it. */ if ((result = d_getdata()) < 0) return(result); } } port. If the * string contains octal 377, then the function pauses for 1 second * before continuing. * * string -- pointer to string * * length -- number of characters in string to be sent. */ d_xstring (string, length) register char *string; int length; { register int result; #ifdef D_DBGLOG d_dbglog ("d_xstring", "sending '%s', length %d", string, length); #endif D_DBGLOG sleepmmdf/lib/dial/d_quit.c 444 0 12 1060 3620510401 10045 # include "d_proto.h" # include "d_returns.h" /* * D_SNDQUIT * * this routine is called to send a QUIT packet to the other end * and wait for the reply. */ d_snquit() { extern int d_snseq; register int length, result; char packet[MAXPACKET + 2]; #ifdef D_LOG d_log("d_snquit", "sending QUIT"); #endif D_LOG /* build and send the QUIT packet */ d_snseq = d_incseq(d_snseq); length = d_bldpack(QUIT, d_snseq, 1, (char *) 0, packet); result = d_snpkt(QUIT, packet, length); return(result); } } /* If there are no more packets available, return EOT */ if ((d_rdqcnt == 0) && d_rdeot) { *eot = 1; d_rdeot = 0; return(count); } /* If there is no more room to transfer to, just return */ if (nbytes == 0) { *eot = 0; return(count); } /* Else we come here. No more data in this packet, but there * are more packets to read. Do it. mmdf/lib/dial/d_pglobals.c 444 0 12 5370 3620510401 10676 # include "d_proto.h" unsigned short d_lxill[8], /* local transmit illegal codes */ d_lrill[8], /* local receive illegal codes */ d_rxill[8], /* remote transmit illegal codes */ d_rrill[8], /* remote receive illegal codes */ d_lcvec[8]; /* local transmit encoding vector */ int d_lxmax, /* local transmit maximum packet size */ d_lrmax, /* local receive maximum packet size */ d_rxmax, /* remote transmit maximum packet size */ d_rrmax, /* remote receive maximum packet size */ d_master, /* this is non-zero if this module is the */ /* protocol master. */ d_rcvseq, /* the expected sequence number of the next */ /* packet to be received. */ /* protocol lockup caused change: is now the */ /* sequence number of the last packet received */ /* and is incremented when search for new */ /* packet is initiated */ d_snseq, /* the sequence number of the next packet */ /* be transmitted. */ /* Changed like d_rcvseq */ d_maxtext; /* the maximum length of the text portion */ /* of a packet. */ char d_snesc, /* escase character to used for encoding */ /* transmissions. */ d_rcvesc; /* escape character for decoding packets */ /* received. */ /* * the following globals are used to implement the DATA transmit * and receive queues */ char d_xqueue[MAXPACKET + 2], /* the transmit queue */ *d_xqpt = &d_xqueue[0]; /* pointer to the next available */ /* slot in the transmit queue. */ int d_xqcnt; /* number of bytes currently loaded */ /* in the transmit queue */ char d_rdqueue[MAXPACKET + 2], /* the receive queue */ *d_rdqpt = &d_rdqueue[0]; /* pointer to the next available */ /* slot in the receive queue. */ int d_rdqcnt, /* number of unread characters in */ /* the receive queue. */ d_rdeot; /* non-zero if the packet currently */ /* on the queue had EOT set. */ /* * Things which are subject to tailoring for a given installation */ char d_esclist[] = "`~#%$&!|^@"; /* list of possible escape characters */ /* in order of preference */ to be logged */ /* VARARGS1 */ d_scerr (format, a, b, c, d, e, f, g, h) char *format; unsigned a, b, c, d, e, f, g, h; { #ifdef D_LOG char tmpfmt[256]; sprintf (tmpfmt, "%s%s", "Error in %s, line %d: ", format); d_log ("d_scerr", tmpfmt, d_scmmdf/lib/dial/gen 555 0 12 57 3620510401 7060 make -f ../../Makefile.com -f Makefile.real $* ize */ d_rrmax, /* remote receive maximum packet size */ d_master, /* this is non-zero if this module is the */ /* protocol master. */ d_rcvseq, /* the expected sequence number of the next */ /* packet to be received. */ /* protocol lockup caused change: is now the */ /* sequence number of the last packet received */ /* mmdf/lib/dial/d_path.c 444 0 12 7724 3620510401 10034 # include "d_proto.h" # include "d_returns.h" #include <signal.h> # include "d_syscodes.h" /* Jul 81 D. Crocker convert maxlen>255 -> 255 */ /* * D_SNPATH * * this routine is called to send a PATH packet. it is used for both * XPATH and RPATH packets. * * type -- the type of the packet to send (XPATH or RPATH) * * ack -- the type of acknowledgement to wait for * * ackwait -- the amount of time to wait for the acknowledgement * * maxleng -- the maximum packet length for this path * * bitvector -- the bit vector of illegal characters (local) for this * path. */ d_snpath(type, maxleng, bitvector) char type; int maxleng; unsigned short bitvector[]; { extern int d_snseq; register int length, result; char packet[MAXPACKET + 2], txtbuf[LPATH]; /* assemble the text portion */ if (maxleng > 255) /* must fit into 8-bit field */ { #ifdef D_LOG d_log("d_snpath", "max packet len > 255 (%d)", maxleng); #endif D_LOG maxleng = 255; /* should be safe to trim it down */ } txtbuf[0] = d_tohex((maxleng >> 4) & 017); txtbuf[1] = d_tohex(maxleng & 017); d_bldhvec(bitvector, &txtbuf[2]); /* build the complete packet and send it */ d_snseq = d_incseq(d_snseq); length = d_bldpack(type, d_snseq, 1, txtbuf, packet); result = d_snpkt(type, packet, length); return(result); } /* * D_GETPATH * * this routine is called to get a path packet of the specified type * from the port. it thens calls the routine to decode it. * * type -- the type of path packet we're looking for * * timeout -- the amount of time that we're willing to wait to receive * it * * maxleng -- pointer to where the maximum packet length specified in the * packet should be loaded * * bitvector -- pointer to the bit vector where the illegal character * bit vector should be placed */ d_getpath(type, maxleng, bitvector) char type; int *maxleng; unsigned short bitvector[]; { register int length, result; char packet[MAXPACKET + 2]; length = d_watch(packet, type); if (length < 0) return(length); /* send the packet to the decoding routine */ result = d_setpath(packet, length, maxleng, bitvector); return(result); } /* * D_SETPATH * * this routine decodes the text portion of a path packet and loads * the given variables * * packet -- pointer to the path packet * * length -- length of the path packet * * maxleng -- pointer to where the maximum packet length given in the * packet should be loaded * * bitvector -- pointer to the bit vector to be loaded */ d_setpath(packet, length, maxleng, bitvector) char packet[]; int length, *maxleng; unsigned short bitvector[]; { extern int d_errno; register int dig1, dig2, max; int result; /* check the length */ if (length != LPATH) { #ifdef D_LOG d_log("d_setpath", "bad length (%d) path packet received", length); #endif D_LOG d_errno = D_PATHERR; return(D_FATAL); } /* decode the maximum packet length */ dig1 = d_fromhex(packet[TEXTOFF]); dig2 = d_fromhex(packet[TEXTOFF + 1]); if ((dig1 < 0) || (dig2 < 0)) { #ifdef D_LOG d_log("d_setpath", "bad hex character in path (length '%c%c', type '%c')", dig1, dig2, packet[TYPEOFF]); #endif D_LOG d_errno = D_PATHERR; return(D_FATAL); } max = (dig1 << 4) | dig2; if ((max < LPATH) || (max > MAXPACKET)) { #ifdef D_LOG d_log("d_setpath", "bad max packet size (%d) in path packet, type '%c'", max, packet[TYPEOFF]); #endif D_LOG d_errno = D_PATHERR; return(D_FATAL); } *maxleng = max; /* decode the illegal character string */ result = d_bldcvec(&packet[TEXTOFF + 2], bitvector); return(result); } prawq--); } /* have to read the port. mmdf/lib/dial/d_parse.c 444 0 12 7637 3671073166 10237 # # include <ctype.h> # include <stdio.h> # include "d_proto.h" # include "d_returns.h" extern int d_nfields; extern char *d_fields[]; extern char *strcpy(); extern char *strncpy(); /* * D_LINPARSE * * routine which takes a line and breaks it into fields on blanks. it * simply accumulates strings embedded in quotes without regard to blanks * until the closing quote. the input string is altered in place by the * routine by the intsertion of nulls where there was blanks. * * fields - pointer to array which will be loaded with pointers to the * fields in the input string. * * linebuf - pointer to the input line buffer * * maxfields - the maximum number of fields allowed on a line * * the routine returns -1 if there are more than the indicated maximum * number of fields; -2 for unclosed quoted strings. in the normal * case the number of fields isolated is returned. */ d_linparse(fields, linebuf, maxfields) char *fields[], linebuf[]; int maxfields; { char *cp, *ncp; register int nfields, result; nfields = 0; if ( (result = d_dollar(linebuf)) < 0) return (result); cp = linebuf; for (;;) { result = d_getword(cp, &fields[nfields], &ncp); switch (result) { case -1: /* end of line */ return (nfields); case -2: /* unterminated quotes */ return (-2); default: /* another call, another word */ break; } if (++nfields > maxfields) return (-1); cp = ncp; } } d_getword (linebuf, start, next) char *linebuf; char **start, **next; { register char *cp; cp = linebuf; /* skip over any leading white space */ while ((*cp == ' ') || (*cp == '\t')) *cp++ = '\0'; /* return -1 to indicate that there was no word */ if ((*cp == '\n') || (*cp == '\0')) { *cp = '\0'; return (-1); } /* save the pointer to the start of the field */ *start = cp; /* special case for quoted strings. embedded blanks are * ignored. must find another '"' before the newline or null. */ if (*cp == '"') { cp++; while ((*cp != '"') && (*cp != '\n') && (*cp != '\0')) cp++; if ((*cp == '\n') || (*cp == '\0')) return(-2); cp++; } else while ((*cp != ' ') && (*cp != '\t') && (*cp != '\n') && (*cp != '\0')) cp++; *cp++ = '\0'; *next = cp; return (0); } d_dollar(line) char *line; { char templine[MAXSCRLINE], number[10]; register char *cp; register int index; int changes = 0; int len = 0; cp = line; for ( ; ;) { switch (*cp) { case '\0': case '\n': if (changes) { templine[len++] = '\0'; (void) strcpy(line, templine); line[len] = '\0'; /* parser requires 2 nulls */ } return(0); case '$': changes++; cp++; if (*cp == '$') /* double $$ expands to $ */ { if (addout(templine, &len, cp++, 1)) return(-5); break; } /* gobble up number and expand */ for (index = 0; isdigit(*cp) && (*cp!='\0') && (*cp!='\n') && (index<10); index++) number[index] = *cp++; number[++index] = '\0'; index = atoi(number); #ifdef D_DBGLOG d_dbglog("d_dollar", "variable number: %d, max: %d", index, d_nfields); #endif if ((index <= 0) || (index > d_nfields)) return(-4); if (addout(templine,&len, d_fields[index],strlen(d_fields[index]))) return(-5); break; default: if (addout(templine, &len, cp++, 1)) return(-5); break; } } } addout(buf, len, str, cnt) char *buf, *str; int *len, cnt; { if (*len + cnt >= MAXSCRLINE - 2) return(1); (void) strncpy(&buf[*len],str,cnt); *len += cnt; return(0); } is altered in place by the * routine by the intsertion of nulls where there was blanks. * mmdf/lib/dial/d_master.c 444 0 12 12347 3641232065 10422 # include "util.h" # include "d_proto.h" # include "d_returns.h" # include <signal.h> # include "d_syscodes.h" # include "d_clogcodes.h" # include "ll_log.h" /* * D_MASCONN * * this routine is called by the master protocl module to initiate a * connection to a remote system. the scriptfile used contains all * of the information necessary to dial the remote machine, set * parameters. * * scriptfile -- name of the script file * * logfile -- name of the file to be used for logging. if this arg * is 0, then logging is done on the standard output * * tson -- this argument should be non-zero if the protocol module * should keep a transcript of the connection * * tsfile -- name of the file in which the transcript should be kept. * * debug -- set to non-zero to enable more extensive logging * */ d_masconn(scriptfile, logfile, tson, tsfile, debug) char *scriptfile, *tsfile; struct ll_struct * logfile; int tson, debug; { extern int d_debug, d_master, d_snseq, d_rcvseq; register int result; /* open the log and transcript file */ d_debug = debug; if ((result = d_opnlog(logfile)) < 0) return(result); d_init(); if (tson) if ((result = d_tsopen(tsfile)) < 0) return(result); d_master = 1; /* numbers will be incremented before use, thus effectively start at 0 */ d_snseq = 3; d_rcvseq = 3; /* open the script file and start interpreting it */ if ((result = d_scopen(scriptfile, 0, (char **) 0)) < 0) return(result); if ((result = d_script()) < 0) return(result); /* when the script file pauses, start the protocol */ result = d_masstart(); return(result); } /* * D_MASDROP * * this routine is called by the master to drop the connection to the * other side. the port, lock, transcript, and log files are closed. * * sndquit -- non-zero if a QUIT sequence should be initiated with * the other side * * doscript -- non-zero if the script should be resumed */ d_masdrop(sndquit, doscript) register int sndquit, doscript; { register int result; d_dbglog("d_masdrop", "dropping connection, sndquit %d, doscript %d", sndquit, doscript); result = 0; /* go through the QUIT sequence if we're supposed to */ if (sndquit) d_snquit(); /* resume the script, if enabled */ if (doscript) { #ifdef D_LOG d_log("d_masdrop", "Resuming script file."); #endif D_LOG result = d_script(); } #ifdef D_LOG d_log("d_masdrop", "master dropped connection"); #endif D_LOG /* drop the connection. close the transcript and log */ d_drop(); d_tsclose(); d_clslog(); return(result); } /* * D_MASSTART * * this routine is called in the master to start up the protocol once * the matching slave routine has been started. this function exchanges * RESET's with the slave and sets up the escape characters for the * encoding. */ d_masstart() { extern int d_lxmax, d_lrmax, d_rxmax, d_rrmax, d_maxtext, d_nbuff; extern int d_wpack; extern int d_errno; extern char d_snesc, d_rcvesc; extern unsigned short d_lxill[], d_lrill[], d_rxill[], d_rrill[], d_lcvec[]; register int result; #ifdef D_DBGLOG d_dbglog("d_masstart", "Beginning master host startup sequence"); #endif D_DBGLOG /* get the XPATH and RPATH packets from the slave. then build * the local transmit code vector. */ if ((result = d_getpath(XPATH, &d_rxmax, d_rxill)) < 0) return(result); #ifdef D_DBGLOG d_dbglog("d_masstart", "XPATH received ok."); #endif D_DBGLOG if ((result = d_getpath(RPATH, &d_rrmax, d_rrill)) < 0) return(result); #ifdef D_DBGLOG d_dbglog("d_masstart", "RPATH received ok."); #endif D_DBGLOG /* If appropriate, send the packet describing how we are buffering * packets. This cannot be sent to some sites, as they do not * recognize the packet type. */ if (d_wpack != 0) d_setbuff (d_nbuff); d_orbitvec(d_lxill, d_rrill, d_lcvec); d_maxtext = d_minimum(d_lxmax, d_rrmax); #ifdef D_DBGLOG d_dbglog("d_masstart", "Maximum transmit packet size set to %d", d_maxtext); #endif D_DBGLOG d_maxtext -= (LHEADER + LTERM); /* now send the XPATH and RPATH to the slave */ if ((result = d_snpath(XPATH, d_lxmax, d_lxill)) < 0) return(result); #ifdef D_DBGLOG d_dbglog("d_masstart", "XPATH sent ok."); #endif D_DBGLOG if ((result = d_snpath(RPATH, d_lrmax, d_lrill)) < 0) return(result); #ifdef D_DBGLOG d_dbglog("d_masstart", "RPATH sent ok."); #endif D_DBGLOG /* get the escape code we're to use for encoding text sent * to the remote host. */ if ((result = d_getescape()) < 0) return(result); #ifdef D_DBGLOG d_dbglog("d_masstart", "ESCAPE received ok. Transmit escape '%c'", d_snesc); #endif D_DBGLOG if ((result = d_snfescape()) < 0) return(result); #ifdef D_DBGLOG d_dbglog("d_masstart", "ESCAPE sent ok. Receive escape '%c'", d_rcvesc); #endif D_DBGLOG #ifdef D_LOG d_log("d_masstart", "Master protocol startup completed ok."); #endif D_LOG return(D_OK); } sndquit %d, doscript %d", sndquit, doscript); result = 0; /* go through the QUIT sequence if we're supposed to */ if (sndquit) d_snquit(); /* resume the script, if enabled */ if (doscript) { #ifdef D_LOG d_log("d_masdrop", "Resummdf/lib/dial/d_log.c 444 0 12 11113 3620510402 7665 # include "util.h" # include "d_returns.h" # include <stdio.h> # include "d_proto.h" # include "d_structs.h" # include "ll_log.h" int d_debug; /* set to non-zero if debug loggin should be done */ static struct ll_struct *d_logptr; /* file pointer to logging file */ /* * D_OPNLOG * * this routine is called to open the log file for appending. the * date is written as the first entry. * * filename -- path name of the logging file */ d_opnlog(logptr) struct ll_struct * logptr; { d_logptr =(struct ll_struct *) (logptr); /* save the pointer */ if (d_logptr -> ll_level >= LLOGPTR) d_debug = 1; return(D_OK); } /* * D_CLSLOG * * this routine is called to close the log file if one was open. */ d_clslog() { #ifdef D_DBGLOG d_log("d_clslog", "closing log file"); #endif D_DBGLOG ll_close(d_logptr); return(D_OK); } /* * D_LOG * * this routine writes entries on the log file. its use is similar * to printf, and it accepts the same style format strings. * * routine -- the name of the subroutine where the call to this function * is made. * * format -- printf style format string * * a, b, c, d, ... -- variables to be printed */ /* VARARGS2 */ d_log(routine, format, a, b, c, d, e, f, g, h) char *routine, *format; unsigned a, b, c, d, e, f, g, h; { #ifdef D_LOG #ifdef D_DBGLOG if (d_debug) /* print routine name if debugging */ d_dbglog (routine, format, a, b, c, d, e, f, g, h); else #endif D_DBGLOG ll_log(d_logptr, LLOGGEN, format, a, b, c, d, e, f, g, h); #endif D_LOG return(D_OK); } /* * D_DBGLOG * * this routine is called to write debugging logging stuff on the log * file. the entry is made only if the 'd_debug' variable is non-zero. * * routine -- the name of the subroutine where the call to this function * is made. * * format -- printf style format string * * a, b, c, d, ... -- variables to be printed */ /* VARARGS2 */ d_dbglog(routine, format, a, b, c, d, e, f, g, h) char *routine, *format; unsigned a, b, c, d, e, f, g, h; { #ifdef D_DBGLOG char fmtbuf[64]; if (d_debug) { sprintf (fmtbuf, "%s%s", "%s: ", format); ll_log(d_logptr, LLOGBTR, fmtbuf, routine, a, b, c, d, e, f, g, h); } #endif D_DBGLOG } /* /* * D_FILLOG * * routine which is used to log messages concerning things found in * files. the point of this routine is that there are a lot of messages * in this package that concern errors found on lines in files. i got * tired of putting stuff in the format to 'd_log' calls to write out * the file name and line number, so i put that in this call. all this * routine does is build a special format string containg fields for the * name and number, and then call 'd_log'. * * filename -- name of the file to be put in entry * * linenum -- line number in file to be put in entry * * routine -- the name of the subroutine where the call to this function * is made. * * format -- printf style format string * * a, b, c, d, ... -- variables to be printed */ /* VARARGS4 */ d_fillog(filename, linenum, routine, format, a, b, c, d, e, f, g, h) char *filename, *routine, *format; unsigned linenum, a, b, c, d, e, f, g, h; { #ifdef D_LOG char tmpfmt[85]; /* if a filename is given, make a new format to include * it. then call the log routine. */ if (filename) { sprintf (tmpfmt, "%s%s", "%s, line %d: ", format); d_log(routine, tmpfmt, filename, linenum, a, b, c, d, e, f, g, h); } else d_log(routine, format, a, b, c, d, e, f, g, h); #endif D_LOG } /* * D_PLOG * * Log a message at a given priority. * It is necessary to have this here so as to access the * struct d_logptr. * * priority - the priority level to log at. * format, a, b, ... - standard printf stuff * */ d_plog (priority, format, a, b, c, d, e, f, g, h) int priority; char *format; unsigned a, b, c, d, e, f, g, h; { ll_log (d_logptr, priority, format, a, b, c, d, e, f, g, h); } /* * D_PELOG * * Log a message at a given priority and print ERRNO * It is necessary to have this here so as to access the * struct d_logptr. * * priority - the priority level to log at. * format, a, b, ... - standard printf stuff * */ d_pelog (priority, format, a, b, c, d, e, f, g, h) int priority; char *format; unsigned a, b, c, d, e, f, g, h; { ll_err (d_logptr, priority, format, a, b, c, d, e, f, g, h); } f ((max < LPATH) || (max > MAXPACKET)) { #ifdef D_LOG d_log("d_setpath", "bad max packet size (%d) in path packet, type '%c'", max, packet[TYPEOFF]); #endif D_LOG d_errno = D_PATHERR; return(D_FATAL); } *maxleng = max; /* decode the illegal character string */ result = d_bldcvec(&packet[TEXTOFF + 2], bitvector); return(result); } prawq--); } /* have to read the port. mmdf/lib/dial/d_init.c 444 0 12 1122 3620510402 10026 # include "d_returns.h" /* 4 Sep 81 D. Crocker getuid() was 11-dependent /* * D_INIT * * routine which is called to do any initialization that's required * before the rest of the routines can be used. all that's done here * now is to get the user's name and path. */ d_init() { extern char d_uname[], d_upath[]; extern int d_errno; int effecid; int realuid; getwho (&realuid, &effecid); if (d_getuser(realuid, d_uname, d_upath) < 0) { d_errno = D_INITERR; return(D_FATAL); } return(D_OK); } logptr, LLOGGEN, format, a, b, c, d, e, f, g, h); #endif D_LOG return(D_OK); } /* * D_DBGLOG * * this routine is called to write debugging logging stuff on the log * file. the entry is made only if the 'd_debug' variable is non-zero. * * routine -- the name of the subroutine where the call to this function * is made. * * format -- printf style format string * * a, b, c, mmdf/lib/dial/d_escape.c 444 0 12 5341 3620510403 10333 #include "d_proto.h" #include <signal.h> #include "d_syscodes.h" #include "d_returns.h" /* * D_FINDESC * * this routine attempts to find a suitable escape character to be used * by the remote host for packets transmitted to us. the character is * chosen from the 'd_esclist' by checking each of the proposed escapes * to see if it can be both transmitted by the remote system and received * by us. the selected character is returned, if one is found; otherwise * D_FATAL. */ char d_findesc() { extern char d_esclist[]; extern unsigned short d_lrill[], d_rxill[]; extern int d_errno; register char *cp; for (cp = d_esclist; *cp; cp++) if (!d_testbit(*cp, d_lrill) && !d_testbit(*cp, d_rxill)) { #ifdef D_DBGLOG d_dbglog("d_findesc", "Character '%c' selected as escape for receiving", *cp); #endif D_DBGLOG return(*cp); } #ifdef D_LOG d_log("d_findesc", "Can't find suitable escape character"); #endif D_LOG d_errno = D_NOESC; return(D_FATAL); } /* * D_SNDESCAPE * * this routine forms an ESCAPE packet and sends it to the remote host */ d_snfescape() { extern char d_rcvesc; extern int d_snseq; register int length, result; char packet[MAXPACKET + 2], esctemp[4]; #ifdef D_DBGLOG d_dbglog("d_snfescape", "sending ESCAPE to other end"); #endif D_DBGLOG /* find an escape character */ if ((d_rcvesc = d_findesc()) < 0) return(d_rcvesc); /* form text containing the ascii value of the escape * character expressed as a 2 digit hex number and send it. */ esctemp[0] = d_tohex((d_rcvesc >> 4) & 017); esctemp[1] = d_tohex(d_rcvesc & 017); esctemp[2] = '\0'; d_snseq = d_incseq(d_snseq); length = d_bldpack(ESCAPE, d_snseq, 1, esctemp, packet); result = d_snpkt(ESCAPE, packet, length); return(result); } /* * D_GETESCAPE * * this routine watches for an incoming ESCAPE packet from the remote * host. it thens set the transmit escape character. * */ d_getescape() { extern int d_errno; extern char d_snesc; register int length; char packet[MAXPACKET + 2]; #ifdef D_DBGLOG d_dbglog("d_getescape", "looking for ESCAPE from other end"); #endif D_DBGLOG /* set timer so we don't wait forever */ length = d_watch(packet, ESCAPE); if (length < 0) return(length); if (length != LESCAPE) { #ifdef D_DBGLOG d_dbglog("d_escape", "bad ESCAPE packet length (%d)", length); #endif D_DBGLOG d_errno = D_INITERR; return (D_FATAL); } d_snesc = d_fromhex(packet[ESCOFF]); d_snesc = (d_snesc << 4) | d_fromhex(packet[ESCOFF + 1]); return(D_OK); } rill) && !d_testbit(*cp, d_rxill)) { #ifdef D_DBGLOG d_dbglog("d_findesc", "Character '%c' selected as escape for receiving", *cp); #endif D_DBGLOG return(*cp); } #ifdef D_LOG d_log("d_findesc", "Can't find suitable escape character"); #emmdf/lib/dial/d_dglobals.c 444 0 12 5425 3664425151 10702 # include "util.h" #ifdef SYS5 # include <termio.h> #else # include <sgtty.h> # include "d_proto.h" #endif SYS5 # include <stdio.h> # include "d_structs.h" struct dialports *d_prtpt; /* pointer to currently user port in */ /* 'd_prts' */ struct directlines *d_ptline; /* pointer to currently used line in */ /* 'd_lines' */ FILE *d_prtfp = (FILE *) EOF; /* port stdio file pointer */ int d_lckfd = -1, /* lock file descriptor */ d_baudrate, /* index of baud rate for this connection */ d_didial, /* set to non-zero if this is a dial-up */ /* connection. */ d_errno; /* error code */ char d_uname[10], /* login name of user */ d_upath[50]; /* login directory path name of user */ /* * this structure defines the baud rate strings that will be accpeted * in phone number specifications. */ struct speedtab d_spdtab[] = { "0", B0, "50", B50, "75", B75, "110", B110, "134.5", B134, "134", B134, "150", B150, "200", B200, "300", B300, "600", B600, "1200", B1200, "1800", B1800, "2400", B2400, "4800", B4800, #ifdef SYS5 "7200", B7200, #endif SYS5 "9600", B9600, #ifdef SYS5 "19200", B19200, #else "19200", EXTA, #endif SYS5 "EXTA", EXTA, "exta", EXTA, "38400", EXTB, "EXTB", EXTB, "extb", EXTB, 0, 0, }; /* * array of ascii baud rate names indexed by the speed. used to print * understandable diagnostics. */ int d_xretry = NSENDTRY; /* Init # of retries to make */ int d_toack = DACKWAIT; /* Init time out time for ack */ int d_todata = DATAWAIT; /* Init time out for data */ #ifdef SYS5 unsigned short d_prbitc = PORTPONC; /* terminal protocol bit on */ unsigned short d_prbiti = PORTPONI; unsigned short d_prbito = PORTPONO; unsigned short d_prbitl = PORTPONL; unsigned short d_scbitc = PORTSONC; /* terminal script bits on */ unsigned short d_scbiti = PORTSONI; unsigned short d_scbito = PORTSONO; unsigned short d_scbitl = PORTSONL; #else int d_pron = PORTPON; /* terminal protocol bit on */ int d_proff = PORTPOFF; /* terminal protocal bit off */ int d_scon = PORTSON; /* terminal script bits on */ int d_scoff = PORTSOFF; /* terminal script bits off */ #endif SYS5 int d_nbuff = 1; /* The number of messages to send out before * requiring an ACK. Initially, get an ACK * immediately. */ int d_wpack; /* Non-zero if the window specification * packet should be sent. */ */ struct directlines *d_ptline; /* pointer to currently used line in */ /* 'd_lines' */ FILE *d_prtfp = (FILE *) EOF; /* port stdio file pointer */ int d_lckfd = mmdf/lib/dial/d_data.c 444 0 12 2466 3620510403 10011 #include "d_proto.h" #include "d_returns.h" #include <signal.h> #include "d_syscodes.h" /* * D_SNDDATA * * this function is called to send the stuff in the data transmit queue. * * eot -- set to non-zero if the EOT bit should be set on the packet */ d_sndata(eot) register int eot; { extern char d_xqueue[], *d_xqpt; extern int d_snseq, d_xqcnt; register int length, result; char packet[MAXPACKET + 2]; d_snseq = d_incseq(d_snseq); length = d_bldpack(DATA, d_snseq, eot, d_xqueue, packet); d_xqcnt = 0; d_xqpt = d_xqueue; result = d_snpkt(DATA, packet, length); return(result); } /* * D_GETDATA * * this function is called to read a DATA packet into the receive * data queue. */ d_getdata() { extern char d_rdqueue[], *d_rdqpt; extern int d_rdqcnt, d_rdeot; register int nout, length; char packet[MAXPACKET + 2]; #ifdef D_DBGLOG d_dbglog("d_getdata", "called for more stuff"); #endif D_DBGLOG length = d_watch(packet, DATA); if (length < 0) return(length); if ((nout = d_decode(&packet[TEXTOFF], length - 6, d_rdqueue)) < 0) return(nout); d_rdqcnt = nout; d_rdqpt = d_rdqueue; if (d_fromhex(packet[FLAGOFF]) & EOFBIT) d_rdeot = 1; return(D_OK); } "1800", B1800, "2400", B2400, "4800", B4800, #ifdef SYS5 "7200", B7200, #endif SYS5 "9600", B9600, #ifdef SYS5 "19200", B19200, #else "19200", EXTA, #endif SYS5 "EXTA", mmdf/lib/dial/d_connect.c 444 0 12 44511 3671073170 10561 # include "util.h" # include <signal.h> # include "d_syscodes.h" # include "d_clogcodes.h" # include "d_proto.h" # include "d_returns.h" # include "d_structs.h" #ifdef SYS5 # include <termio.h> #else # include <sgtty.h> #endif SYS5 # include <sys/stat.h> #ifdef V4_2BSD # include <sys/file.h> #endif V4_2BSD /* Jun 81 Dave Crocker fixed some text, referring to errno * Aug 81 Dave Crocker added sleep before kill & alarm around * parent waiting for acu * Sep 81 Dean Krafft put signal (ALRM,d_catch) before ACUWAIT * Apr 82 Dave Crocker modify dial timings & lock-file handling * Jun 82 Dave Crocker do_dial use of kill() moved around * d_drop() timeouts around modem line fclose() * Jan 84 Dennis Rockwell converted to do sane things with 4.2 select(2) * by setting non-blocking open option * Feb 84 Dan Long moved d_cstart to start of phone conversation * Feb 84 Dan Long added support for line-type selection * Mar 84 Dennis Rockwell converted from select to s_io library */ extern int errno; extern int d_errno; extern struct directlines *d_lines; extern struct directlines *d_ptline; extern struct dialports *d_prtpt; extern struct dialports *d_prts; extern char d_uname[]; extern int d_didial; extern int d_baudrate; extern FILE * d_prtfp; extern struct speedtab d_spdtab[]; extern char **d_typelist(); extern char *strdup(); extern char *multcat(); extern int d_lckfd; /* * D_CONNECT * * this routine is called to make a connection to the remote system. it * is capable of using direct lines or dial-up connections, as specified * in the connection path table ('numtab'). * * numtab -- array of structure whch contain speed indicies and associated * phone numbers or direct connect line names. * * nnumbs -- number of entries in numtab * * ntries -- number of time to retry a connection. * * interval -- number of seconds between connection retries. */ d_connect (numtab, nnumb, ntries, interval) struct telspeed numtab[]; int nnumb, ntries, interval; { register int num, try, result; #ifdef D_DBGLOG d_dbglog ("d_connect", "nnumb %d, ntries %d, interval %d", nnumb, ntries, interval); #endif /* make sure we were given some numbers to dial */ if (nnumb <= 0) { #ifdef D_LOG d_log ("d_connect", "no connection paths given!"); #endif D_LOG d_errno = D_NONUMBS; return (D_FATAL); } /* for each attempt, try each of the paths given */ for (try = 1; try <= ntries; try++) { for (num = 0; num < nnumb; num++) { #ifdef D_DBGLOG d_dbglog ("d_connect", "%d: (speed %d) num '%s'", num, numtab[num].t_speed, numtab[num].t_number); #endif D_DBGLOG if (numtab[num].t_speed == 0) result = d_direct (numtab[num].t_number); else result = d_dial (numtab[num].t_number, numtab[num].t_speed); if (result != D_NONFATAL) return (result); } /* if we're going to try again, and we're supposed to wait awhile, sleep */ if ((try < ntries) && (interval > 0)) { #ifdef D_DBGLOG d_dbglog ("d_connect", "sleeping for %d seconds", interval); #endif D_DBGLOG sleep ((unsigned) interval); } } #ifdef D_LOG d_log ("d_connect", "couldn't make connection."); #endif D_LOG return (D_FATAL); } /* * D_DIRECT * * routine called to try to make a direct connection using the line * given as the argument. * * linename -- name of direct connect line */ d_direct (linename) register char *linename; { int fd; register int result; #ifdef D_LOG register int i; #endif D_LOG #ifdef SYS5 struct termio hupbuf; #endif SYS5 /* see if a direct line with the given name is available */ if ((result = d_avline (linename)) < 0) return (result); d_baudrate = d_ptline -> l_speed; #ifdef D_LOG for (i=0; d_spdtab[i].s_speed != 0; i++) { if (d_spdtab[i].s_index == d_baudrate) { d_log ("d_direct", "Trying direct connection on %s at %s.", d_ptline -> l_tty, d_spdtab[i].s_speed); break; } } #endif D_LOG /* found a line. set an alarm in case the open hangs */ if (setjmp (timerest)) { #ifdef D_LOG d_log ("d_direct", "direct line open timeout for %s", d_ptline -> l_tty); #endif D_LOG d_errno = D_PORTOPEN; return (D_FATAL); } s_alarm (CONNWAIT); #ifndef V4_2BSD if ((fd = open (d_ptline -> l_tty, 2)) >= 0) { #else V4_2BSD if ((fd = open (d_ptline -> l_tty, O_RDWR | O_NDELAY)) >= 0) { #endif V4_2BSD #ifdef SYS5 ioctl (fd, TCGETA, &hupbuf); hupbuf.c_cflag |= HUPCL; if (ioctl (fd, TCSETA, &hupbuf) < OK) { #else if (ioctl (fd, TIOCHPCL, 0) < OK) { #endif SYS5 #ifdef D_LOG d_log ("d_direct", "problem setting close-on-hangup; errno = %d", errno); #endif D_LOG } #ifdef TIOCEXCL if (ioctl (fd, TIOCEXCL, 0) < OK) { /* gain exclusive access [mtr] */ #ifdef D_LOG d_log ("d_direct", "problem gaining exclusive access; errno = %d", errno); #endif D_LOG } #endif TIOCEXCL } s_alarm (0); if (fd < 0) { #ifndef V4_2BSD if (errno == EINTR) { #ifdef D_LOG d_log ("d_direct", "direct line open timeout for %s", d_ptline -> l_tty); #endif D_LOG } else #endif V4_2BSD #ifdef D_LOG d_log ("d_direct", "direct line open errno %d for %s", errno, d_ptline -> l_tty); #endif D_LOG d_errno = D_PORTOPEN; return (D_FATAL); } /* stdio for input and straight write() for output */ d_prtfp = fdopen (fd, "r"); if (d_ttsave (d_prtfp, d_ptline -> l_tty) < 0 || d_ttscript (d_baudrate) < 0) { #ifdef D_LOG d_log ("d_direct", "can't set direct-line parameters; errno = %d", errno); #endif D_LOG (void) close (fd); fd = NOTOK; /* make sure it hangs up, later */ d_errno = D_PORTOPEN; return (D_FATAL); } #ifdef D_LOG d_log ("d_direct", "Open"); #endif D_LOG return (D_OK); } /* * D_DIAL * * routine called to make dial-up connection to remote system * * number -- number to be called * * speed -- speed index */ d_dial (number, speed) char *number; int speed; { register int result; char **tlist; /* get ordered list of acceptable linetypes for this phone number */ tlist = d_typelist(&number); /* try to find an available dial-out port for the given speed and types */ result = d_avport (speed, tlist); if (result < 0) return (result); d_baudrate = speed; #ifdef D_LOG d_log ("d_dial", "port %s at %d", d_prtpt -> p_port, d_baudrate); #endif D_LOG /* do the dialing */ if ((result = d_dodial (number)) < 0) d_drop (); return (result); } /* * D_AVPORT * * routine which finds an available dial-out port with the given speed. * an ordered list of acceptable types is given--the first type is best. * exclusive open of lock files is employed to check for other * users. if the open succeeds, a global pointer 'd_prtpt' is set * to the entry in the available port structure. * * speed -- the speed index of the port be sought * * typelist -- a list of acceptable line types for the port */ d_avport (speed, typelist) int speed; char **typelist; { register struct dialports *dpt; register int result; #ifdef D_LOG register int i; #endif D_LOG char **tpt; #ifdef D_DBGLOG d_dbglog ("d_avport", "looking for port with speed %d", speed); #endif D_DBGLOG /* for each type in the typelist, look at each of the known dial-out */ /* ports. if the type and speed match, check the access list (if there */ /* is one). they try to lock the port. */ for (tpt = typelist; *tpt; tpt++) { #ifdef D_DBGLOG d_dbglog("d_avport", "checking for type: %s", *tpt); #endif D_DBGLOG for (dpt = d_prts; dpt -> p_port; dpt++) { if ( ((dpt -> p_speed & (1 << (speed - 1))) == 0) || !lexequ(dpt -> p_ltype, *tpt) ) continue; /* check the access list for this port, if there is one */ if (dpt -> p_access) { if ((result = d_chkaccess (d_uname, dpt -> p_access)) < 0) return (result); if (result == D_NO) continue; } /* try to lock the port. if we can, we're in business. */ if ((result = d_lock (dpt -> p_lock)) < 0) return (result); if (result == D_OK) { d_prtpt = dpt; d_didial = 1; return (D_OK); } } } /* if we get here, then we've looked at all the ports and found none */ /* that are available. */ #ifdef D_LOG for (i=0; d_spdtab[i].s_speed != 0; i++) { if (d_spdtab[i].s_index == speed) { d_log ("d_avport", "No %s baud dial-out ports available", d_spdtab[i].s_speed); break; } } #endif D_LOG if (typelist[1] != 0) { #ifdef D_LOG d_log ("d_avport", "of type: %s", typelist[0]); #endif D_LOG } else { char *cp, *textlist; textlist = strdup(typelist[0]); for (tpt = &typelist[1]; *tpt != 0; tpt++) { cp = multcat(textlist, ",", *tpt, (char *)0); free(textlist); textlist = cp; } #ifdef D_LOG d_log("d_avport", "of types: %s", textlist); #endif D_LOG } d_errno = D_NOPORT; return (D_NONFATAL); } /* * D_AVLINE * * routine to search for a direct connect line of the given name, and to * secure its use exclusively. * * linename -- direct connect line name */ d_avline (linename) register char *linename; { register struct directlines *lpt; register int result; #ifdef D_LOG int foundone = 0; /* any lines exist at all? */ #endif D_LOG /* cycle through the available direct lines */ for (lpt = d_lines; lpt -> l_name != (char *) 0; lpt++) { if (strcmp (linename, lpt -> l_name) != 0) continue; #ifdef D_LOG foundone = 1; #endif D_LOG /* check the access (if there is one) */ if (lpt -> l_access != (char *) 0) { if ((result = d_chkaccess (d_uname, lpt -> l_access)) < 0) return (result); if (result == D_NO) continue; } /* try to lock it. */ if ((result = d_lock (lpt -> l_lock)) < 0) return (result); if (result == D_OK) { d_ptline = lpt; d_didial = 0; return (D_OK); } } /* if we get here, there is no available direct line */ #ifdef D_LOG if (foundone) d_log ("d_avline", "No %s direct lines available", linename); else d_log ("d_avline", "No direct lines named '%s' are known", linename); #endif D_LOG d_errno = D_NOPORT; return (D_NONFATAL); } /* * D_DODIAL * * this routine trys to make the connection to the remote system. * it forks, the parent opens the port and the child drives the acu * * number -- pointer to the phone number string to be passed to the * dialer */ d_dodial (number) char *number; { register int parentpid, childpid; int fd; int errcode; #ifdef SYS5 struct termio hupbuf; #endif SYS5 #ifdef D_DBGLOG d_dbglog ("d_dodial", "about to attempt to call '%s'", number); #endif D_DBGLOG parentpid = getpid (); if ((childpid = d_tryfork ()) == -1) { #ifdef D_LOG d_log ("d_dodial", "couldn't fork to dial"); #endif D_LOG d_errno = D_FORKERR; return (D_FATAL); } /* the parent tries to open the port */ if (childpid) { #ifdef D_DBGLOG d_dbglog ("d_dodial", "dialing parent opening '%s'", d_prtpt -> p_port); #endif D_DBGLOG if (setjmp (timerest)) { d_ttrestore (); return (D_NONFATAL); } s_alarm (ACUWAIT); fd = open (d_prtpt -> p_port, 2); s_alarm (0); d_errno = errno; /* save, so kill does not overwrite */ #ifdef D_DBGLOG d_dbglog ("d_dodial", "parent open return (errno=%d)", d_errno); #endif D_DBGLOG d_cstart (number,d_prtpt -> p_ltype); /* carrier: start call timer */ kill (childpid, SIGKILL); /* so it doesn't waste it time */ errcode = pgmwait (childpid); /* gather up the dialing child */ #ifdef D_DBGLOG d_dbglog ("d_dodial", "acu child exit error code %d", errcode); #endif D_DBGLOG if (fd < 0) { if (errno != EINTR) /* not timeout or child kill */ { /* so must terminate */ #ifdef D_LOG d_log ("d_dodial", "can't open port '%s', errno #%d", d_prtpt -> p_port, d_errno); #endif D_LOG d_errno = D_PORTOPEN; return (D_FATAL); } } else { #ifdef SYS5 ioctl(fd, TCGETA, &hupbuf); hupbuf.c_cflag |= HUPCL; if (ioctl (fd, TCSETA, &hupbuf) < OK) #else if (ioctl (fd, TIOCHPCL, 0) < OK) #endif SYS5 { #ifdef D_LOG d_log ("d_dodial", "problem setting hangup on close; errno = %d", errno); #endif D_LOG (void) close (fd); fd = NOTOK; /* make sure it hangs up, later */ d_errno = D_PORTOPEN; return (D_FATAL); } else { d_prtfp = fdopen (fd, "r"); if (d_ttsave (d_prtfp, d_prtpt -> p_port) < 0 || d_ttscript (d_baudrate) < 0) { #ifdef D_LOG d_log ("d_dodial", "can't set dial-port parameters; errno = %d", errno); #endif D_LOG (void) close (fd); fd = NOTOK; /* make sure it hangs up, later */ d_errno = D_PORTOPEN; d_ttrestore (); return (D_FATAL); } } /* stdio for input and straight */ /* write() for output */ } switch (d_errno = errcode) { case 0: if (fd < 0) return (D_NONFATAL); return (D_OK); case D_BUSY: case D_ABAN: d_ttrestore (); return (D_NONFATAL); default: d_ttrestore (); return (D_FATAL); } } /* the child drives the acu. if there's an error, then a * signal is sent to the parent to interrupt his open call. * The error code is returned to the parent through his * exit status. */ d_errno = 0; sleep ((unsigned) 5); /* give parent time to get into open() */ d_drvacu (number); sleep ((unsigned) CONNWAIT); /* wait for open to complete */ kill (parentpid, SIGALRM); /* kick parent out of open call */ exit (d_errno); #ifdef lint return D_FATAL; #endif lint } /* * D_DRVACU * * this routine opens the appropriate acu and feeds the phone number to * it. it's possible that it's in use, so we'll wait up to a minute, * trying every couple of seconds. D_FATAL and D_NONFATAL errors are * possible here. * * number -- phone number string to be passed to the dialer */ d_drvacu (number) char *number; { int len; char linebuf[100]; register int acufd, warning; warning = 0; if (d_prtpt -> p_acupref != (char *) 0) (void) strcpy (linebuf, d_prtpt -> p_acupref); else linebuf[0] = '\0'; strcat (linebuf, number); if (d_prtpt -> p_acupost != (char *) 0) strcat (linebuf, d_prtpt -> p_acupost); len = strlen (linebuf); #ifdef D_DBGLOG d_dbglog ("d_drvacu", "about to write '%s' on acu", linebuf); #endif D_DBGLOG while (1) { /* make sure open & write don't hang */ /* this doesn't really work under 4.2 */ #ifdef D_DBGLOG d_dbglog ("d_drvacu", "opening dialer"); #endif D_DBGLOG if (setjmp (timerest)) { break; } s_alarm (DIALWAIT); acufd = open (d_prtpt -> p_acu, 2); #ifdef D_DBGLOG d_dbglog ("d_drvacu", "dialer open (errno=%d)", errno); #endif D_DBGLOG /* if we get it, then try to dial the number. if that goes alright, */ /* return */ if (acufd >= 0) { sleep ((unsigned) 1); /* let things settle down */ #ifdef D_LOG d_log ("d_drvacu", "Dialing..."); #endif D_LOG if (write (acufd, linebuf, len) != len) break; (void) close (acufd); /* free the dialer */ #ifdef D_DBGLOG d_dbglog ("d_drvacu", "acu successful write"); #endif D_DBGLOG s_alarm (0); return (D_OK); } /* we get here from a failure of the acu open. if it's worse than just */ /* being busy, break out to the common error decoder */ s_alarm(0); #ifdef D_DBGLOG d_dbglog ("d_drvacu", "acu open (errno=%d)", errno); #endif D_DBGLOG if (errno != EBUSY) break; /* print the warning message once, and then sleep before trying again */ if (!warning) { #ifdef D_LOG d_log ("d_drvacu", "waiting for dialer."); #endif D_LOG warning = 1; } sleep ((unsigned) 5); } /* this stuff prints error messages for failures of both the open and */ /* write calls */ s_alarm (0); switch (errno) { case EBUSY: #ifdef D_LOG d_log ("d_drvacu", "That number is busy."); #endif D_LOG d_errno = D_BUSY; return (D_NONFATAL); case EDNPWR: #ifdef D_LOG d_log ("d_drvacu", "dialer power off"); #endif D_LOG d_errno = D_NOPWR; return (D_FATAL); case EDNABAN: #ifdef D_LOG d_log ("d_drvacu", "call abandoned"); #endif D_LOG d_clog (LOG_ABAN); d_errno = D_ABAN; return (D_NONFATAL); case EDNDIG: #ifdef D_LOG d_log ("d_drvacu", "internal error: bad digit to dialer"); #endif D_LOG d_errno = D_SYSERR; return (D_FATAL); default: #ifdef D_LOG d_log ("d_drvacu", "system errno #%d", errno); #endif D_LOG d_errno = D_INTERR; return (D_FATAL); } } /* * D_DROP * * routine which is called to drop the connection to the remote system. * if closes the port and lock file, and logs the call, of this was a * dial-up. */ d_drop () { #ifdef SYS5 struct termio hupbuf; #endif SYS5 #ifdef D_DBGLOG d_dbglog ("d_drop", "dropping connection."); #endif D_DBGLOG /* restore the line to its old state */ d_ttrestore (); /* close port */ if (d_prtfp != (FILE *) EOF && d_prtfp != NULL) { if (setjmp (timerest)) { return; } s_alarm (CONNWAIT); #ifdef SYS5 ioctl (fileno(d_prtfp), TCGETA, &hupbuf); hupbuf.c_cflag &= ~CBAUD; /* set 0 baud -- hangup */ ioctl (fileno(d_prtfp), TCSETA, &hupbuf); #endif SYS5 fclose (d_prtfp); s_alarm (0); /* XXX Why commented out? */ /* d_prtfp = (FILE *) EOF; */ if ((FILE *) EOF == d_prtfp) { #ifdef D_LOG d_log ("d_drop", "problem closing connection line."); #endif D_LOG return; /* leave the line locked, in case of major problem */ } if (d_didial) d_clog (LOG_COMP); } /* release the port to others by closing lock file */ d_unlock (d_ptline -> l_lock); } one number string to be passed to the dialer */ d_drvacu (number) char *number; { int len; char linebuf[100]; register int acufd, warning; mmdf/lib/dial/d_clog.c 444 0 12 5646 3671073171 10043 # include "util.h" # include "d_clogcodes.h" # include "d_returns.h" # include <stdio.h> # include "d_proto.h" # include "d_structs.h" extern time_t time(); extern char *ctime(); extern char *d_calllog; extern char d_uname[]; extern int d_errno; static time_t d_cstime; /* time call started */ static char d_cnumber[30]; /* the number called */ static char d_linetype[30]; /* the type of dialout line used */ /* * D_CSTART * * routine called to note the beginning of a call. the current time is * saved, and if the phone number and user name are given, they are * saved also. * * number -- character string which is phone number called */ d_cstart(number, linetype) register char *number; register char *linetype; { /* get the time and save the number if given */ time(&d_cstime); if (number) (void) strcpy(d_cnumber, number); if (linetype) (void) strcpy(d_linetype, linetype); return(D_OK); } /**/ /* * D_CLOG * * routine which logs calls. we have to be careful here that * sensitive numbers are protected, but still allow us to see all * long distance calls. the strategy that's used is to write X's * over the last 4 digits of number which have more than 7 digits. * * code -- a character that is written in the file to indicate the * status of call completion * */ d_clog(code) int code; { FILE *clogfptr; long duration; long stoptime; int seconds; register int hours, minutes; /* check start time--if zero, this is an extraneous call to d_clog */ if (d_cstime == 0L) return; /* open the call log file */ if ((clogfptr = fopen(d_calllog, "a")) == NULL) { d_errno = D_CALLLOG; d_log("d_clog", "can't open call log file"); return; } /* get the stop time and calculate the duration of the session */ time(&stoptime); duration = stoptime - d_cstime; /*NOSTRICT*/ d_cstime = 0L; /*NOSTRICT*/ hours = (int) (duration / 3600); /*NOSTRICT*/ minutes = (int) ((duration - (hours * 3600)) / 60l); /*NOSTRICT*/ seconds = (int) (duration - (hours * 3600) - (minutes * 60)); fprintf(clogfptr, "%-8s %c ", d_uname, code); fprintf(clogfptr, "%-16.16s ", d_cnumber); /* now the time data. considerable craziness to make a pretty duration */ fprintf(clogfptr, "%-19.19s ", ctime(&stoptime)); if (hours > 0) fprintf(clogfptr, "%2d:", hours); else fprintf(clogfptr, " "); if (minutes > 0) fprintf(clogfptr, "%2d:", minutes); else if (hours > 0) fprintf(clogfptr, "00:", minutes); else fprintf(clogfptr, " 0:", minutes); if (seconds > 9) fprintf(clogfptr, "%2d %s\n", seconds, d_linetype); else fprintf(clogfptr, "0%1d %s\n", seconds, d_linetype); fclose(clogfptr); } ); if (linetype) (void) strcpy(d_linetype, linetype); return(D_OK); } mmdf/lib/dial/d_checksum.c 444 0 12 3605 3620510404 10677 # include "d_returns.h" /* * D_CSCOMP * * routine which computes the checksum for a packet and loads it into * the first 4 bytes as hex digits * * text -- pointer to packet to be check summed. this packet should * be complete except for the checksum field. the bytes to * be included in the sum should start at text + 4; * * length -- total length of the packet. this count should include the * 4 checksum digits but not the line terminator. */ d_cscomp(text, length) char *text; int length; { register char *cp; register int sum, value; int digit; /* add up the bytes */ cp = &text[4]; sum = 0; while (length > 4) { sum += *cp++; length--; } /* build and load the checksum */ cp = &text[3]; for (digit = 0; digit <= 3; digit++) { value = sum & 017; sum >>= 4; *cp-- = d_tohex(value); } return(D_OK); } /* * D_CSCHECK * * this routine checks that a packet has the correct checksum. D_OK is * returned if it's alright, otherwise D_NO. * * text -- pointer to the packet, including the checksum bytes * * length -- number of bytes in the packet, excluding the line terminator */ d_cscheck(text, length) char *text; int length; { register char *cp; register int sum, cnum; int digit, chksum, value; /* sum up the bytes */ cp = &text[4]; sum = 0; for (cnum = 4; cnum < length; cnum++) sum += *cp++; /* convert the checksum and see if ti matches what we've just computed */ cp = text; chksum = 0; for (digit = 0; digit <= 3; digit++) { if ((value = d_fromhex(*cp++)) < 0) return(D_NO); chksum = (chksum << 4) | value; } if (chksum == sum) return(D_OK); return(D_NO); } d calculate the duration of the session */ time(&stoptime); duration = stoptime - d_cstime; /*NOSTRICT*/ mmdf/lib/dial/Makefile.real 444 0 12 16037 3664425155 11053 # # Makefile for dial package portion of libmmdf.a # # Just the prefix of the name should be given (i.e. "foo" for "foo.c") MODULES = d_master d_slave d_path d_quit d_escape \ d_sndstream d_rcvdata d_data d_init d_pglobals \ d_script d_connect d_pstty d_numbers d_utils \ d_clog d_script2 d_dial2 d_parse d_packet \ d_port d_checksum d_coding d_dglobals \ d_tai d_tscript d_log d_io d_setbuff \ d_control d_assign d_lock d_getnbuff mktran test # # The following are put into libmmdf.a # OBJECTS = d_master.o d_slave.o d_path.o d_quit.o d_escape.o \ d_sndstream.o d_rcvdata.o d_data.o d_init.o d_pglobals.o \ d_script.o d_connect.o d_pstty.o d_numbers.o d_utils.o \ d_clog.o d_script2.o d_dial2.o d_parse.o d_packet.o \ d_port.o d_checksum.o d_coding.o d_dglobals.o \ d_tai.o d_tscript.o d_log.o d_io.o d_setbuff.o \ d_control.o d_assign.o d_lock.o d_getnbuff.o conf_dial.o # # The following are linted # COBJECTS = d_master.c d_slave.c d_path.c d_quit.c d_escape.c \ d_sndstream.c d_rcvdata.c d_data.c d_init.c d_pglobals.c \ d_script.c d_connect.c d_pstty.c d_numbers.c d_utils.c \ d_clog.c d_script2.c d_dial2.c d_parse.c d_packet.c \ d_port.c d_checksum.c d_coding.c d_dglobals.c \ d_tai.c d_tscript.c d_log.c d_io.c d_setbuff.c \ d_control.c d_assign.c d_lock.c d_getnbuff.c real-default: ../dial-made ../dial-made: $(OBJECTS) $(AR) r ../libmmdf.a $(OBJECTS) -touch ../dial-made -@echo "dial package routines built normally" # # Special case dependencies # conf_dial.o: ../../conf/$(HOST)/conf_dial.c ../../h/util.h \ ../../h/d_proto.h ../../h/d_structs.h $(CC) -c $(CFLAGS) ../../conf/$(HOST)/conf_dial.c mktran: mktran.o $(MMDFLIBS) $(CC) -o mktran $(LDFLAGS) mktran.o $(MMDFLIBS) $(SYSLIBS) test: test.o $(MMDFLIBS) $(CC) -o test $(LDFLAGS) test.o $(MMDFLIBS) $(SYSLIBS) lint: $(LINT) -Cdial $(LFLAGS) $(COBJECTS) ../../conf/$(HOST)/conf_dial.c clean: -rm -f mktran test *.o x.c makedep eddep make.out errs llib-ldial* # DO NOT DELETE THIS LINE -- make depend uses it d_master.o: d_master.c d_master.o: ../../h/util.h d_master.o: ../../h/d_proto.h d_master.o: ../../h/d_returns.h d_master.o: /usr/include/signal.h d_master.o: ../../h/d_syscodes.h d_master.o: ../../h/d_clogcodes.h d_master.o: ../../h/ll_log.h d_slave.o: d_slave.c d_slave.o: ../../h/util.h d_slave.o: ../../h/ll_log.h d_slave.o: ../../h/d_proto.h d_slave.o: ../../h/d_returns.h d_slave.o: ../../h/d_structs.h d_path.o: d_path.c d_path.o: ../../h/d_proto.h d_path.o: ../../h/d_returns.h d_path.o: /usr/include/signal.h d_path.o: ../../h/d_syscodes.h d_quit.o: d_quit.c d_quit.o: ../../h/d_proto.h d_quit.o: ../../h/d_returns.h d_escape.o: d_escape.c d_escape.o: ../../h/d_proto.h d_escape.o: /usr/include/signal.h d_escape.o: ../../h/d_syscodes.h d_escape.o: ../../h/d_returns.h d_sndstream.o: d_sndstream.c d_sndstream.o: ../../h/d_returns.h d_rcvdata.o: d_rcvdata.c d_rcvdata.o: ../../h/d_returns.h d_data.o: d_data.c d_data.o: ../../h/d_proto.h d_data.o: ../../h/d_returns.h d_data.o: /usr/include/signal.h d_data.o: ../../h/d_syscodes.h d_init.o: d_init.c d_init.o: ../../h/d_returns.h d_pglobals.o: d_pglobals.c d_pglobals.o: ../../h/d_proto.h d_script.o: d_script.c d_script.o: ../../h/util.h d_script.o: /usr/include/signal.h d_script.o: ../../h/d_syscodes.h d_script.o: ../../h/d_proto.h d_script.o: ../../h/d_returns.h d_script.o: /usr/include/stdio.h d_script.o: ../../h/d_structs.h d_script.o: ../../h/d_script.h d_connect.o: d_connect.c d_connect.o: ../../h/util.h d_connect.o: /usr/include/signal.h d_connect.o: ../../h/d_syscodes.h d_connect.o: ../../h/d_clogcodes.h d_connect.o: ../../h/d_proto.h d_connect.o: ../../h/d_returns.h d_connect.o: ../../h/d_structs.h d_connect.o: /usr/include/sgtty.h d_connect.o: /usr/include/sys/stat.h d_connect.o: /usr/include/sys/file.h d_pstty.o: d_pstty.c d_pstty.o: ../../h/util.h d_pstty.o: /usr/include/sgtty.h d_pstty.o: /usr/include/sys/stat.h d_pstty.o: ../../h/d_proto.h d_pstty.o: ../../h/d_returns.h d_pstty.o: ../../h/d_structs.h d_numbers.o: d_numbers.c d_numbers.o: ../../h/util.h d_numbers.o: ../../h/d_returns.h d_numbers.o: ../../h/d_proto.h d_numbers.o: /usr/include/stdio.h d_numbers.o: ../../h/d_structs.h d_utils.o: d_utils.c d_utils.o: ../../h/util.h d_utils.o: ../../h/d_returns.h d_utils.o: /usr/include/pwd.h d_utils.o: /usr/include/stdio.h d_utils.o: ../../h/d_proto.h d_utils.o: ../../h/d_structs.h d_clog.o: d_clog.c d_clog.o: ../../h/util.h d_clog.o: ../../h/d_clogcodes.h d_clog.o: ../../h/d_returns.h d_clog.o: /usr/include/stdio.h d_clog.o: ../../h/d_proto.h d_clog.o: ../../h/d_structs.h d_script2.o: d_script2.c d_script2.o: /usr/include/stdio.h d_script2.o: ../../h/d_proto.h d_script2.o: ../../h/d_returns.h d_script2.o: ../../h/d_structs.h d_dial2.o: d_dial2.c d_dial2.o: /usr/include/stdio.h d_dial2.o: ../../h/d_returns.h d_parse.o: d_parse.c d_parse.o: ../../h/d_returns.h d_packet.o: d_packet.c d_packet.o: ../../h/d_proto.h d_packet.o: ../../h/d_returns.h d_packet.o: /usr/include/signal.h d_packet.o: /usr/include/stdio.h d_packet.o: ../../h/d_structs.h d_packet.o: ../../h/d_syscodes.h d_port.o: d_port.c d_port.o: ../../h/util.h d_port.o: ../../h/d_proto.h d_port.o: ../../h/d_returns.h d_port.o: /usr/include/signal.h d_port.o: ../../h/d_syscodes.h d_port.o: /usr/include/stdio.h d_port.o: ../../h/d_structs.h d_port.o: /usr/include/sys/ioctl.h d_port.o: /usr/include/sgtty.h d_checksum.o: d_checksum.c d_checksum.o: ../../h/d_returns.h d_coding.o: d_coding.c d_coding.o: ../../h/util.h d_coding.o: ../../h/d_proto.h d_coding.o: ../../h/d_returns.h d_dglobals.o: d_dglobals.c d_dglobals.o: ../../h/util.h d_dglobals.o: /usr/include/sgtty.h d_dglobals.o: ../../h/d_proto.h d_dglobals.o: /usr/include/stdio.h d_dglobals.o: ../../h/d_structs.h d_tai.o: d_tai.c d_tai.o: ../../h/util.h d_tai.o: ../../h/ll_log.h d_tai.o: ../../h/d_proto.h d_tai.o: ../../h/d_structs.h d_tai.o: ../../h/cmd.h d_tscript.o: d_tscript.c d_tscript.o: /usr/include/stdio.h d_tscript.o: ../../h/d_returns.h d_log.o: d_log.c d_log.o: ../../h/util.h d_log.o: ../../h/d_returns.h d_log.o: /usr/include/stdio.h d_log.o: ../../h/d_proto.h d_log.o: ../../h/d_structs.h d_log.o: ../../h/ll_log.h d_io.o: d_io.c d_io.o: /usr/include/stdio.h d_setbuff.o: d_setbuff.c d_setbuff.o: ../../h/d_proto.h d_setbuff.o: ../../h/d_returns.h d_setbuff.o: /usr/include/stdio.h d_setbuff.o: ../../h/d_structs.h d_control.o: d_control.c d_control.o: ../../h/d_proto.h d_control.o: ../../h/d_returns.h d_assign.o: d_assign.c d_assign.o: ../../h/d_returns.h d_lock.o: d_lock.c d_lock.o: ../../h/util.h d_lock.o: /usr/include/sys/stat.h d_lock.o: /usr/include/sys/file.h d_lock.o: ../../h/d_returns.h d_getnbuff.o: d_getnbuff.c d_getnbuff.o: ../../h/d_proto.h d_getnbuff.o: /usr/include/signal.h d_getnbuff.o: ../../h/d_syscodes.h d_getnbuff.o: ../../h/d_returns.h mktran.o: mktran.c mktran.o: /usr/include/stdio.h mktran.o: ../../h/d_returns.h mktran.o: ../../h/ll_log.h mktran.o: ../../h/d_proto.h mktran.o: ../../h/d_script.h test.o: test.c test.o: /usr/include/stdio.h test.o: ../../h/ll_log.h # DEPENDENCIES MUST END AT END OF FILE # IF YOU PUT STUFF HERE IT WILL GO AWAY # see make depend above t.o: d_init.c d_init.o: ../../h/d_returns.h d_pglobals.o: d_pglobals.c d_pglobals.o: ../../h/d_proto.h d_script.o: d_script.c d_script.o: ../../h/util.h d_script.o: /usr/include/signal.h d_script.o: ../../h/d_syscodes.h d_script.o: ../../h/d_proto.h d_script.o: ../../h/d_returns.h d_script.o: /usr/include/stdio.h d_script.o: ../../h/d_structs.h d_script.o: ../../h/d_script.h d_connect.o: d_connect.c d_connect.o: ../../h/util.h d_connect.o: /usr/include/signal.h d_connect.o: ..mmdf/lib/dial/d_dial2.c 444 0 12 7133 3620510404 10070 # include <stdio.h> # include "d_returns.h" /* * D_CHKACCESS * * this routine checks for the user name given as the argument string * in the access file given as the second argument. * * username -- the user's name that we're looking for * * accessfile -- path name of the access file. this file has one user name * per line */ d_chkaccess(username, accessfile) char *username, *accessfile; { extern int d_errno; #ifdef D_LOG register int linenum = 0; #endif D_LOG register int length; char *fields[1], linebuf[128]; FILE *accfp; #ifdef D_DBGLOG d_dbglog("d_chkaccess", "user %s accessfile %s", username, accessfile); #endif D_DBGLOG /* open the access list file. if we can't, just return quietly */ if ((accfp = fopen(accessfile, "r")) == NULL) return(0); while (fgets(linebuf, sizeof linebuf - 1, accfp) != NULL) { #ifdef D_LOG linenum++; #endif D_LOG /* make sure we got the whole line */ length = strlen(linebuf); if (linebuf[length - 1] != '\n') { #ifdef D_LOG d_log("d_chkaccess", "access file '%s', line %d too long", accessfile, linenum); #endif D_LOG d_errno = D_ACCERR; return(D_FATAL); } /* parse the line */ switch (d_linparse(fields, linebuf, 1)) { case 0: continue; case 1: if (strcmp(username, fields[0]) == 0) { fclose(accfp); return(D_OK); } continue; case -1: #ifdef D_LOG d_log("d_chkaccess", "access file '%s', line %d: too many fields", accessfile, linenum); #endif D_LOG d_errno = D_ACCERR; return(D_FATAL); case -2: #ifdef D_LOG d_log("d_chkaccess", "access file '%s', line %d: qupted string too long", accessfile, linenum); #endif D_LOG d_errno = D_ACCERR; return(D_FATAL); default: #ifdef D_LOG d_log("d_chkaccess", "access file '%s', line %d: unknown error", accessfile, linenum); #endif D_LOG d_errno = D_ACCERR; return(D_FATAL); } } #ifdef D_DBGLOG d_dbglog("d_chkaccess", "name not found"); #endif D_DBGLOG fclose(accfp); return(D_NO); } /* * D_TYPELIST * * this routine returns a list of acceptable dial port types based on the * given phone number. * * Currently, the type is gotten out of the phone number and the * phone number is munged to exclude the type field. * * numptr -- pointer to the phone number being called * */ char ** d_typelist(numptr) char **numptr; { extern char *strdup (); char tempbuf[25]; char *tpt, *newnum, *number; static char *typelist[] = { "LOCAL", 0 }; tpt = tempbuf; number = *numptr; newnum = number; #ifdef D_DBGLOG d_dbglog("d_typelist", "number %s", *numptr); #endif D_DBGLOG if (d_instring('<', number) != 0) { while (*number) if (*number != '<') *newnum++ = *number++; else { /* pick off type */ while (*++number != '>') *tpt++ = *number; number++; } *newnum = '\0'; } *tpt = '\0'; #ifdef D_DBGLOG d_dbglog("d_typelist", "new number %s", *numptr); #endif D_DBGLOG if (tempbuf[0] != '\0') typelist[0] = strdup(tempbuf); #ifdef D_DBGLOG d_dbglog("d_typelist", "returning type %s", typelist[0]); #endif D_DBGLOG return(typelist); } ect.o: ../../h/d_returns.h d_connect.o: ../../h/d_structs.h d_connect.o: /usr/include/sgtty.h d_connect.o: /usr/include/sys/stat.h d_connect.o: /usr/include/sys/file.h d_pstty.o: d_pstty.c d_pstty.o: ../../h/util.h d_pstty.o: /usr/include/sgtty.h d_pstty.o: /usr/include/sys/stat.h d_pstty.o: ../../h/d_proto.h d_pstty.o: ../../h/d_returns.h d_pstty.o: ../../h/d_structs.h d_numbers.o: d_numbers.c d_numbers.o: ../../h/utmmdf/lib/dial/test.c 444 0 12 1661 3653210456 7563 # include <stdio.h> # include "ll_log.h" char scfile[128]; struct ll_struct log = { "Log", "mktran log", '\0177', 100, 0, 0, 0, }; main(argc, argv) int argc; char **argv; { register int result, word; extern int d_debug; extern int d_master, d_snseq, d_rcvseq; mmdf_init(); siginit(); if (argc > 1) log.ll_level = (char)(atoi (argv[1])); if ((result = d_opnlog(&log)) < 0) { printf("opnlog returns %d\n", result); exit(-1); } d_init(); if ((result = d_tsopen("Tranfile")) < 0) { printf("d_tsopen returns %d\n", result); exit(-1); } d_master = 1; d_snseq = 3; d_rcvseq = 3; if ((result = d_scopen("Script", 0, (char **) 0)) < 0) { printf("d_scopen returns %d\n", result); exit(-1); } result = d_script(); printf("d_script returns %d\n", result); /* drop the connection */ d_drop (); exit(0); } um++; #endif D_LOG /* make sure we got the whole line */ length = strlen(linmmdf/lib/dial/d_numbers.c 444 0 12 14570 3671073172 10607 # include "util.h" # include "d_returns.h" # include "d_proto.h" # include <stdio.h> # include "d_structs.h" /* * D_NUMPARSE * * this routine is called to parse a phone number specification string * of the form: * * speed|phone number, speed|phone number, ... * * such as: * * 300|9:738-8003,1200|9:738-2928 * * and fill a table of structures with the appropriate information. * the elements of each table entry contain the speed index corresponding * to the speed designation (300, 1200, etc.) and the phone number is * converted from the external form into that required by the acu driver. * * string - pointer to specification string as above * * numtab - pointer to array of structures for the speed index, phone * number pairs * * filename - name of the file from which the specification string came. * this name is reported when errors are encountered to aid * in debugging. * * lineno - number of the line in the above file which contains the * string * * * Mar 84 D. Long added support for "<type>" in phone numbers */ d_numparse(string, numtab, maxnums, filename, lineno) char *string, *filename; struct telspeed *numtab; int maxnums; unsigned lineno; { register char *in, *ap, *cp; int count; char accum[30], tempbuf[50]; int index, num, linetype; in = string; num = 0; #ifdef D_DBGLOG d_dbglog("d_numparse", "parsing '%s'", string); #endif D_DBGLOG /* treat each speed/phone number pair separately */ while (1) { /* isolate a number and copy it into a temporary buffer */ if (*in == ',') in++; if (*in == '\0') return(num); if (num >= maxnums) { d_fillog(filename, lineno, "d_numparse", "more than %d phone numbers", maxnums); return(D_FATAL); } cp = tempbuf; count = 0; while (*in && (*in != ',')) if (++count > 50) { d_fillog(filename, lineno, "d_numparse", "phone number too long"); return(D_FATAL); } else *cp++ = *in++; *cp = '\0'; cp = tempbuf; /* if the string begins with a letter, then it is taken to be the name of */ /* a direct connect line. the speed index will be set to 0. */ if (isalpha(tempbuf[0])) { if (strlen(tempbuf) > 25) { d_fillog(filename, lineno, "d_numparse", "direct line name too long"); return(D_FATAL); } (void) strcpy(numtab[num].t_number, tempbuf); numtab[num].t_speed = 0; #ifdef D_DBGLOG d_dbglog ("d_numparse", "%d: (speed %d) num '%s'", num, numtab[num].t_speed, numtab[num].t_number); #endif D_DBGLOG num++; continue; } /* if the string to be parsed contains a '|', then it is supposed to */ /* have a preceeding speed indicator. get at most 4 digits for this */ if (d_instring('|', tempbuf)) { ap = accum; count = 0; while ((*cp != '|') && (++count <= 4)) if (isdigit(*cp)) *ap++ = *cp++; else { d_fillog(filename, lineno, "d_numparse", "bad character in speed"); return(D_FATAL); } if (count > 4) { d_fillog(filename, lineno, "d_numparse", "invalid speed"); return(D_FATAL); } /* now look up the string to get the index */ *ap = '\0'; if ((index = d_spdindex(accum)) < 0) { d_fillog(filename, lineno, "d_numparse", "unknown speed"); return(D_FATAL); } numtab[num].t_speed = index; cp++; } /* if there's no speed given, assume a default of 300 baud */ else numtab[num].t_speed = 7; /* accumulate up to 25 characters of phone number */ ap = numtab[num].t_number; count = 0; linetype = 0; while (*cp && (*cp != ',') && (count < 25)) { if (isdigit(*cp) || (linetype && (*cp != '>')) ) { *ap++ = *cp++; count++; continue; } switch (*cp) { case '(': case ')': case '-': cp++; continue; case ':': *ap++ = '='; /* wait for next dial tone */ count++; cp++; continue; case '<': *ap++ = *cp++; count++; linetype = 1; continue; case '>': *ap++ = *cp++; count++; linetype = 0; continue; default: /* may be wrong, but let it pass */ d_fillog(filename, lineno, "d_numparse", "illegal dial character '%c' (%3o'O) in phone number?", *cp, *cp); count++; cp++; continue; } } /* make sure the number isn't too long */ if (count >= 25) { d_fillog(filename, lineno, "d_numparse", "phone number too long"); return(D_FATAL); } /* make sure the <> were balanced */ if (linetype) { d_fillog(filename, lineno, "d_numparse", "Line type field invalid"); return(D_FATAL); } *ap = '\0'; #ifdef D_DBGLOG d_dbglog ("d_numparse", "%d: (speed %d) num '%s'", num, numtab[num].t_speed, numtab[num].t_number); #endif D_DBGLOG num++; } } /* * D_SPDINDEX * * this routine looks up the speed designation string in the table * of legal identifiers and returns the speed index. * * speed -- pointer to the speed string */ d_spdindex(speed) register char *speed; { extern struct speedtab d_spdtab[]; extern int d_errno; register struct speedtab *sp; sp = d_spdtab; while (sp -> s_speed) { if (strcmp(sp -> s_speed, speed) == 0) return(sp -> s_index); sp++; } d_errno = D_BADSPD; return(D_FATAL); } /* * D_INSTRING * * routine which returns a non-zero value if the specified character * is in the string. * * c -- the character to be searched for * * string -- the string to search */ d_instring(c, string) char c; register char *string; { while (*string) if (c == *string++) return(1); return(0); } ) { if (strlen(tempbuf) > 25) { d_fillog(filename, lineno, "d_numparse", "direct line name too long"mmdf/lib/dial/d_lock.c 444 0 12 5616 3671073173 10046 #include "util.h" #include <sys/stat.h> #ifdef V4_2BSD #include <sys/file.h> #endif V4_2BSD #include "d_returns.h" extern int d_errno; /* * D_LOCK * * routine which attempts to open a file exclusively, and thereby lock * the associated port or direct line. * * Feb 84 D. Long modified to stat the lock rather than d_verifying it * * Mar 84 D. Rockwell & D. Long -- use flock instead of UUCP convention * D. Rockwell & D. Long -- fixed bug with d_lckfd in d_lock. * * Jul 85 D. Rockwell & D. Long -- add UUCP-compatible locking (yuck) * This assumes that the phone line is being locked with * "/usr/spool/uucp/LCK..tty*" * * lockfile -- lock file name */ d_lock (lockfile) register char *lockfile; { extern int d_lckfd; #ifdef UUCPLOCK char lockername[1024]; int fd; int pid; d_lckfd = -1; pid = getpid(); sprintf(lockername, "/usr/spool/uucp/MLTMP.%d", pid); (void) unlink(lockername); if ((fd = creat(lockername, 0644)) < 0) { #ifdef D_LOG d_log ("d_lock", "can't create locker file '%s'", lockername); #endif D_LOG (void) unlink(lockername); /* never needed? */ d_errno = D_LOCKERR; return(D_FATAL); } write(fd, &pid, sizeof pid); (void) close(fd); if (link(lockername, lockfile) < 0) { #define NEED_D_VERIFY 1 if (d_verify(lockfile) >= D_OK) { (void) unlink(lockername); return(D_NO); } (void) unlink(lockfile); if (link(lockername, lockfile) < 0) { #ifdef D_LOG d_log ("d_lock", "can't link lock file '%s'", lockfile); #endif D_LOG (void) unlink(lockername); return(D_NO); } } (void) unlink(lockername); #else UUCPLOCK #ifndef V4_2BSD #define NEED_D_VERIFY 1 if (d_verify (lockfile) >= D_OK) return (D_NO); /* it's really busy */ (void) unlink (lockfile); /* clean it out, if necessary */ if ((d_lckfd = creat (lockfile, 0644)) < 0) { #else V4_2BSD if (d_lckfd < 0 && (d_lckfd = open (lockfile, O_RDONLY|O_CREAT, 0644)) < 0) { #endif V4_2BSD #ifdef D_LOG d_log ("d_lock", "can't create lock file '%s'", lockfile); #endif D_LOG d_errno = D_LOCKERR; return (D_FATAL); } #ifdef V4_2BSD if (flock(d_lckfd, LOCK_EX|LOCK_NB) < 0) { (void) close(d_lckfd); d_lckfd = -1; return (D_NO); } #endif V4_2BSD #endif UUCPLOCK return (D_OK); } #ifdef NEED_D_VERIFY d_verify (port) /* is the port REALLY active? */ register char *port; { struct stat statbuf; time_t curtime; if (stat (port, &statbuf) < 0) return (D_NO); /* really is inaccessable */ time (&curtime); if (curtime > (statbuf.st_atime + (time_t) (15 * 60))) return (D_NO); /* inactive for over 15 minutes */ return (D_OK); } #endif NEED_D_VERIFY d_unlock(lockfile) { if (d_lckfd >= 0) { #ifdef V4_2BSD flock(d_lckfd, LOCK_UN); #endif V4_2BSD (void) close(d_lckfd); d_lckfd = -1; } (void) unlink( lockfile ); } e; } } /* make sure the number isn't too long */ if (count >= 25) { dmmdf/lib/dial/d_pstty.c 444 0 12 12666 3671073174 10325 # include "util.h" #ifdef SYS5 # include <termio.h> #else # include <sgtty.h> #endif SYS5 # include <sys/stat.h> # include "d_proto.h" # include "d_returns.h" # include "d_structs.h" /* * Routines for controlling the tty settings on the control line * * Jun 81 D. Crocker Rewritten to work on v6 & v7 * Jul 81 D. Crocker Make explicit TIOCHPCL call * Dec 82 Doug Kinston Chmods no longer control return values (DPK@BRL) * Jan 84 D. Rockwell Make d_ttrestore heed d_savalid * Save d_savfd instead of FILE * */ extern int errno; LOCVAR int d_savfd; /* handle on the opened tty */ LOCVAR char d_savfname[25]; /* name of tty */ #ifdef SYS5 LOCVAR struct termio d_savtty #else LOCVAR struct sgttyb d_savtty; /* where to stash original setting */ #endif SYS5 LOCVAR int d_savmode; /* protections on tty */ LOCVAR int d_savalid; d_ttsave (fp, fname) /* save the current setting */ FILE *fp; char fname[]; { struct stat statbuf; register int retval; d_savfd = fileno(fp); if (fname != 0) (void) strcpy (d_savfname, fname); else d_savfname[0] = '\0'; #ifdef D_DBGLOG d_dbglog("d_ttsave", "saving terminal modes, fd = %d", d_savfd); #endif D_DBGLOG #ifdef SYS5 if ((retval = ioctl (d_savfd, TCGETA, &d_savtty)) >= 0) #else if ((retval = ioctl (d_savfd, TIOCGETP, &d_savtty)) >= 0) #endif SYS5 { if ((retval = fstat (d_savfd, &statbuf)) >= 0) { d_savalid++; d_savmode = statbuf.st_mode & ~S_IFMT; } } return (retval); } d_ttrestore () /* restore initial setting */ { int retval; #ifdef D_DBGLOG d_dbglog ("d_ttrestore", "restoring terminal characteristics, fd = %d", d_savfd); #endif D_DBGLOG if (d_savalid == 0) { #ifdef D_DBGLOG d_dbglog("d_ttrestore", "didn't have to"); #endif D_DBGLOG return(0); } #ifdef SYS5 retval = ioctl (d_savfd, TCSETAW, &d_savtty); #else retval = ioctl (d_savfd, TIOCSETP, &d_savtty); #endif if (!isnull (d_savfname[0])) chmod( d_savfname, d_savmode ); d_savalid = 0; return (retval); } d_ttscript (baudrate) /* tty will be following call script */ int baudrate; { #ifdef SYS5 struct termio sttybuf; extern unsigned short d_scbitc, d_scbiti, d_scbito, d_scbitl; #else struct sgttyb sttybuf; extern int d_scon, d_scoff; #endif SYS5 if (!isnull (d_savfname[0])) chmod( d_savfname, 0600 ); #ifdef SYS5 ioctl(d_savfd, TCGETA, &sttybuf); #endif SYS5 if (baudrate != 0) { /* use special speed */ #ifdef SYS5 sttybuf.c_cflag = (baudrate & CBAUD); #else sttybuf.sg_ispeed = baudrate; sttybuf.sg_ospeed = baudrate; #endif SYS5 } else { /* use current speed */ #ifdef SYS5 ssybuf.c_cflag = (d_savtty.c_cflag & CBAUD); #else sttybuf.sg_ispeed = d_savtty.sg_ispeed; sttybuf.sg_ospeed = d_savtty.sg_ospeed; #endif SYS5 } #ifdef SYS5 sttybuf.c_cc[VERASE] = '\010'; /* erase control-h */ sttybuf.c_cc[VKILL] = '\030'; /* kill control-x */ sttybuf.c_cc[VTIME] = 0; /* delay */ sttybuf.c_cc[VMIN] = 1; /* hi water mark */ sttybuf.c_cflag |= d_scbitc; sttybuf.c_iflag = d_scbiti; sttybuf.c_oflag = d_scbito; sttybuf.c_lflag = d_scbitl; #else sttybuf.sg_erase = '\010'; /* erase = control-H */ sttybuf.sg_kill = '\030'; /* kill = control-X */ sttybuf.sg_flags |= d_scon; /* force on special bits for script */ sttybuf.sg_flags &= ~((int) d_scoff); /* force off special bits for script */ #endif SYS5 #ifdef SYS5 return (ioctl (d_savdf, TCSETAW, &sttybuf)); #else return (ioctl (d_savfd, TIOCSETP, &sttybuf)); #endif SYS5 } d_ttproto (baudrate) int baudrate; { #ifdef SYS5 struct termio sttybuf; extern unsigned short d_prbitc, d_prbiti, d_prbito, d_prbitl; #else struct sgttyb sttybuf; extern int d_pron, d_proff; #endif SYS5 if (!isnull (d_savfname[0])) chmod( d_savfname, 0600 ); #ifdef SYS5 ioctl (d_savfd, TCGETA, &sttybuf); #endif if (baudrate != 0) { /* use special speed */ #ifdef SYS5 sttybuf.c_cflag = (baudrate & CBAUD); #else sttybuf.sg_ispeed = baudrate; sttybuf.sg_ospeed = baudrate; #endif SYS5 } else { /* use current speed */ #ifdef SYS5 sttybuf.c_cflags = (d_savtty.c_cflags & CBAUD); #else sttybuf.sg_ispeed = d_savtty.sg_ispeed; sttybuf.sg_ospeed = d_savtty.sg_ospeed; #endif SYS5 } #ifdef SYS5 sttybuf.c_cc[VERASE] = '\010'; /* erase control-h */ sttybuf.c_cc[VKILL] = '\030'; /* kill control-x */ sttybuf.c_cc[VEOL] = '\n'; sttybuf.c_cc[VEOF] = '\004'; sttybuf.c_cflag |= d_prbitc; sttybuf.c_iflag = d_prbiti; sttybuf.c_oflag = d_prbito; sttybuf.c_lflag = d_prbitl; #else sttybuf.sg_erase = '\010'; /* erase = control-H */ sttybuf.sg_kill = '\030'; /* kill = control-X */ sttybuf.sg_flags |= d_pron; /* force on special bits for protocol*/ sttybuf.sg_flags &= ~((int) d_proff); /* force off special bits for protocol*/ #endif SYS5 #ifdef SYS5 return( ioctl (d_savfd, TCSETAW, &sttybuf) ); #else return( ioctl (d_savfd, TIOCSETP, &sttybuf) ); #endif SYS5 } rn(0); } #ifdef SYS5 retval = ioctl (d_savfd, TCSETAW, &d_savtty);mmdf/lib/dial/d_packet.c 444 0 12 40103 3644552231 10370 # # include "d_proto.h" # include "d_returns.h" # include <signal.h> # include <stdio.h> # include "d_structs.h" # include "d_syscodes.h" extern int d_master; extern int d_errno; extern int d_xretry; extern int d_nbuff; extern int d_toack; extern int d_rcvseq; extern int d_todata; static struct pkbuff packst[NBUFFMAX]; static int first; /* index of first packet in buff */ static int nout; /* number of packets in buff */ /* Jun 81 D. Crocker Major rework of alarm usage * Converted some d_dbglogs to d_log * fix sndpacket() timeout return value * Jul 81 D. Crocker fix ack timeout to cause retransmit * Dec 81 D. Crocker add retry hack to separate packets * Feb 84 D. Long changed separating string to nn instead of nrnr */ /* * D_BLDPACK * * routine which builds a packet for transmission. most of the * understanding of the packet format is in this function. * * type -- packet type code * * seqnum -- packet sequence number * * eof -- end of file indicator. when non-zero, it causes the eof bit * to be set in the outgoing packet * * text -- if non-zero, then this is taken to be a pointer to the text to * be included in the packet * * buffer -- place to load the complete packet */ d_bldpack (type, seqnum, eof, text, buffer) char type, *buffer; register char *text; int seqnum, eof; { register char *cp; register int flags; int length; #ifdef D_DBGLOG d_dbglog ("d_bldpack", "building packet - type '%c', seqnum %d, eof %d", type, seqnum, eof); #endif D_DBGLOG buffer[TYPEOFF] = type; /* load the flag byte */ flags = seqnum & 03; if (eof) flags |= EOFBIT; if (d_master) flags |= MASTERBIT; buffer[FLAGOFF] = d_tohex (flags); /* load the text, if any */ cp = &buffer[TEXTOFF]; length = 6; if (text) while (*text) { *cp++ = *text++; length++; } /* tack on the checksum and the line terminator */ d_cscomp (buffer, length); *cp++ = '\r'; *cp++ = '\n'; /* although the '\0' is not used by this package, it makes * printing easier. */ *cp = '\0'; length += 2; return (length); } /* * * D_SNPKT * * this routine sends a packet and waits for a given type response * packet from the other side. retransmissions are done here. * * type -- type of packet being sent; used to determine reply type. * * packet -- pointer to fully formed packet * * length -- length of the packet * */ d_snpkt (type, packet, length) char type; char *packet; int length; { register int result; int next; #ifdef D_DBGLOG d_dbglog ("d_snpkt", "sending packet - code '%c'", packet[TYPEOFF]); #endif D_DBGLOG /* * Should we wait for an outstanding packet before sending this one? */ if (type != DATAACK && nout == d_nbuff) { result = d_confirm (&packst[first]); /* * We always want to pull this msg off the front of * the queue, even if the 'confirm' failed. Ours is * not to judge whether this is bad or not, but merely * to indicate to the calling routine what happened. * If it doesn't care that the packet wasn't ACKed, * then we don't want to screw things up by leaving * the packet in the queue. */ first = (first + 1) % NBUFFMAX; nout--; switch (result) { case D_OK: break; case D_FATAL: /* * Couldn't get an ACK on the last packet. * Return an error. */ return (D_FATAL); default: #ifdef D_LOG d_log ("d_snpkt", "Unknown return (%d) from d_confirm", result); #endif D_LOG return (D_FATAL); } } /* * We now have a space for this packet, store it. */ next = (first + nout) % NBUFFMAX; d_packset(type, packet, length, &packst[next]); /* * Send out the packet for the first time. */ result = d_transmit(&packst[next]); /* * If this was an ACK packet, or if the write to the port failed, * then there is no need to wait around for an ACK. Note that in * this case the packet was essentially never queued in the buffer * because we never incremented 'nout'. */ if ((result == D_OK) || (result < 0)) return (result); /* Update the information about outstanding packets */ nout++; /* * In some cases the packet just sent must be ACKed immediately. * These cases are: 1) if buffering is not allowed at all, * 2) If this is an end-of-logical-record packet, or 3) if this * is not a data packet (which implies end of record). * Of course, if we are going to require that the last packet * be ACKed, we had better get ACK's for all the ones preceding * it. * * Performance improvement note: the above is only strictly true * for QUIT packets. We can wait for the next transmitted * packet to get the previous ACK in all other cases. -- DR */ if (((d_fromhex (packst[next].p_data[FLAGOFF]) & EOFBIT) != 0) || (packst[next].p_data[TYPEOFF] != DATA)) { /* * Get an immediate ACK for all outstanding packets */ while (nout > 0) { #ifdef D_DBGLOG d_dbglog ("d_snpkt", "nout = %d, pack index = %d", nout, first); #endif D_DBGLOG result = d_confirm (&packst[first]); /* See note above for explanation of next two lines */ first = (first + 1) % NBUFFMAX; nout--; switch (result) { case D_OK: break; case D_FATAL: return (D_FATAL); default: #ifdef D_LOG d_log ("d_snpkt", "Unknown return (%d) from d_transmit", result); #endif D_LOG return (D_FATAL); } } return (D_OK); } /* If we get here, then this packet has not yet been ACKed, but * we are going to fudge and return D_OK, planning all the while * to get the ACK later. If this packet is never ACKed, then its * error will be returned on a later packet. */ return (D_OK); } /* * * D_TRANSMIT * * this routine does the actual work of transmitting the packet. * It returns a value indicating whether an ACK should be expected. * * packet - a pointer to the packet structure * */ d_transmit(pack) struct pkbuff *pack; { register int result; #ifdef D_DBGLOG d_dbglog ("d_transmit", "transmitting '%s'", pack->p_data); #endif D_DBGLOG if ((result = d_wrtport (pack->p_data, pack->p_length)) < 0) { #ifdef D_LOG d_log ("d_transmit", "on packet [%c](%d)'%s'", pack->p_pktype, pack->p_length, pack->p_data); #endif D_LOG return (result); } /* If this is an ACK packet, then it doesn't get acknowledged */ switch (pack->p_pktype) { case DATAACK: /* acknowledge DATA packet */ case XPATHACK: /* acknowledge XPATH packet */ case RPATHACK: /* acknowledge RPATH packet */ case ESCAPEACK: /* acknowledge ESCAPE packet */ case QUITACK: /* acknowledge QUIT packet */ case NBUFFACK: /* acknowledge NBUFF packet */ return (D_OK); default: return (D_CONTIN); } } /* * * D_PACKSET * * type - the type code for this packet * packet - pointer to the start of the packet itself * length - length of the packet in chars * pack - a structure with data about the packet being sent * * */ d_packset (type, packet, length, pack) char type; char packet[]; int length; struct pkbuff *pack; { register char *cp; register int index; /* transfer appropriate information to the structure */ for (cp = pack->p_data, index = 0; index < length; index++) *cp++ = packet[index]; /* Makes printing easier if needed after an error */ *cp = '\0'; pack->p_length = length; pack->p_pktype = type; pack->p_seqnum = d_fromhex (packet[FLAGOFF]) & 03; switch (type) { case DATA: /* data packet */ /* Remember, if data was sent, an ACK is being waited on */ pack->p_timeo = d_toack; pack->p_acktype = DATAACK; return; case XPATH: /* transmit path packet */ pack->p_timeo = XPAWAIT; pack->p_acktype = XPATHACK; return; case RPATH: /* receive path packet */ pack->p_timeo = RPAWAIT; pack->p_acktype = RPATHACK; return; case ESCAPE: /* escape packet */ pack->p_timeo = EACKWAIT; pack->p_acktype = ESCAPEACK; return; case QUIT: /* quit packet */ pack->p_timeo = QACKWAIT; pack->p_acktype = QUITACK; return; case NBUFF: /* nbuffer packet */ pack->p_timeo = NBACKWAIT; pack->p_acktype = NBUFFACK; return; } } /* * * D_CONFIRM * * this routine looks for the ACK for the given pack. If * it fails to recv it in a reasonable time, it retransmits * the pack. * * packet - a pointer to the struct containing info about the * packet we are trying to get ACKed. * */ d_confirm (packet) struct pkbuff *packet; { register int result, index; register int retries = 0; /* loop, checking for the ACK and trying a retransmit */ #ifdef D_DBGLOG d_dbglog("d_confirm", "Looking for ack of packet [%c](%d)'%s'", packet->p_pktype, packet->p_length, packet->p_data); #endif D_DBGLOG while (retries++ < d_xretry) { /* The packet was sent once before this routine was called. * Therfore, start off looking for the ACK. */ result = d_getack (packet); if (result > 0) return (result); if (result == D_RETRY) { /* * Because the later packets should've been dropped * due to bad sequence numbers, we must retransmit * this packet and its successors. * Send newlines to clear out any leftover noise. */ #ifdef D_DBGLOG d_dbglog("d_confirm", "Retransmitting %d packet[s]", nout); #endif D_DBGLOG #ifdef D_LOG d_log ("d_confirm", "separating"); #endif D_LOG d_wrtport ("\n\n", 2); for(index = 0; index < nout; index++) { if (d_transmit (&packst[(first+index) % NBUFFMAX]) < 0) { #ifdef D_DBGLOG d_dbglog("d_confirm", "Error on retransmission"); #endif D_DBGLOG return (D_FATAL); } } continue; } /* An unknown error */ #ifdef D_DBGLOG d_dbglog ("d_confirm", "Unknown error from 'getack()'"); #endif D_DBGLOG return (result); } #ifdef D_LOG d_log ("d_confirm", "retransmissions (%d) exhausted", retries-1); d_log ("d_confirm", "on packet [%c](%d)'%s'", packet->p_pktype, packet->p_length, packet->p_data); #endif D_LOG d_errno = D_NORESP; return (D_FATAL); } /* * * D_GETACK * * Actually tries to get the ACK. This routine will set up * the time out alarm. It returns when the alarm sounds, or * when the ACK has been received. * * pack - a pointer to the structure representing the packet * */ d_getack (pack) struct pkbuff *pack; { char respbuff[MAXPACKET + 2]; register int recvseq, result; /* read incoming packets and watch for errors. interrupted * reads return, allowing the calling routine to retransmit. * Other errors cause an immediate return. */ d_alarm (pack->p_timeo); #ifdef D_DBGLOG d_dbglog ("d_getack", "Wanted: ack type '%c', seqnum %d", pack->p_acktype, pack->p_seqnum); #endif D_DBGLOG for ( ;; ) { if ((result = d_rdport (respbuff)) < D_OK) { if (result == D_INTRPT) /* The ack timed out */ return (D_RETRY); return (result); } /* check the type and sequence number */ if (respbuff[TYPEOFF] == pack->p_acktype) { recvseq = d_fromhex (respbuff[FLAGOFF]) & 03; if (recvseq == pack->p_seqnum) { #ifdef D_DBGLOG d_dbglog ("d_getack", "response received"); #endif D_DBGLOG return (D_OK); } #ifdef D_LOG else d_log ("d_getack", "bad packet seq num '%s'", respbuff); #endif D_LOG } #ifdef D_LOG else d_log ("d_getack", "bad packet type '%s'", respbuff); #endif D_LOG } } /* * * D_ACKPACK * * this routine is called to acknowledge a received packet. it builds * the ack and sends it. an ack won't be acknowledged. * * type -- the type of the packet to be acknowledged * * seqnum -- the sequence number of the packet * * * return values: * * D_NONFATAL -- returned if the received packet has an unknown type */ d_ackpack (type, seqnum) char type; register int seqnum; { char ackbuff[MAXPACKET + 2]; register int ackleng, result; #ifdef D_DBGLOG d_dbglog ("d_ackpack", "acknowledging packet type '%c', seqnum %d", type, seqnum); #endif D_DBGLOG /* switch on the type. some packet don't get ack's sent */ switch (type) { case DATAACK: case XPATHACK: case RPATHACK: case QUITACK: case ESCAPEACK: case NBUFFACK: return (D_OK); } /* * this code produces a protocol change which acts to alleviate * a protocol deadlock problem in which the peers got out of sync * during the envelope-verification phase. */ /* if this packet is too badly out of sequence, do not ack it */ if (seqnum == d_incseq(d_rcvseq)) { #ifdef D_DBGLOG d_dbglog("d_ackpack", "packet way out of seq--no ack--d_rcvseq %d", d_rcvseq); #endif D_DBGLOG return(D_OK); } /* now, generate the proper ACK */ switch(type) { case DATA: ackleng = d_bldpack ((type = DATAACK), seqnum, 1, (char *) 0, ackbuff); break; case RPATH: ackleng = d_bldpack ((type = RPATHACK), seqnum, 1, (char *) 0, ackbuff); break; case XPATH: ackleng = d_bldpack ((type = XPATHACK), seqnum, 1, (char *) 0, ackbuff); break; case QUIT: ackleng = d_bldpack ((type = QUITACK), seqnum, 1, (char *) 0, ackbuff); break; case ESCAPE: ackleng = d_bldpack ((type = ESCAPEACK), seqnum, 1, (char *) 0, ackbuff); break; case NBUFF: ackleng = d_bldpack ((type = NBUFFACK), seqnum, 1, (char *) 0, ackbuff); break; default: #ifdef D_LOG d_log ("d_ackpack", "unknown packet type '%c'", type); #endif D_LOG return (D_NONFATAL); } /* send that pack that we've built */ result = d_snpkt (type, ackbuff, ackleng); return (result); } /* * * D_WATCH * * this routine is called to watch the incoming packets for one of * a specified type. all other packets, except QUIT's, are ignored. * * packet -- place to load the received packet * * type -- the type of the packet we're looking for */ d_watch (packet, type) register char *packet; char type; { register int length; int recvseq; #ifdef D_DBGLOG d_dbglog ("d_watch", "watching for packet with code '%c'", type); #endif D_DBGLOG switch (type) /* set timer, for delayed packets */ { case DATA: /* data packet */ d_alarm (d_todata); break; case XPATH: /* transmit path packet */ d_alarm (XPATHWAIT); break; case RPATH: /* receive path packet */ d_alarm (RPATHWAIT); break; case ESCAPE: /* escape packet */ d_alarm (ESCAPEWAIT); break; case NBUFF: /* windowsize packet */ d_alarm(NBUFFWAIT); break; } /* read packets and search. QUIT's cause a return after acknowlegement */ d_rcvseq = d_incseq (d_rcvseq); for ( ;; ) { length = d_rdport (packet); if (length < 0) { #ifdef D_LOG d_log ("d_watch", "bad watch read (%d)", length); #endif D_LOG break; } recvseq = d_fromhex (packet[FLAGOFF]) & 03; if (recvseq != d_rcvseq) { #ifdef D_LOG d_log ("d_watch", "packet out of sequence - type '%c', got %d, expected %d", packet[TYPEOFF], recvseq, d_rcvseq); d_log ("d_watch", "in '%s'", packet); #endif D_LOG continue; } if (packet[TYPEOFF] == type) break; /* success */ /* We have received a packet, but it is not the type we * wanted. Let's try to interrpret it as a special control * packet. */ switch (d_control (packet, length)) { case D_FATAL: return (D_FATAL); case D_OK: case D_NONFATAL: break; } } return (length); } /* * * D_INCSEQ * * this routine increments the sequence number passed as the argument * and returns the result. * * seqnum -- the sequence number to be incremented */ d_incseq (seqnum) register int seqnum; { seqnum = (seqnum + 1) & 03; return (seqnum); } register int ackleng, result; #ifdef D_DBGLOG d_dbglog ("d_ackpack", "acknowledging packet type '%c', seqnum %d", type, seqnum); #endif D_DBGLOG /* switch on the type. some packet don't get ack's sent */ switch (type) { case DATAACK: case XPATHACK: case RPATHACK: case QUITACK: case ESCAPEACK: case NBUFFACK: return (D_OK); } /* * this code produces a protocol mmdf/lib/dial/d_slave.c 444 0 12 11534 3620510406 10231 # include "util.h" # include "ll_log.h" # include "d_proto.h" # include "d_returns.h" # include "d_structs.h" /* May 82 D. Crocker added startup newline, to bypass some noise */ extern int errno; extern int d_errno; /* * D_SLVCONN * * this routine is called by the slave protocol module to acknowledge * a connection by the remote system. it sets the port characteristics * and performs the protocl startup sequence. * * logfile -- name of the log file * * tson -- set to non-zero to indicate that a transcript of the connection * should be kept * * tsfile -- name of the file in which the transcript should be kept * * debug -- set to non-zero to enable debug logging * * portfd -- file descriptor of the port * * maxrecv -- maximum receive packet length * * maxxmit -- maximum transmit packet length * */ d_slvconn (logfile, tson, tsfile, debug, portfd, maxrecv, maxxmit) struct ll_struct * logfile; char tsfile[]; int tson, debug, portfd, maxrecv, maxxmit; { extern int d_master, d_debug, d_snseq, d_rcvseq, d_lrmax, d_lxmax; extern FILE * d_prtfp; register int result; /* open the log file and transcript */ if ((result = d_opnlog (logfile)) < 0) return (result); if (tson) if ((result = d_tsopen (tsfile)) < 0) return (result); /* set up globals */ d_debug = debug; d_master = 0; d_snseq = 3; /* these are incremented before any ... */ d_rcvseq = 3; /* ... packets are expected */ d_lrmax = maxrecv; d_lxmax = maxxmit; /* use stdio for input and write() calls for output */ d_prtfp = fdopen (portfd, "r"); /* save current tty parameters and and set tty for protocol mode */ if (d_ttsave (d_prtfp, NULL) < 0 || d_ttproto (0) < 0) { /* don't abort; may be ok */ #ifdef D_LOG d_log ("d_slvconn", "error getting port parameters (errno=%d)", errno); #endif D_LOG d_errno = D_PRTSGTTY; } /* do the protocol startup sequence */ result = d_slvstart (); return (result); } /* * D_SLVDROP * * this routine is called to cause the connection from the master to * be dropped. * * sndquit -- called with non-zero value if the slave should attempt a * a QUIT exchange. */ d_slvdrop (sndquit) int sndquit; { register int result; if (sndquit) result = d_snquit (); #ifdef D_LOG d_log ("d_slvdrop", "slave dropped connection"); #endif D_LOG if (d_ttrestore () < 0) { /* restory tty characteristics */ d_errno = D_PRTSGTTY; } d_tsclose (); d_clslog (); return (result); } /* * D_SLVSTART * * this routine is called in the slave to start the protocol. */ d_slvstart () { extern int d_lxmax, d_lrmax, d_rxmax, d_rrmax, d_maxtext; extern char d_snesc, d_rcvesc; extern unsigned short d_lxill[], d_lrill[], d_rxill[], d_rrill[], d_lcvec[]; register int result; #ifdef D_DBGLOG d_dbglog ("d_slvstart", "Beginning slave host startup sequence"); #endif D_DBGLOG sleep (2); /* wait for line-settling */ d_wrtport ("\n", 1); /* hack to bypass initial noise */ /* send XPATH and RPATH to the master */ if ((result = d_snpath (XPATH, d_lxmax, d_lxill)) < 0) return (result); #ifdef D_DBGLOG d_dbglog ("d_slvstart", "XPATH sent ok."); #endif D_DBGLOG if ((result = d_snpath (RPATH, d_lrmax, d_lrill)) < 0) return (result); #ifdef D_DBGLOG d_dbglog ("d_slvstart", "RPATH sent ok."); #endif D_DBGLOG /* get XPATH and RPATH from the master. then form the * local transmission encoding vector. */ if ((result = d_getpath (XPATH, &d_rxmax, d_rxill)) < 0) return (result); #ifdef D_DBGLOG d_dbglog ("d_slvstart", "XPATH received ok."); #endif D_DBGLOG if ((result = d_getpath (RPATH, &d_rrmax, d_rrill)) < 0) return (result); #ifdef D_DBGLOG d_dbglog ("d_slvstart", "RPATH received ok."); #endif D_DBGLOG d_orbitvec (d_lxill, d_rrill, d_lcvec); d_maxtext = d_minimum (d_lxmax, d_rrmax); #ifdef D_DBGLOG d_dbglog ("d_slvstart", "Maximum transmit packet size set to %d", d_maxtext); #endif D_DBGLOG d_maxtext -= LHEADER; /* send our receive escape code to the master and get his */ if ((result = d_snfescape ()) < 0) return (result); #ifdef D_DBGLOG d_dbglog ("d_slvstart", "ESCAPE sent ok. Receive escape '%c'", d_rcvesc); #endif D_DBGLOG if ((result = d_getescape ()) < 0) return (result); #ifdef D_DBGLOG d_dbglog ("d_slvstart", "ESCAPE received ok. Transmit escape '%c'", d_snesc); #endif D_DBGLOG #ifdef D_LOG d_log ("d_slvstart", "Slave protocol startup completed ok."); #endif D_LOG return (D_OK); } aximum transmit packet length * */ d_slvconn (logfile, tson, tsfile, debug, portfd, maxrecv, maxxmit) struct ll_struct * logfile; char tsfile[]; int tsonmmdf/lib/dial/d_io.c 444 0 12 5305 3620510406 7505 # #include <stdio.h> /* This is a set of routines that is called to read characters from * the tty line. It stores them up so that they may be "replayed" * if necessary. The characters are stored in a circular buffer. */ /* The following functions are implemented: * * getc - returns 1 character from the appropriate place. * mark - designates the next character to be read as the one * to start with if a replay is requested. * replay - further calls to d_getc() will read characters from * the buffer until it is exhausted. * * * Added the select call to adapt to 4.2's signal mechanism. * DRockwell@BBN 31 Jan 84 * Backed out above change in favor of the s_io library. * DRockwell@BBN 1 Mar 84 */ #define BUFFSIZE 256 static int nleft; /* number of chars remaining in this replay */ static int current; /* index of current char in this replay */ static int last; /* next position to insert character at */ static int nchars; /* total number of chars in buffer. Note that */ /* this stays the same throughout a replay. */ static int first; /* index of the first character in the buffer */ static int replay; /* set if we are doing a replay */ #ifdef V4_2BSD static int rbuff[BUFFSIZE]; #else static char rbuff[BUFFSIZE]; #endif d_getc (channel) register FILE *channel; { #ifdef V4_2BSD register int c; #else char c; #endif /* Check to see if we are replaying the input */ if (replay != 0) { if (nleft == 0) { replay = 0; return (d_getc (channel)); } c = rbuff[current]; current = indexinc (current); nleft--; /* Be careful not to return an EOF from the queue. They * get in here when an alarm call from the timeout code * interrupts a read. However, they should clearly not * be returned from the queue, as they may not represent * the current state of things. */ if (c == EOF) return (d_getc (channel)); } else { if (nchars == BUFFSIZE) /* Have run out of buffer space. Rather then leave * a partial line, which could screw up 'rdport', * delete the first line in the buffer */ skpline(); else nchars++; c = getc (channel); rbuff[last] = c; last = indexinc (last); } return (c); } d_replay () { replay = 1; current = first; nleft = nchars; } d_mark () { replay = 0; nchars = 0; first = 0; last = 0; } static indexinc (index) int index; { if (index == (BUFFSIZE-1)) return (0); else return (index + 1); } static skpline () { while ((rbuff[first] != '\n') && (nchars > 0 ) ) { first++; nchars--; } if (nchars <= 0) d_mark (); else { first++; nchars--; } } OG sleep (2); /* wait for line-settling */ d_wrtport ("\n", 1); /* hack to bypass initial noise */ /* send XPATH and RPATH to the master */ if ((result = d_snpath (XPATH, d_lxmax, d_lxill)) < 0) return (result); #ifdef D_DBGLOG d_dbglog ("d_slvstart", "XPATH sent ok."); #endif D_Dmmdf/lib/dial/d_getnbuff.c 444 0 12 1633 3620510406 10676 #include "d_proto.h" #include <signal.h> #include "d_syscodes.h" #include "d_returns.h" /* * D_GETNBUFF * * this routine watches for an incoming NBUFF packet from the remote * host. it thens set the transmit buffering limit. * */ d_getnbuff() { extern int d_errno; extern int d_nbuff; register int length; char packet[MAXPACKET + 2]; #ifdef D_DBGLOG d_dbglog("d_getnbuff", "looking for NBUFF from other end"); #endif D_DBGLOG /* set timer so we don't wait forever */ length = d_watch(packet, NBUFF); if (length < 0) return(length); if (length != LNBUFF) { #ifdef D_DBGLOG d_dbglog("d_getnbuff", "bad NBUFF packet length (%d)", length); #endif D_DBGLOG d_errno = D_INITERR; return (D_FATAL); } d_nbuff = d_fromhex(packet[NBOFF]); d_nbuff = (d_nbuff << 4) | d_fromhex(packet[NBOFF + 1]); return(D_OK); } to insert character at */ static int nchars; /* total number of chars in buffer. Note that */ /*mmdf/lib/table/ 755 0 12 0 3671074620 6530 mmdf/lib/table/ch_tbseq.c 444 0 12 10240 3671073176 10571 /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ /* Sep 81 Dave Crocker added ch_tclose & ch_tfree, to allow * handling file descriptor overflow * May 82 Dave Crocker add ch_tread, to focus input * Jun 82 Dave Crocker split ch_tio off from ch_table * if local name, use lname, for proper case * May 84 G. B. Reilly added in domain support */ #include "util.h" #include "mmdf.h" #include "ch.h" /* has table state strcture def */ #include "dm.h" /* has domain info */ extern Chan **ch_tbsrch; extern LLog *logptr; extern char *locname; extern char *ch_dflnam; extern Domain **dm_list; /******************* FIND HOST NAME IN ANY TABLE ****************** */ Chan * ch_h2chan (hostr, pos) /* which chan name table is host in? */ register char *hostr; /* name of host */ int pos; /* position */ { register Chan **chanptr; char adrstr[LINESIZE]; /* the list of channel name tables is first searched. If a hit is found * in the table for the local channel, OK is returned to indicate a local * reference. */ for (chanptr = ch_tbsrch; *chanptr != 0; chanptr++) { if (tb_k2val ((*chanptr) -> ch_table, TRUE, hostr, adrstr) == OK) { /* the hostname IS in this table */ if (lexequ (ch_dflnam, (*chanptr) -> ch_name)) { return ((Chan *) OK); /* local reference */ } return (*chanptr); /* let caller know which chan */ } } return ((Chan *) NOTOK); /* host not listed anywhere */ } /* *********** GIVEN Subdomain, FIND Domain ***************** */ Domain * dm_s2dom (subdomain, official, dmbuf) char *subdomain; /* name of subdomain to look up */ char *official; /* where to stuff official name */ char *dmbuf; /* Domain route buffer */ { char *argv[DM_NFIELD]; char val[LINESIZE]; register Domain **dmnptr; for (dmnptr = dm_list; *dmnptr != 0; dmnptr++) { if (tb_k2val ((*dmnptr) -> dm_table, TRUE, subdomain, val) == OK) { (void) strcpy(dmbuf, val); str2arg(val, DM_NFIELD, argv, 0); (void) strcpy(official, argv[0]); return (*dmnptr); } } (void) strcpy (official, subdomain); /* to have something in the buffer */ (void) strcpy (dmbuf, subdomain); /* to have something in the buffer */ return ((Domain *) NOTOK); } /* ******* FIND VALUE (address), GIVEN ITS KEY (hostname) ********* */ tb_k2val (table, first, name, buf) /* use key and return value */ register Table *table; int first; /* start at beginning of list? */ char *buf; /* put value int this buffer */ register char *name; /* name of ch "member" / key */ { char host[LINESIZE]; if (!tb_open (table, first)) return (NOTOK); /* not opened */ while (tb_read (table, host, buf)) { /* cycle through keys */ if (lexequ (name, host)) /* does key match search name? */ { compress (buf, buf); /* get rid of extra white space */ return (OK); } } (void) strcpy (buf, "(ERROR)"); return (NOTOK); } e { first++; nchars--; } } OG sleep (2); /* wait for line-settling */ d_wrtport ("\n", 1); /* hack to bypass initial noise */ /* send XPATH and RPATH to the master */ if ((result = d_snpath (XPATH, d_lxmax, d_lxill)) < 0) return (result); #ifdef D_DBGLOG d_dbglog ("d_slvstart", "XPATH sent ok."); #endif D_Dmmdf/lib/table/gen 555 0 12 57 3620510407 7244 make -f ../../Makefile.com -f Makefile.real $* extern Chan **ch_tbsrch; extern LLog *logptr; extern char *locname; extern char *ch_dflnam; extern Domain **dm_list; /******************* FIND HOST NAME IN ANY TABLE ****************** */ Chan * ch_h2chan (hostr, pos) /* which chan name table is host in? */ register char *hostr; /* name of host */ int pos; /* position */ { register Chan **chanptr; char adrstr[LINESIZE]; /* the list of channel name tables is fmmdf/lib/table/dm_table.c 444 0 12 26567 3671073200 10557 /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ #include "util.h" #include "mmdf.h" #include "ch.h" /* has table state strcture def */ #include "dm.h" #include "chdbm.h" extern Domain **dm_list; extern Domain *dm_s2dom(); extern struct ll_struct *logptr; extern char *strcpy (); extern char *index(); extern char *rindex(); extern char *locdomain; extern char *locname; /* * when mangling addresses, should we try the reverse before or after * the RFC822 direction * is set by submit otherwise is as below. */ typedef struct {char *dptr; int dsize;} datum; /* * Steve Kille jan 84 Redo this module * * G. Brendan Reilly May 84 take out dependencies on DBM package * * */ /* ******* FIND VALUE (address), GIVEN ITS KEY (hostname) ********* */ LOCFUN dm_k2val (dmntbl, subdmn, domain, dmnroute) /* sub-domain spec -> routing host */ register Domain *dmntbl; /* domain table to look in */ register char *subdmn; /* name of sub-domain to look for */ char *domain; /* where to stuff full domain name */ Dmn_route *dmnroute; /* where to put routing information */ { char *p; #ifdef DEBUG ll_log (logptr, LLOGBTR, "dm_k2val (%s, %s)", dmntbl -> dm_name, subdmn); #endif switch(tb_k2val (dmntbl -> dm_table, TRUE, subdmn, dmnroute -> dm_buf)){ case MAYBE: return(MAYBE); case OK: #ifdef DEBUG ll_log (logptr, LLOGFTR, "hit"); #endif dmnroute -> dm_argc = str2arg (dmnroute -> dm_buf, DM_NFIELD, dmnroute -> dm_argv, (char *)0); (void) strcpy(domain, dmnroute -> dm_argv[0]); return (OK); } p = subdmn; while ((p = index (p, '.')) != (char *)0) { char tdmnbuf[LINESIZE]; p++; switch(tb_k2val (dmntbl -> dm_table, TRUE, p, tdmnbuf)) { case MAYBE: return(MAYBE); default: continue; case OK: break; } /* get the entry */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "hit"); #endif *(p - 1) = '\0'; /* build the domain */ if (dmntbl -> dm_domain[0] == '\0') { if (*p == '\0') (void) strcpy (domain, subdmn); else sprintf (domain, "%s.%s", subdmn, p); } else sprintf (domain, "%s.%s.%s", subdmn, p, dmntbl -> dm_domain); sprintf (dmnroute -> dm_buf, "%s %s", domain, tdmnbuf); dmnroute -> dm_argc = str2arg (dmnroute -> dm_buf, DM_NFIELD, dmnroute -> dm_argv, (char *)0); return (OK); } return (NOTOK); } /* *********** GIVEN Subdomain, FIND HOST ROUTE ************* */ /* * If BOTHEND is defined then we are almost certainly running in JNT * land and so we should try the reversed address before the RFC822 * direction, should save some cpu cycles */ LOCFUN Domain * #ifndef BOTHEND dm_sd2route (value, domain, dmnroute) char *value; /* what the use provides */ char *domain; /* full domain value */ Dmn_route *dmnroute; /* source route */ #else dm_sd2route (value, reverse, domain, dmnroute) char *value; /* what the use provides */ char *reverse; /* the same bacwards */ char *domain; /* full domain value */ Dmn_route *dmnroute; /* source route */ #endif { char *sdptr; Domain *dmnptr; char official[LINESIZE]; #ifdef BOTHEND char *revsdptr = reverse; #endif #ifdef DEBUG ll_log (logptr, LLOGBTR, "dm_sd2route (%s)", value); #endif /* first try the JNT way round if */ /* got BOTHEND defined */ #ifdef BOTHEND if ((dmnptr = dm_s2dom (reverse, official, dmnroute -> dm_buf)) != (Domain *) NOTOK || (dmnptr = dm_s2dom (value, official, dmnroute -> dm_buf)) != (Domain *) NOTOK) #else if ((dmnptr = dm_s2dom (value, official, dmnroute -> dm_buf)) != (Domain *) NOTOK) #endif { if(dmnptr == (Domain *)MAYBE) return(dmnptr); (void) strcpy(domain, official); dmnroute -> dm_argc = str2arg (dmnroute -> dm_buf, DM_NFIELD, dmnroute -> dm_argv, (char *)0); #ifdef DEBUG ll_log (logptr, LLOGFTR, "Domain value '%s' from '%s'", domain, dmnptr -> dm_show); #endif return (dmnptr); } /* * If not, move in from left. Progressing on both forwards * and backwards options. */ sdptr = value; while ((sdptr = index (sdptr, '.')) != (char *) 0) { char tbuf[LINESIZE]; #ifdef BOTHEND revsdptr = index (revsdptr, '.') + 1; if ((dmnptr = dm_s2dom (revsdptr, official, tbuf)) != (Domain *) NOTOK) { if(dmnptr == (Domain *)MAYBE) return(dmnptr); *(revsdptr - 1) = '\0'; sprintf (domain, "%s.%s", reverse, official); sprintf (dmnroute -> dm_buf, "%s %s", domain, tbuf); dmnroute -> dm_argc = str2arg (dmnroute -> dm_buf, DM_NFIELD, dmnroute -> dm_argv, (char *)0); #ifdef DEBUG ll_log(logptr,LLOGFTR, "Domain value (rev) '%s' from '%s',o = '%s'", domain, dmnptr -> dm_show, official); #endif return (dmnptr); } #endif BOTHEND if ((dmnptr = dm_s2dom (++sdptr, official, tbuf)) != (Domain *) NOTOK) { if(dmnptr == (Domain *)MAYBE) return(dmnptr); *(sdptr - 1) = '\0'; sprintf (domain, "%s.%s", value, official); sprintf (dmnroute -> dm_buf, "%s %s", domain, tbuf); dmnroute -> dm_argc = str2arg (dmnroute -> dm_buf, DM_NFIELD, dmnroute -> dm_argv, (char *)0); #ifdef DEBUG ll_log (logptr, LLOGFTR, "Domain value '%s' from '%s', o = '%s'", domain, dmnptr -> dm_show, official); #endif return (dmnptr); } } #ifdef DEBUG ll_log (logptr, LLOGBTR, "'%s' not found", value); #endif return ((Domain *) NOTOK); } /* *********** GIVEN VALUE, FIND HOST ROUTE ******************** */ /* This will take any value, and map it into a domain route. */ /* It is optimised for val being a fully qualified domain */ #ifndef BOTHEND Domain * dm_v2route (value, domain, dmnroute) char *value; /* what the use provides */ char *domain; /* full domain value */ register Dmn_route *dmnroute; /* source route */ { Dmn_route tmpdmn; /* hold parsed domain string */ register Domain *dmnptr; int sublen; /* length of right-hand to look up */ int ind; #ifdef DEBUG ll_log (logptr, LLOGBTR, "dm_v2route (%s)", value); #endif (void) strcpy (tmpdmn.dm_buf, value); tmpdmn.dm_argc = cstr2arg (tmpdmn.dm_buf, DM_NFIELD, tmpdmn.dm_argv, '.'); if (tmpdmn.dm_argc == NOTOK) { #ifdef DEBUG ll_log (logptr, LLOGTMP, "Cstr2arg failed (%s)", tmpdmn.dm_buf); #endif return ( (Domain *)NOTOK); } #ifdef DEBUG ll_log (logptr, LLOGFTR, "%d fields", tmpdmn.dm_argc); #endif dmnroute -> dm_argc = 1; /* initialize with something safe */ (void) strcpy (dmnroute -> dm_buf, value); dmnroute -> dm_argv[0] = dmnroute -> dm_buf; tmpdmn.dm_argc--; for (ind = sublen = 0; ind <= tmpdmn.dm_argc; ind++) { if (ind != 0) tmpdmn.dm_buf[sublen - 1] = '.'; /* undo cstr2arg! */ sublen += strlen (tmpdmn.dm_argv[ind]); /* how much to peel off, from right */ /* the +1 is for the delimiter */ if (ind != tmpdmn.dm_argc) sublen++; /* point to next if not last */ if ((dmnptr = dm_nm2struct (&value[sublen])) == (Domain *) NOTOK) continue; /* failed */ /* this is the value */ switch(dm_k2val (dmnptr, tmpdmn.dm_argv[0], domain, dmnroute)){ case MAYBE: return( (Domain *)MAYBE); case OK: #ifdef DEBUG ll_log (logptr, LLOGFTR, "%s' hit in domain table '%s' with '%s'", value, dmnptr -> dm_show, dmnroute -> dm_buf); #endif return (dmnptr); } } return (dm_sd2route (value, domain, dmnroute)); } #else BOTHEND LOCFUN Domain * dm_rv2route (value, domain, dmnroute) char *value; /* what the use provides */ char *domain; /* full domain value */ register Dmn_route *dmnroute; /* source route */ { Dmn_route tmpdmn; /* hold parsed domain string */ register Domain *dmnptr; int sublen; /* length of right-hand to look up */ int ind; char buf[LINESIZE]; (void) strcpy (tmpdmn.dm_buf, value); tmpdmn.dm_argc = cstr2arg (tmpdmn.dm_buf, DM_NFIELD, tmpdmn.dm_argv, '.'); if (tmpdmn.dm_argc == NOTOK) { #ifdef DEBUG ll_log (logptr, LLOGTMP, "Cstr2arg failed (%s)", tmpdmn.dm_buf); #endif return ( (Domain *)NOTOK); } #ifdef DEBUG ll_log (logptr, LLOGFTR, "%d fields", tmpdmn.dm_argc); #endif tmpdmn.dm_argc--; for (ind = sublen = 0; ind <= tmpdmn.dm_argc; ind++) { if (ind != 0) tmpdmn.dm_buf[sublen - 1] = '.'; /* undo cstr2arg! */ sublen += strlen (tmpdmn.dm_argv[ind]); /* how much to peel off, from right */ /* the +1 is for the delimiter */ if (ind != tmpdmn.dm_argc) sublen++; /* point to next if not last */ if ((dmnptr = dm_nm2struct (&value[sublen])) == (Domain *) NOTOK) continue; /* failed */ /* this is the value */ switch(dm_k2val (dmnptr, tmpdmn.dm_argv[0], domain, dmnroute)){ case MAYBE: return( (Domain *)MAYBE); case OK: #ifdef DEBUG ll_log (logptr, LLOGFTR, "%s' hit in domain table '%s' with '%s'", value, dmnptr -> dm_show, dmnroute -> dm_buf); #endif return (dmnptr); } } return ( (Domain *) 0); /* not found */ } Domain * dm_v2route (value, domain, dmnroute) char *value; /* what the use provides */ char *domain; /* full domain value */ Dmn_route *dmnroute; /* source route */ { Domain *dmnptr; char *reverse; /* to handle bigend mess */ extern char *ap_dmflip(); #ifdef DEBUG ll_log (logptr, LLOGBTR, "dm_v2route (%s)", value); #endif dmnroute -> dm_argc = 1; /* initialize with something safe */ (void) strcpy (dmnroute -> dm_buf, value); dmnroute -> dm_argv[0] = dmnroute -> dm_buf; if ((dmnptr = dm_rv2route (value, domain, dmnroute)) != (Domain *)0) return (dmnptr); reverse = ap_dmflip (value); if( (dmnptr = dm_rv2route (reverse, domain, dmnroute)) != (Domain *)0){ free (reverse); return (dmnptr); } dmnptr = dm_sd2route (value, reverse, domain, dmnroute); free (reverse); return (dmnptr); } #endif BOTHEND gc = cstr2arg (tmpdmn.dm_buf, DM_NFIELD, tmpdmn.dm_argv, '.'); if (tmpdmn.dm_argc == NOTOK) { #ifdef DEBUG ll_log (logptr, LLOGTMP,mmdf/lib/table/ch_tbdbm.c 444 0 12 23067 3671073201 10543 /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ /* READ "NETWORK" HOST NAME TABLES * * These routines use the dbm(3x) procedures of Unix Version 7. * All accesses are via the fetch call of the library. Linkage between * elements of the database are contained within the procedures instead * of the database itself due to the nature of the dbm design. * * May 81 Jim Lieb, SRI rewrite by to use dbm library * Jun 82 D. Crocker minor cleanup & installation * force key value to be lower case * partition sequence #s by channel, not global * Dec 85 P.Cockcroft UCL add nameserver support and add bullet protection */ #include "util.h" #include "mmdf.h" #include "ch.h" /* has table state strcture def */ #include "dm.h" #include "chdbm.h" extern LLog *logptr; extern char *tbldfldir; extern char *tbldbm; extern char *locname; extern Chan **ch_tbsrch; extern char *ch_dflnam; extern char *strcpy(); typedef struct {char *dptr; int dsize;} datum; extern datum fetch(); /* ******************* FIND HOST NAME IN ANY TABLE ****************** */ Chan * ch_h2chan (hostr, pos) /* which chan name table is host in? */ char *hostr; /* name of host */ int pos; /* which position to get (0 = first) */ { register Chan *chanptr; register char *cp; register struct DBvalues *dp; DBMValues dbm; Chan **chp; char hostname[ADDRSIZE]; #ifdef NAMESERVER int ns_done = 0; #endif NAMESERVER int dbm_done = 0; /* the list of channel name tables is first searched. If a hit is found * in the table for the local channel, OK is returned to indicate a local * reference. */ #ifdef DEBUG ll_log( logptr, LLOGFTR, "h2chan ('%s', %d)", hostr, pos); #endif for (chp = ch_tbsrch; (chanptr = *chp) != (Chan *)0; chp++) { #ifdef DEBUG ll_log( logptr, LLOGFTR, "h2chan table '%s'", chanptr->ch_table->tb_name); #endif #ifdef NAMESERVER if ((chanptr -> ch_table -> tb_flags & TB_SRC) == TB_NS) { if (!ns_done) { /* * assumes the fact we are looking for a 'channel' * if this is not the case you must do the lookup every * time round the loop * This code believes all NS channel tables are equivalent, * and they will return the same answer. Not unreasonable * at the current time, but watch out. */ switch(ns_fetch (chanptr->ch_table, hostr, hostname, 1)){ case OK: ns_done++; break; case MAYBE: return( (Chan *)MAYBE); } ns_done++; } if (ns_done == 1) continue; if(--pos > 0) continue; #if DEBUG > 1 ll_log( logptr, LLOGFTR, "NSconsider ('%s', '%s')", hostname, chanptr->ch_lname); #endif if (lexequ (chanptr -> ch_name, ch_dflnam)) return( (Chan *)OK ); /* local ref */ return( chanptr ); } else #endif NAMESERVER { if (!dbm_done) { dbm_done++; if (tb_fetch (hostr, dbm)) dbm_done++; } if (dbm_done == 1) continue; for (dp = dbm ; (cp = dp->RECname) != NULL ; dp++) { #if DEBUG > 1 ll_log( logptr, LLOGFTR, "consider ('%s')", cp); #endif if (lexequ(cp, chanptr -> ch_table -> tb_name)) { if (--pos > 0) break; if (lexequ(chanptr -> ch_name, ch_dflnam)) return( (Chan *)OK ); return(chanptr); } } } } return ((Chan *) NOTOK); } /* *********** GIVEN Subdomain, FIND Domain ***************** */ Domain * dm_s2dom (subdomain, official, dmbuf) char *subdomain; /* name of subdomain to look up */ char *official; /* Where to put official name */ char *dmbuf; /* Domain route buffer */ { register Domain *dmnptr; register struct DBvalues *dp; register char *cp; DBMValues dbm; extern Domain **dm_list; Domain **dmp; char *argv[DM_NFIELD]; int dbm_done = 0; #ifdef DEBUG ll_log( logptr, LLOGFTR, "dm_s2dom ('%s')", subdomain); #endif for (dmp = dm_list; (dmnptr = *dmp) != (Domain *)0; dmp++) { #ifdef NAMESERVER if ((dmnptr -> dm_table -> tb_flags & TB_SRC) == TB_NS) { if ((dmnptr-> dm_table -> tb_flags & TB_PARTIAL) == TB_PARTIAL) switch(ns_fetch(dmnptr->dm_table,subdomain,official,1)){ case OK: /* route is simply official name */ (void) strcpy(dmbuf,official); return( dmnptr ); case MAYBE: return( (Domain *)MAYBE); } } else #endif NAMESERVER { if (!dbm_done) { dbm_done++; if (tb_fetch (subdomain, dbm)) dbm_done++; } if (dbm_done == 1) continue; for ( dp = dbm ; (cp = dp->RECname) != NULL ; dp++) { #ifdef DEBUG ll_log( logptr, LLOGFTR, "consider ('%s')", cp); #endif if (!lexequ(cp, dmnptr -> dm_table -> tb_name)) continue; if(dp->RECval == NULL) { /* should never happen */ *dmbuf = *official = '\0'; return(dmnptr); } (void) strcpy (dmbuf, dp->RECval); str2arg(dp->RECval, DM_NFIELD, argv, 0); (void) strcpy(official, argv[0]); return(dmnptr); } } } (void) strcpy (official, subdomain); return ((Domain *) NOTOK); } /* ******* Get a record from the database, given a key ********** */ /* Note that the strings are in internal statics */ tb_fetch (name, dbm) /* return filled entry for name */ char *name; /* use this key to fetch entry */ DBMValues dbm; /* put the entry here */ { LOCVAR int dbmopen = FALSE; LOCVAR char dbvalue[256]; datum key, value; char *lastchar; register char *p, *cp; register struct DBvalues *dp; register cnt; if (!dbmopen) { extern int dbminit(); char filename[128]; #ifdef DEBUG ll_log (logptr, LLOGBTR, "tb_fetch: dbminit"); #endif getfpath (tbldbm, tbldfldir, filename); if (dbminit (filename) < 0) err_abrt (LLOGTMP, "Error opening database '%s'", filename); dbmopen = TRUE; } #ifdef DEBUG ll_log (logptr, LLOGBTR, "fetch (%s)", name); #endif key.dptr = name; key.dsize = strlen (name) + 1; for (p = name; *p; p++) *p = uptolow (*p); value = fetch (key); if (value.dptr == NULL) { #ifdef DEBUG ll_log (logptr, LLOGBTR, "fetch of '%s' failed", name); #endif return (FALSE); } for (cp = value.dptr, p= dbvalue, lastchar = &p[value.dsize]; p < lastchar; *p++ = *cp++) ; *p = '\0'; /* copy value to separate storage */ for (p = dbvalue , dp = dbm, cnt = 0; *p && cnt < MAXVAL ; cnt++, dp++) { for(dp->RECname = p ; *p ; p++) if (*p == ' ') { /* get table name */ *p++ = '\0'; break; } for (dp->RECval = (*p ? p : NULL); *p; p++) if (*p == FS) { /* get value-part */ *p++ = '\0'; break; } #ifdef DEBUG ll_log (logptr, LLOGFTR, "fetch val(%d)='%s/%s'", cnt, dp->RECname, (dp->RECval==NULL) ? "<NULL>": dp->RECval); #endif } for(;cnt <= MAXVAL; cnt++, dp++) /* zero the rest of the structures */ dp->RECname = dp->RECval = NULL; return (TRUE); } /* ******* FIND VALUE (address), GIVEN ITS KEY (hostname) ********* */ tb_k2val (table, first, name, buf) /* use key and return value */ register Table *table; int first; /* start at beginning of list? */ register char name[]; /* name of ch "member" / key */ char *buf; /* put value int this buffer */ { LOCVAR DBMValues dbm; LOCVAR struct DBvalues *dp = (struct DBvalues *) 0; register char *cp; #ifdef NAMESERVER int retval; #endif #ifdef DEBUG ll_log (logptr, LLOGBTR, "tb_k2val (%s, first=%d, %s)", table -> tb_name, first, name); #endif #ifdef NAMESERVER if ((table->tb_flags&TB_SRC) == TB_NS) { if ((retval = ns_fetch (table, name, buf, first)) != NOTOK) return (retval); #ifdef DEBUG ll_log (logptr, LLOGFTR, "tb_k2val failed"); #endif (void) strcpy (buf, "(ERROR)"); return (NOTOK); } #endif NAMESERVER if (!first) dp++; else { if (tb_fetch (name, dbm)) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "hit"); #endif dp = dbm; } else dp = (struct DBvalues *) 0; /* none */ } if (dp) for (; (cp = dp->RECname) != NULL; dp++) { if (strequ (cp, table -> tb_name)) { /* this is the right channel */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "table(%s)", cp); #endif if(dp->RECval == NULL) *buf = '\0'; else (void) strcpy (buf, dp->RECval); return (OK); /* give them the value-part */ } } #ifdef DEBUG ll_log (logptr, LLOGFTR, "tb_k2val failed"); #endif (void) strcpy (buf, "(ERROR)"); return (NOTOK); } ) != NULL ; dp++) { #ifdef DEBUG ll_log( logptr, LLOGFTR, "consider ('%s')", cp); #endif if (!lexequ(cp, dmnptr -> dm_table -> tb_name)) continue; if(dp->RECval == NULL) { /* should never happen */ *dmbuf = *official = '\0'; return(dmnptr); } (void) strcpy (dmbuf, dp->RECval); str2arg(dp->RECval, DM_NFIELD, argv, 0); (void) strcpy(official, argv[0]); return(dmnptr); } } } (void) strcpy (official, subdomaimmdf/lib/table/dm_struct.c 444 0 12 3231 3620510410 10744 #include "util.h" #include "mmdf.h" /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ #include "ch.h" #include "dm.h" extern LLog *logptr; extern Domain **dm_list; Domain *dm_nm2struct (name) /* give pointer to structure for chan */ char name[]; /* string name of domain */ { register Domain **dmnptr; #ifdef DEBUG ll_log (logptr, LLOGBTR, "dm_nm2struct (%s)", name); #endif for (dmnptr = dm_list; *dmnptr != 0; dmnptr++) if (lexequ (name, (*dmnptr) -> dm_name)) { #ifdef DEBUG ll_log (logptr, LLOGFTR, "hit"); #endif return (*dmnptr); /* return addr of chan struct entry */ } return ((Domain *) NOTOK); } le must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker Maymmdf/lib/table/tb_io.c 444 0 12 21741 3620510411 10063 #include "util.h" #include "mmdf.h" #include "ch.h" /* has table state strcture def */ /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ /* READ "NETWORK" HOST NAME TABLES * * BNF for the file is: * * entries = entries entry * entry = name separator value eol * name = {string of chars not containing a <separator>} * separator= {see the chars in _hkeyend[]} * value = {string of chars not containing an <eol>} * eol = {see the chars in _hvalend[]} * * where * * name is a key * value is any relevant text, usually the host "address" * * There may be any number of entries with the same value string, * thereby allowing name aliases, such as UDel-EE, UDel, and UDEE. * * The key of the first entry with a given value is taken as the * "official" "name" of that "address". (Sorry about all the * quotation marks.) * * Turns out that the local machine's alias file can also be accessed * by these routines. * * Any number of tables may be supported simultaneously, since a * single table's state information structure is maintained externally, * and passed with each call, through the parameter netinfo. * The state structure's place for the file descriptor MUST * initially be zero. * * "Hard" failures are considered not acceptable and hence cause a * call to the external routine err_abrt(); to change this policy, * just change the calls to err_abrt() to be simple failure returns. * The rest of the code should run "transparently". */ /* Sep 81 Dave Crocker added ch_tclose & ch_tfree, to allow * handling file descriptor overflow * May 82 Dave Crocker add ch_tread, to focus input * Jun 82 Dave Crocker split ch_tio off from ch_table * Dec 82 Doug Kingston Fixed bug in tb_read() that caused it to * ignore EOF's. (DPK@BRL) * Apr 83 Steve Kille Change it back again */ extern struct ll_struct *logptr; extern char *tbldfldir; extern Table **tb_list; /* all known tables */ /* SEK change syntax */ extern int tb_numtables; /* how many of them there are */ extern char *index(); extern char *compress(); LOCVAR char _hkeyend[] = ": \t\n\377", /* chars which delimit end of key */ _hvalend[] = "\n\000\377"; /* chars which delimit end of value */ LOCVAR short tb_nopen; /* number of opened file descriptors */ /* ****************** BASIC FILE ACTION *************************** */ tb_open (table, fromstart) /* gain access to a table */ register Table *table; /* table's state information */ int fromstart; /* ok to reset, if already open? */ { extern int errno; /* in case open fails */ char temppath[128]; /* currently, there is one file (descriptor) per table. for a large * configuration, you will run out of fd's. until/unless you run * a single-file dbm-based version, there is a hack, below, which * limits the number of open file-descriptors to 6. Trying to open * a 7th will cause a currently-opened one to be closed. */ switch ((int)table) { case 0: case NOTOK: return (FALSE); /* can't open what isn't specified */ } switch ((int)(table -> tb_fp)) { case 0: /* not opened yet */ /*HACK*/ table -> tb_pos = 0L; if (table -> tb_file != 0) { /* we have a path string */ if (tb_nopen < 6 || tb_free ()) { getfpath (table -> tb_file, tbldfldir, temppath); /* add on directory portion */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "opening '%s'", temppath); #endif if ((table -> tb_fp = fopen (temppath, "r")) != NULL) { tb_nopen++; break; /* all is well */ } } ll_err (logptr, (errno == ENOENT) ? LLOGPTR : LLOGTMP, "Error opening %s name table '%s' (numfiles open = %d)", table -> tb_show, temppath, tb_nopen); /* major only if file exists */ } table -> tb_fp = (FILE *) EOF; /* no path str -> quietly fail */ /* DROP ON THROUGH */ case NOTOK: return (FALSE); /* failed earlier try */ default: if (fromstart) /* always start from beginning? */ tb_seek (table, 0L); } return (TRUE); } /**/ tb_close (table) /* close a table */ register Table *table; { switch ((int)table) { case 0: case NOTOK: return (FALSE); /* can't close what isn't specified */ } switch ((int)(table -> tb_fp)) { case NOTOK: table -> tb_fp = 0; /* mark as free */ case 0: /* not opened yet */ return (FALSE); /* no close performed */ default: fclose (table -> tb_fp); table -> tb_fp = 0; /* mark as free */ tb_nopen--; return (TRUE); } /* NOTREACHED */ } tb_free () /* create a free file descriptor */ { /* step from the lowest search priority to the highest, looking for a * channel to close. return success as soon as one is done. */ register Table **tableptr; #ifdef DEBUG ll_log (logptr, LLOGFTR, "tb_free ()"); #endif for (tableptr = tb_list + tb_numtables - 1; tableptr >= tb_list; tableptr--) { if (tb_close (*tableptr)) return (TRUE); /* got a channel closed */ } return (FALSE); /* couldn't get any closed */ } tb_seek (table, thepos) /* reset table position */ register Table *table; long thepos; { switch ((int)table) { case 0: case NOTOK: return (NOTOK); /* can't open what isn't specified */ } switch ((int)(table -> tb_fp)) { /* not opened yet */ case NULL: case EOF: return (NOTOK); } fseek (table -> tb_fp, thepos, 0); /*HACK*/ table -> tb_pos = thepos; return ((ferror (table -> tb_fp)) ? NOTOK : OK); } /* ***************** READ THE NAME TABLE ************************** */ /* * This routine used not to recognise comments, this meant that dbmbuild * could skip real records in the following case. * #<NL> * nott:nott * would be read as key = '#' value = 'nott:nott' and dbmbuild would * throw it away as a comment. (JPO) */ tb_read (table, key, value) /* read a table's record */ register Table *table; char *key, *value; { register int len; register FILE *fp; char terminator; fp = table -> tb_fp; if (feof (fp)) return (FALSE); while ((len = qread (fp, key, LINESIZE, _hkeyend, '\\')) > 0) { /*HACK*/ table -> tb_pos += len; if( key[0] == '\n' ) continue; /* blank line */ if( key[0] == '#' ) if( key[len - 1] == '\n' ) continue; /* have read up to the newline */ else { if((len = gcread(fp, value, LINESIZE, _hvalend)) > 0) { /*HACK*/ table -> tb_pos += len; continue; /* skip to end of line */ } else return (FALSE); /* EOF I guess */ } terminator = key[len - 1]; key[len - 1] = '\0'; /* get rid of terminator character */ if (len > 1) compress (key, key); if (index(_hvalend, terminator) > 0) value[0] = '\0'; /* whole line was a key */ else { if ((len = gcread (fp, value, LINESIZE, _hvalend)) > 0) { /*HACK*/ table -> tb_pos += len; value[len - 1] = '\0'; if (len > 1) compress (value, value); } else /* Don't worry about errors here... */ value[0] = '\0'; } return (TRUE); } if( len < 0 ) /* (len < 0) indicating IO error */ err_abrt (RP_FIO, "Error reading %s name table '%s'", table -> tb_show, table -> tb_file); return (FALSE); /* EOF on entry boundary, DPK@BRL */ } "Error opening %s name tablemmdf/lib/table/tb_struct.c 444 0 12 3154 3664425162 10776 #include "util.h" #include "mmdf.h" #include "ch.h" /* * MULTI-CHANNEL MEMO DISTRIBUTION FACILITY (MMDF) * * * Copyright (C) 1979,1980,1981 University of Delaware * * Department of Electrical Engineering * University of Delaware * Newark, Delaware 19711 * * Phone: (302) 738-1163 * * * This program module was developed as part of the University * of Delaware's Multi-Channel Memo Distribution Facility (MMDF). * * Acquisition, use, and distribution of this module and its listings * are subject restricted to the terms of a license agreement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David H. Crocker May 1981 * version 1 David H. Crocker October 1981 * */ extern Table **tb_list; #ifdef DEBUG extern LLog *logptr; #endif Table *tb_nm2struct (name) /* give pointer to structure for chan */ register char *name; /* string name of chan */ { register Table **tableptr; #ifdef DEBUG ll_log(logptr, LLOGBTR, "tb_nm2struct(%s)",name); #endif for (tableptr = tb_list; *tableptr != (Table *) 0; tableptr++) if (lexequ (name, (*tableptr) -> tb_name)) return (*tableptr); /* return addr of chan struct entry */ return ((Table *) NOTOK); } greement. * Documents describing systems using this module must cite its source. * * The above statements must be retained with all copies of this * program and may not be removed without the consent of the * University of Delaware. * * * version -1 David H. Crocker March 1979 * version 0 David H. Crocker April 1980 * version v7 David Hmmdf/lib/table/tb_ns.fake.c 444 0 12 256 3620510411 10737 /* * TB_NS.FAKE.C * * Module to call on host system provided lookup routines. */ /* Place holder only */ int dummy_thing_for_namelist; /* to keep ranlib/lorder quiet */ ct (name) /* give pointer to structure for chan */ register char *name; /* string name of chan */ { register Table **tableptr; #ifdef DEBUG ll_log(logptr, LLOGBTR, "tb_nm2struct(%s)",name); #endif for (tableptr = tb_list; *tableptr != (Table *) 0; tableptr++) if (lexequ (name, (*tableptr) -> tb_nmmdf/lib/table/Makefile.real 444 0 12 3655 3635173604 11210 # Makefile for name-table lookup-routines # Use ch_tbseq if sequential name tables, only, are available # Use ch_tbdbm if you have Unix dbm() subroutines available # MODULES = dm_struct dm_table ch_tbdbm ch_tbseq \ tb_io tb_ns.$(TB_NS) tb_struct OBJECTS = dm_struct.o dm_table.o $(CH_TB).o \ tb_io.o tb_ns.$(TB_NS).o tb_struct.o COBJECTS = dm_struct.c dm_table.c $(CH_TB).c \ tb_io.c tb_ns.$(TB_NS).c tb_struct.c real-default: ../table-made ../table-made: $(OBJECTS) -ar r ../libmmdf.a $(OBJECTS) -touch ../table-made -@echo "table routines built normally" lint: $(LINT) -Ctable $(LFLAGS) $(COBJECTS) clean: -rm -f *.o *.a x.c makedep eddep make.out errs llib-ltable* # DO NOT DELETE THIS LINE -- make depend uses it dm_struct.o: dm_struct.c dm_struct.o: ../../h/util.h dm_struct.o: ../../h/mmdf.h dm_struct.o: ../../h/ch.h dm_struct.o: ../../h/dm.h dm_table.o: dm_table.c dm_table.o: ../../h/util.h dm_table.o: ../../h/mmdf.h dm_table.o: ../../h/ch.h dm_table.o: ../../h/dm.h dm_table.o: ../../h/chdbm.h ch_tbdbm.o: ch_tbdbm.c ch_tbdbm.o: ../../h/util.h ch_tbdbm.o: ../../h/mmdf.h ch_tbdbm.o: ../../h/ch.h ch_tbdbm.o: ../../h/dm.h ch_tbdbm.o: ../../h/chdbm.h ch_tbseq.o: ch_tbseq.c ch_tbseq.o: ../../h/util.h ch_tbseq.o: ../../h/mmdf.h ch_tbseq.o: ../../h/ch.h ch_tbseq.o: ../../h/dm.h tb_io.o: tb_io.c tb_io.o: ../../h/util.h tb_io.o: ../../h/mmdf.h tb_io.o: ../../h/ch.h tb_ns.4.2.o: tb_ns.4.2.c tb_ns.4.2.o: ../../h/util.h tb_ns.4.2.o: ../../h/mmdf.h tb_ns.4.2.o: ../../h/ch.h tb_ns.4.2.o: ../../h/ns.h tb_ns.4.2.o: /usr/include/netdb.h tb_ns.4.2.o: /usr/include/sys/socket.h tb_ns.4.2.o: /usr/include/netinet/in.h tb_ns.4.2.o: /usr/include/arpa/nameser.h tb_ns.4.2.o: /usr/include/arpa/resolv.h tb_struct.o: tb_struct.c tb_struct.o: ../../h/util.h tb_struct.o: ../../h/mmdf.h tb_struct.o: ../../h/ch.h # DEPENDENCIES MUST END AT END OF FILE # IF YOU PUT STUFF HERE IT WILL GO AWAY # see make depend above tb_ns.$(TB_NS).c tb_struct.c real-default: ../table-made ../table-made: $(OBJECTS)mmdf/lib/table/tb_ns.4.2.c 444 0 12 41743 3671073203 10413 /* * TB_NS.C * * Original version by Phil Cockcroft to use MF and MD records. * Also had several speed improvements such as caching. Later * reworked by Phil to do MX lookups. * * Mostly rewritten by Craig Partridge to move routing decisions * into SMTP channel. * */ #include "util.h" #include "mmdf.h" #include "ch.h" #include "ns.h" #include <netdb.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/nameser.h> #include <arpa/resolv.h> extern char *strncpy(); /* * if you want caching, define NSCACHE */ #define NSCACHE 25 /* * next definition should go away as all servers use MX * right now if they don't we just try the address. MF and MD are * dead! */ #define OLDSERVER #ifndef MAXADDR #define MAXADDR 10 #endif #ifndef MAXADDR_PER #define MAXADDR_PER 2 #endif #ifndef MAXDATA #define MAXDATA (4 * PACKETSZ) /* tcp tried after udp */ #endif #ifndef MAXMX #define MAXMX (MAXADDR) /* shouldn't be < MAXADDR */ #endif extern struct ll_struct *logptr; extern char *strdup(), *strcpy(); extern char *locmachine, *locdomain, *locname; union ansbuf { /* potentially huge */ HEADER ab1; char ab2[MAXDATA]; }; union querybuf { /* just for outbound stuff */ HEADER qb1; /* didn't want to clobber stack */ char qb2[2 * MAXDNAME]; }; LOCVAR struct in_addr mx_addrs[MAXADDR]; /* cache of MX addrs */ LOCVAR int max_mxa= -1, on_mxa= -1; /* indicies into cache of MX addrs */ LOCVAR char dn_name[MAXDNAME]; LOCVAR char local[MAXDNAME]; #ifdef NSCACHE struct ns_cache { int nc_type; int nc_count; int nc_time; char *nc_key; char *nc_data; }; LOCVAR struct ns_cache *cachedata[NSCACHE+1]; LOCVAR int cachetime; #endif /* * table fetch routine for ns */ ns_fetch(table, key, value, first) Table *table; /* What "table" are we searching */ char *key; char *value; /* Where to put result */ int first; /* now used */ { register char *tmp; char lbuf[LINESIZE]; int type; int rval; #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_fetch (%o, %s, %d)", table->tb_flags, key, first); ll_log(logptr, LLOGFTR, "ns_fetch: timeout (%d), rep (%d), servers (%d)", _res.retrans, _res.retry, _res.nscount); #endif type = (table->tb_flags & TB_TYPE); if (first) { switch(type) { case TB_DOMAIN: /* name -> official domain name */ sprintf(lbuf, "%s.%s", key, table->tb_name); key = lbuf; break; case TB_ROOT: /* name -> official domain name (special case) */ case TB_CHANNEL: /* official domain name -> list of addresses */ break; default: #ifdef DEBUG ll_log(logptr, LLOGTMP, "unknown table type '%o'",type); #endif return(NOTOK); } /* end switch */ max_mxa = on_mxa = -1; if (!cachehit(key,(type==TB_CHANNEL?T_MX:T_CNAME),&rval)) { if (type == TB_CHANNEL) { if ((rval = ns_getmx(key, &max_mxa, mx_addrs, MAXADDR)) == OK) { on_mxa = 0; #ifdef NSCACHE cacheaddr(key,T_MX,max_mxa,mx_addrs); #endif } } else { rval = ns_getcn(key, dn_name, sizeof(dn_name)); #ifdef NSCACHE if (rval == OK) cachename(key,T_CNAME,dn_name); else if (rval == MAYBE) cachename(key,0,(char *)0); #endif } } if (rval != OK) { #ifdef DEBUG if (rval == MAYBE) ll_log(logptr, LLOGFTR, "nameserver query timed out"); else ll_log(logptr, LLOGFTR, "nameserver query failed"); #endif return(rval); } } /* O.K. now give answer */ switch (type) { case TB_CHANNEL: /* if NS failure we returned MAYBE above */ if ((max_mxa <= 0) || (on_mxa >= max_mxa)) return(NOTOK); tmp = (char *)&(mx_addrs[on_mxa++]); (void) sprintf(value,"%u.%u.%u.%u", ((unsigned)tmp[0]) & 0xff, ((unsigned)tmp[1]) & 0xff, ((unsigned)tmp[2]) & 0xff, ((unsigned)tmp[3]) & 0xff); break; default: /* can't get multiple names */ if (!first) return(NOTOK); /* give them the name */ (void) strcpy(value,dn_name); break; } #ifdef DEBUG ll_log(logptr, LLOGFTR, "NS returns '%s'", value); #endif return(OK); } /* * see if name is not cannonical name */ LOCFUN ns_getcn(key, name, namelen) char *key, *name; int namelen; { register int n; register char *cp; union querybuf qbuf; union ansbuf abuf; HEADER *hp; char *eom; extern char *ns_skiphdr(); #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_getcn(%s)",key); #endif n = res_mkquery(QUERY, key, C_IN, T_CNAME, (char *)0, 0, (char *)0, (char *)&qbuf, sizeof(qbuf)); /* what else can we do? */ if (n < 0) return(NOTOK); n = res_send((char *)&qbuf,n,(char *)&abuf, sizeof(abuf)); if (n < 0) { #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_getcn: bad return from res_send"); #endif return(MAYBE); } hp = (HEADER *)&abuf; if (hp->rcode != NOERROR) return(ns_error(hp)); if (ntohs(hp->ancount) == 0) { /* it is the official name */ (void) strncpy(name,key,namelen); #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_getcn: %s -> %s",key,name); #endif return(OK); } /* only get here on NOERRR with ancount != 0 */ #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_getcn: parsing answer to query"); #endif /* skip header */ eom = ((char *)&abuf)+n; if ((cp = ns_skiphdr((char *)&abuf, hp, eom)) == 0) return(MAYBE); /* one CNAME is enough */ if ((n = dn_expand((char *)&abuf,eom, cp, name, namelen))<0) return(MAYBE); /* skip to name */ cp += (n + (3 * sizeof(u_short)) + sizeof(u_long)); if ((n = dn_expand((char *)&abuf, eom, cp, name, namelen))<0) return(MAYBE); #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_getcn: %s -> %s",key,name); #endif return(OK); } /* * build a list of addresses of MX hosts to try.... */ LOCFUN ns_getmx(key, max, mxtab, tsize) char *key; int *max; struct in_addr mxtab[]; int tsize; { register char *cp; register int i, j, n; HEADER *hp; struct hostent *he; union querybuf qbuf; union ansbuf abuf; u_short type, dsize; int pref, localpref; int count, mxcount; int sawmx; /* are we actually processing mx's? */ char *eom; char buf[MAXDNAME]; /* for expanding in dn_expand */ char newkey[MAXDNAME]; /* in case we get a CNAME RR back... */ struct { /* intermediate table */ char *mxname; u_short mxpref; } mx_list[MAXMX]; extern char *ns_skiphdr(); #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_getmx(%s, %x, %x, %d)",key,max,mxtab,tsize); #endif restart: sawmx = 0; n = res_mkquery(QUERY, key, C_IN, T_MX, (char *)0, 0, (char *)0, (char *)&qbuf, sizeof(qbuf)); /* what else can we do? */ if (n < 0) return(NOTOK); #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_getmx: sending ns query (%d bytes)",n); #endif n = res_send((char *)&qbuf,n,(char *)&abuf, sizeof(abuf)); if (n < 0) { #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_getmx: bad return from res_send"); #endif return(MAYBE); } hp = (HEADER *)&abuf; #ifdef OLDSERVER if ((hp->rcode != NOERROR) && (hp->rcode != FORMERR)) #else if (hp->rcode != NOERROR) #endif /* OLDSERVER */ return(ns_error(hp)); #ifdef OLDSERVER if ((ntohs(hp->ancount) == 0) || (hp->rcode == FORMERR)) #else if (ntohs(hp->ancount) == 0) #endif /* OLDSERVER */ { mxcount = 1; mx_list[0].mxname = strdup(key); mx_list[0].mxpref = 0; goto doaddr; } /* read MX list */ sawmx = 1; count = ntohs(hp->ancount); /* need local machine name */ if (local[0] == '\0') { if ((locmachine != 0) && (*locmachine != '\0')) (void) sprintf(local,"%s.%s.%s",locmachine,locname,locdomain); else (void) sprintf(local,"%s.%s",locname,locdomain); } /* skip header */ eom = ((char *)&abuf) + n; if ((cp = ns_skiphdr((char *)&abuf, hp, eom))==0) return(MAYBE); /* get them MX's */ localpref = -1; mxcount = 0; *max = 0; #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_getmx: %d answers to query",count); #endif while ((cp < eom) && (count--)) { n = dn_expand((char *)&abuf,eom, cp, buf, sizeof(buf)); if (n < 0) goto quit; cp += n; type = getshort(cp); /* get to datasize */ cp += (2 * sizeof(u_short)) + sizeof(u_long); dsize = getshort(cp); cp += sizeof(u_short); /* * is it an MX ? * note it could be a CNAME if we didn't use a domain lookup */ if (type == T_CNAME) { ll_log(logptr,LLOGTMP,"ns_getmx: CNAME answer to MX query"); n = dn_expand((char *)&abuf,eom, cp, newkey, sizeof(newkey)); cp += dsize; if (n < 0) continue; /* pray? */ #ifdef DEBUG ll_log(logptr,LLOGFTR,"ns_getmx: %s -> %s (new query)",key,newkey); #endif DEBUG key = newkey; goto restart; } if (type != T_MX) { ll_log(logptr,LLOGTMP,"ns_getmx: RR of type %d in response",type); cp += dsize; continue; /* keep trying */ } pref = getshort(cp); cp += sizeof(u_short); n = dn_expand((char *)&abuf,eom, cp, buf, sizeof(buf)); if (n < 0) goto quit; cp += n; /* is it local? */ if ((lexequ(local,buf)) && ((localpref < 0) || (pref < localpref))) { localpref = pref; for(i=(mxcount-1); i >= 0; i--) { if (mx_list[i].mxpref < localpref) break; (void) free(mx_list[i].mxname); mxcount--; } continue; } /* now, see if we keep it */ if ((localpref >= 0) && (pref >= localpref)) continue; /* find where it belongs */ for(i=0; i < mxcount; i++) if (mx_list[i].mxpref > pref) break; /* not of interest */ if (i == MAXMX) continue; /* shift stuff to make space */ for(j=mxcount-1; j > i; j--) { if (j==(MAXMX-1)) (void) free(mx_list[j].mxname); mx_list[j].mxname = mx_list[j-1].mxname; mx_list[j].mxpref = mx_list[j-1].mxpref; } mx_list[i].mxname = strdup(buf); mx_list[i].mxpref = pref; if (mxcount <= i) mxcount = i + 1; } /* * should read additional RR section for addresses and cache them * but let's hold on that. */ doaddr: /* now build the address list */ #ifdef DEBUG ll_log(logptr, LLOGFTR,"ns_getmx: using %d mx hosts",mxcount); #endif for(i=0,j=0; (i < mxcount) && (j < tsize); i++) { /* * note that gethostbyname() is slow -- we should cache so * we don't ask for an address repeatedly */ he = gethostbyname(mx_list[i].mxname); if (he == 0) { #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_getmx: no addresses for %s", mx_list[i].mxname); #endif /* nope -- were trying special case and no address */ if ((!sawmx) && (h_errno != TRY_AGAIN)) return(NOTOK); continue; } for(n=0; (j < tsize) && (n < MAXADDR_PER); n++, j++) { if (he->h_addr_list[n] == 0) break; bcopy(he->h_addr_list[n],(char *)&mxtab[j],sizeof(struct in_addr)); } #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_getmx: %d addresses saved for %s", n, mx_list[i].mxname); #endif } *max = j; quit: for(i=0; i < mxcount; i++) (void) free(mx_list[i].mxname); #ifdef DEBUG if (*max == 0) ll_log(logptr, LLOGFTR, "ns_getmx: problems parsing response to query"); #endif return (*max == 0 ? MAYBE : OK); } /* * figure out proper error code to return given an error */ LOCFUN ns_error(hp) register HEADER *hp; { #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_error: server returned code %d",hp->rcode); #endif switch (hp->rcode) { case NXDOMAIN: return(NOTOK); /* even if not authoritative */ case SERVFAIL: return(MAYBE); default: break; } return(NOTOK); } /* * skip header of query and return pointer to first answer RR. */ LOCFUN char *ns_skiphdr(answer, hp, eom) char *answer; HEADER *hp; register char *eom; { register int qdcount; register char *cp; register int n; char tmp[MAXDNAME]; qdcount = ntohs(hp->qdcount); cp = answer + sizeof(HEADER); while ((qdcount-- > 0) && (cp < eom)) { n = dn_expand(answer,eom,cp,tmp,sizeof(tmp)); if (n < 0) return(0); cp += (n + QFIXEDSZ); } return((cp < eom)? cp : 0); } /* * routine to set the resolver timeouts * takes maximum number of seconds you are willing to wait */ ns_settimeo(ns_time) int ns_time; { register int i; register int retry, retrans; static int called = 0; static struct state oldres; if ((_res.options & RES_INIT) == 0) res_init (); /* always start afresh */ if (called) { bcopy((char *)&oldres,(char *)&_res,sizeof(oldres)); } else { called = 1; bcopy((char *)&_res,(char *)&oldres,sizeof(oldres)); } /* too small? */ if (ns_time <= (NS_MINTIME)) { retry = NS_MINRETRY; retrans = NS_MINRETRANS; goto done; } retry = _res.retry; retrans = _res.retrans; /* bigger than bind? */ if (ns_time > (retry * retrans)) { /* can't increase servers, and no point to retrans so up retry a bit */ while ((retry < NS_MAXRETRY) && (ns_time > ((retry+1)*retrans))) retry++; goto done; } /* reduce timer periods */ for(i=0; ns_time < (retry * retrans); i++) { switch (i%2) { case 0: /* cut down rexmits first */ if (retry > NS_MINRETRY) { retry--; continue; } /* fall thru */ case 1: /* then interval between rexmits */ if (retrans > NS_MINRETRANS) { retrans /= 2; if (retrans < NS_MINRETRANS) retrans = NS_MINRETRANS; } continue; } } done: _res.retrans = retrans; _res.retry = retry; #ifdef DEBUG ll_log(logptr, LLOGFTR, "ns_timeo: servers(%d), retrans(%d), retry(%d)", _res.nscount, _res.retrans, _res.retry); #endif } /* * Caching stuff starts here.... * cache stores following pairs: * key -> mx address list * key -> canonical name * key -> we timed out on * * must store complete answers to a table fetch -- storing by RR's * leads to incomplete information in the cache -- and thus busted * routing. */ /* * see if key/type pair is in cache. If so, set rval appropriately */ cachehit(key,type,rval) char *key; int type; int *rval; { #ifdef NSCACHE register int i; register struct ns_cache *cp; register struct in_addr *ip1, *ip2; *rval = OK; for(i=0; i < NSCACHE; i++) { cp = cachedata[i]; if (cp->nc_type && (cp->nc_type != type)) continue; if (!lexequ(key,cp->nc_key)) continue; /* got a hit */ cp->nc_time = cachetime++; #ifdef DEBUG ll_log(logptr,LLOGFTR,"ns: cache hit, type %d",cp->nc_type); #endif /* DEBUG */ switch (cp->nc_type) { case 0: /* key we timed out on */ *rval = MAYBE; return(1); case T_MX: /* fill mx cache */ max_mxa = cp->nc_count; on_mxa = 0; ip1 = (struct in_addr *)cp->nc_data; ip2 = mx_addrs; for(i=0; i < max_mxa; i++) *ip2++ = *ip1++; #ifdef DEBUG ll_log(logptr,LLOGFTR,"ns: found %d addresses for %s in cache", max_mxa, key); #endif return(1); case T_CNAME: /* fill in dn_name */ if (cp->nc_count) (void) strcpy(dn_name,cp->nc_data); else (void) strcpy(dn_name,cp->nc_key); return(1); } } #endif /* NSCACHE */ /* no one should look at rval, but... */ *rval = NOTOK; return(0); } #ifdef NSCACHE cacheput(cp) struct ns_cache *cp; { register int i; register int d; cp->nc_time = cachetime++; #ifdef DEBUG ll_log(logptr,LLOGFTR,"ns: cache put (key=%s, type=%d, count=%d)", cp->nc_key, cp->nc_type, cp->nc_count); #endif for(d=0,i=0; i < NSCACHE; i++) { if (cachedata[i] == 0) { cachedata[i] = cp; return; } if (cachedata[i]->nc_time < cachedata[d]->nc_time) d = i; } #ifdef DEBUG ll_log(logptr,LLOGFTR,"ns: full cache, discarding (%s,%d)", cp->nc_key, cp->nc_type); #endif /* must delete someone */ (void) free(cachedata[d]->nc_key); if (cachedata[d]->nc_count) (void) free(cachedata[d]->nc_data); (void) free((char *)cachedata[d]); /* put new one in */ cachedata[d] = cp; } cacheaddr(key,type,count,list) char *key; int type, count; struct in_addr *list; { register struct ns_cache *cp; register struct in_addr *ip1, *ip2; extern char *malloc(), *calloc(); /* NOSTRICT */ if ((cp = (struct ns_cache *)malloc(sizeof(*cp)))==0) return; /* NOSTRICT */ if ((cp->nc_data = calloc((unsigned)count,sizeof(*list)))==0) { (void) free((char *)cp); return; } if ((cp->nc_key = strdup(key))==0) { (void) free(cp->nc_data); (void) free((char *)cp); return; } cp->nc_count = count; cp->nc_type = type; ip1 = list; ip2 = (struct in_addr *)cp->nc_data; while (count--) *ip2++ = *ip1++; cacheput(cp); } cachename(key,type,name) char *key, *name; int type; { register struct ns_cache *cp; extern char *malloc(), *strdup(); /* NOSTRICT */ if ((cp = (struct ns_cache *)malloc(sizeof(*cp)))==0) return; if ((cp->nc_key = strdup(key))==0) { (void) free((char *)cp); return; } if ((name != 0) && !lexequ(key,name)) { if ((cp->nc_data = strdup(key)) == 0) { (void) free(cp->nc_key); (void) free((char *)cp); return; } cp->nc_count = 1; } else { cp->nc_data = 0; cp->nc_count = 0; } cp->nc_type = type; cacheput(cp); } #endif NSCACHE ; } /* fall thru */ mmdf/lib/util/ 755 0 12 0 3671074623 6421 mmdf/lib/util/getllog.bsd.c 444 0 12 2753 3620510412 11044 #include "util.h" #include <pwd.h> #include <utmp.h> #include <lastlog.h> /* get informtion about who is logged in */ extern char *blt(); LOCVAR char LLPATH[] = "/usr/adm/lastlog"; LOCVAR FILE *llogfp = NULL; LOCVAR struct utmp llogentry; LOCVAR int lloguid; /* keep track of current uid */ int setllog() { if( llogfp == NULL ) { llogfp = fopen( LLPATH, "r" ); if ( llogfp == NULL ) return(NOTOK); } else rewind( llogfp ); lloguid = 0; return (OK); } endllog() { if( llogfp != NULL ){ fclose( llogfp ); llogfp = NULL; } } struct utmp * getllog() { union tmpunion { char io[sizeof (struct lastlog)]; struct lastlog entry; } llogbuf; struct passwd *pwdptr, *getpwuid (); if (llogfp == NULL) { if( (llogfp = fopen( LLPATH, "r" )) == NULL ) return(0); lloguid = 0; } if (fread (llogbuf.io, sizeof (struct lastlog), 1, llogfp) != 1) return(0); llogentry.ut_time = (long) llogbuf.entry.ll_time; blt (llogbuf.entry.ll_line, llogentry.ut_line, 8); if ((pwdptr = getpwuid (lloguid)) == 0) strcpy (llogentry.ut_name, "????"); else blt (pwdptr -> pw_name, llogentry.ut_name, 8); lloguid++; return(&llogentry); } struct utmp * getllnam(name) char name[]; { struct passwd *pwdptr, *gpwlnam (); if ((pwdptr = gpwlnam (name)) == 0) return (0); if (setllog () == NOTOK) return(NULL); lloguid = pwdptr->pw_uid; fseek(llogfp, (long)pwdptr->pw_uid * sizeof (struct lastlog), 0); return (getllog ()); } { if( llogfp != NULLmmdf/lib/util/gen 555 0 12 57 3620510412 7126 make -f ../../Makefile.com -f Makefile.real $* strcpy (llogentry.ut_name, "????"); else blt (pwdptr -> pw_name, llogentry.ut_name, 8); lloguid++; return(&llogentry); } struct utmp * getllnam(name) char name[]; { struct passwd *pwdptr, *gpwlnam (); if ((pwdptr = gpwlnam (name)) == 0) return (0); if (setllog () == NOTOK) return(NULL); lloguid = pwdptr->pw_uid; fseek(llogfp, (long)pwdptr->pw_uid * sizeof (struct lastlog), 0); return (getllog ()); } { if( llogfp != NULLmmdf/lib/util/tai_file.c 444 0 12 3751 3671073204 10424 #include "util.h" #include <sys/stat.h> /* Perform process start-up tailoring */ extern int errno; char *tai_eptr; /* point to error text, if any */ LOCVAR char *tai_data; /* pointer to incore copy of tai file */ LOCVAR char *tai_ptr; /* pointer to "next" tai record */ tai_init (filename) /* prepare to get tailoring information */ char filename[]; /* location of the info */ { extern char *malloc (); struct stat statbuf; unsigned taisize; /* number of bytes in the tai file */ int fd; if (stat (filename, &statbuf) < OK) return (NOTOK); taisize = (unsigned) st_gsize (&statbuf); if ((tai_data = malloc (taisize + 1)) == 0) return (NOTOK); if ((fd = open (filename, 0)) < 0) return (NOTOK); if (read (fd, tai_data, taisize) != taisize) { (void) close (fd); errno = EIO; /* i/o error */ return (NOTOK); } tai_data[taisize] = '\0'; (void) close (fd); tai_ptr = tai_data; return (OK); } tai_end () { tai_data = 0; return (OK); } /**/ tai_get (max, argv) /* get next parsed tailoring line */ int max; /* maximum number of allowable fields */ char *argv[]; /* array to hold pointers to field elements */ { int retval; register char *nxtptr; /* beginning of next record */ retry: if (tai_ptr == 0) return (0); for (nxtptr = tai_ptr; *nxtptr != '\0'; nxtptr++) if (*nxtptr == '\n') { /* skip over to the next record */ *nxtptr = ' '; /* no-op the newline */ nxtptr++; if (*nxtptr != ' ' && *nxtptr != '\t') { /* continuation line */ nxtptr[-1] = '\0'; break; } } retval = str2arg (tai_ptr, max, argv, (char *) 0); if (retval < 0) tai_eptr = tai_ptr; /* record where the error happened */ if (*nxtptr == '\0') tai_ptr = 0; else tai_ptr = nxtptr; if (retval == 0) /* nothing but comments on line */ goto retry; /* get something useful */ return (retval); } /* fall thru */ mmdf/lib/util/tai_noop.c 444 0 12 1116 3620510413 10441 #include "util.h" /* Perform process start-up tailoring */ /* This version is a No-Op. It presumes that the tailoring * information has been compiled-in and that an external * file is not to be consulted. */ tai_init (filename) /* prepare to get tailoring information */ char filename[]; /* location of the info */ { return (OK); } tai_get (max, argv) /* get next parsed tailoring line */ int max; /* maximum number of allowable fields */ char *argv[]; /* array to hold pointers to field elements */ { return (0); /* return end-of-list */ } tai_end () { return (OK); } t_gsize (&statbuf); if ((tai_data = malloc (taisize + 1)) == 0) return (NOTOK); if ((fd = open (filename, 0)) < 0) return (NOTOK); if (read (fd, tai_data, taisize) != taisize) { (void) close (fd); errno = EIO; /* i/o error */ return (NOTOK); } tai_data[taisize] = '\0'; (void) close (fd); tai_ptr = tai_data; return (OK); } tai_end () { tai_data = 0; return (OK); } /**/ tai_get (max, argv) /* get next parmmdf/lib/util/blt.c 444 0 12 625 3620510413 7376 # /* Copy the given string of the given length to the given */ /* destination string. */ /* */ char * blt (from, to, length) register char *from, *to; register int length; { if(length != 0) do { *to++ = *from++; } while(--length); return (to); } t max; /* maximum number of allowable fields */ char *argv[]; /* array to hold pointers to field elements mmdf/lib/util/chrcnv.c 444 0 12 3162 3620510413 10117 # char /* character conversion table */ chrcnv[] = /* lower to upper case letters */ { '\0', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\10', '\t', '\n', '\13', '\14', '\r', '\16', '\17', '\20', '\21', '\22', '\23', '\24', '\25', '\26', '\27', '\30', '\31', '\32', '\33', '\34', '\35', '\36', '\37', ' ', '!', '"', '#', '$', '%', '&', '\47', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', '\177', '\0', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\10', '\t', '\n', '\13', '\14', '\r', '\16', '\17', '\20', '\21', '\22', '\23', '\24', '\25', '\26', '\27', '\30', '\31', '\32', '\33', '\34', '\35', '\36', '\37', ' ', '!', '"', '#', '$', '%', '&', '\47', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', '\177' }; 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', '\177', '\0', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\10', '\t', '\n', '\13', '\14', '\r', '\16', '\17', '\20', '\21', '\22', '\23'mmdf/lib/util/cnvtdate.c 444 0 12 6753 3671073205 10466 #include "util.h" #include "conf.h" #include "cnvtdate.h" #ifndef SYS5 #include <sys/timeb.h> #endif SYS5 #ifdef SYS5 #define timezone envtimezone #endif SYS5 #ifdef ALTOS #define timezone mytimezone #endif ALTOS char * cnvtdate (flag, datbuf) /* produce a date/time string */ int flag; /* date format option value */ char *datbuf; { static char *day[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; #ifndef SYS5 struct timeb timeb; #else SYS5 time_t tsec; #endif SYS5 extern char *timezone(); extern struct tm *localtime (); register struct tm *i; #ifndef SYS5 ftime (&timeb); i = localtime ((time_t *) & timeb.time); #else SYS5 time (&tsec); i = localtime (&tsec); #endif SYS5 switch (flag) { case TIMCOM: sprintf (datbuf, "%02d%02d%02d%02d%02d", i -> tm_year, i -> tm_mon + 1, i -> tm_mday, i -> tm_hour, i -> tm_min); break; case TIMSECS: /* w/seconds dd mon yy hh:mm:ss */ sprintf (datbuf, "%d %s %d %d:%02d:%02d", i -> tm_mday, month[i -> tm_mon], i -> tm_year, i -> tm_hour, i -> tm_min, i -> tm_sec); break; case TIMREG: /* RFC 822 standard time string */ default: /* "Wed, 21 Jan 76 14:30 PDT" */ sprintf (datbuf, "%s, %d %s %d %d:%02d:%02d %s", day[i -> tm_wday], i -> tm_mday, month[i -> tm_mon], i -> tm_year, i -> tm_hour, i -> tm_min, i -> tm_sec, #ifndef SYS5 timezone (timeb.timezone, i -> tm_isdst)); #else timezone (0, i -> tm_isdst)); #endif SYS5 break; case TIMSHRT: /* w/out day of week */ sprintf (datbuf, "%d %s %d %d:%02d %s", i -> tm_mday, month[i -> tm_mon], i -> tm_year, i -> tm_hour, i -> tm_min, #ifndef SYS5 timezone (timeb.timezone, i -> tm_isdst)); #else timezone (0, i -> tm_isdst)); #endif SYS5 break; } return(datbuf); } #ifdef SYS5 char * envtimezone( ignored, dst ) int ignored; int dst; { extern char *tzname[2]; static int doneit = 0; static char lcltz[8] = "???"; extern char *strncpy(); char *cp; if (!doneit) { doneit++; tzset(); } cp = tzname[dst ? 1 : 0]; if (cp == (char *)0 || *cp == '\0') cp = "???"; (void) strncpy(lcltz, cp, sizeof(lcltz) - 1); return(lcltz); } #endif SYS5 #ifdef ALTOS /* sigh */ #define abs(a) ((a) >= 0 ? (a) : -(a)) struct keywd { char *key; int value; }; #define NZONES (sizeof (zones)/sizeof (struct keywd)) static struct keywd zones[] = { /* order sensitive */ "UT", 0, "EST", -5, "CST", -6, "MST", -7, "PST", -8, "PDT", -9, "A", -1, "B", -2, "C", -3, "D", -4, "E", -5, "F", -6, "G", -7, "H", -8, "I", -9, "K", -10, "L", -11, "M", -12, "N", 1, "O", 2, "P", 3, "Q", 4, "R", 5, "S", 6, "T", 7, "U", 8, "V", 9, "W", 10, "X", 11, "Y", 12 }; char *mytimezone (zone, dst) int zone, dst; { int i, j, hours, mins; static char buffer[10]; zone = 0 - zone; if (zone < 0) { mins = -((-zone) % 60); hours = -((-zone) / 60); } else { mins = zone % 60; hours = zone / 60; } if (dst) hours -= 1; if (mins == 0) for (i = 0, j = NZONES; i < j; i++) if (zones[i].value == hours) { (void) strcpy (buffer, zones[i].key); if (dst && buffer[1] == 'S') buffer[1] = 'D'; return buffer; } sprintf (buffer, "%s%02d%02d", zone < 0 ? "-" : "+", abs (hours), abs (mins)); return buffer; } #endif ALTOS > tm_hour, i -> tm_mimmdf/lib/util/compress.c 444 0 12 1205 3620510414 10464 #include "util.h" /* compress-out redundant linear white space & strip trailing lwsp */ char * compress (fromptr, toptr) register char *fromptr, *toptr; { register char chr; chr = ' '; /* init to skip leading spaces */ while ((*toptr = *fromptr++) != '\0') { /* convert newlines and tabs to */ if (isspace (*toptr)) { /* only save first space */ if (chr != ' ') *toptr++ = chr = ' '; } else chr = *toptr++; } if (chr == ' ') /* remove trailing space if any */ *--toptr = '\0'; return (toptr); } / 60; } if (dst) hours -= 1; if (mins == 0) for (i = 0, j = NZONES; i < j; i++) if (zones[i].value == hours) { (void) strcpy (buffer, zones[i].key); if (dst && buffer[1] == 'S') buffer[1] = 'D'; return buffer; } sprintf (buffer, "%s%02d%02d", zone < 0 ? "-" : "+", abs (hours), abs (mins)); return buffer; } #endif ALTOS > tm_hour, i -> tm_mimmdf/lib/util/endstr.c 444 0 12 750 3620510414 10114 /* check for EXACT match of suffix string at end of thestring */ endstr (suffix, thestring, suflen) register char *suffix, *thestring; register short suflen; { /* does str end with suffix string? */ short thelength; if (suflen < 0) suflen = strlen (suffix); thelength = strlen (thestring); if (suflen > thelength) return (0); return (initstr (suffix, &thestring[thelength - suflen], suflen)); } hr = ' '; } else mmdf/lib/util/equal.c 444 0 12 774 3620510414 7732 /* */ /* Determine whether the two given strings have an identical */ /* prefix of the given length. */ /* */ equal (str1, str2, length) register char *str1, *str2; short length; { extern char chrcnv[]; while (chrcnv[*str1++] == chrcnv[*str2++] && --length > 0); return (length == 0); } mmdf/lib/util/equwrd.c 444 0 12 1065 3620510414 10144 #include "util.h" /* */ /* Determine whether the two given strings begin with the same */ /* alphabetic prefix of the given length. */ /* */ equwrd (wrd1, wrd2, length) register char *wrd1, *wrd2; int length; { extern char chrcnv[]; while (chrcnv[*wrd1++] == chrcnv[*wrd2++] && --length > 0); return (length == 0 && !isalpha (*wrd1) && !isalpha (*wrd2)); } /* remove trailing space if any */ *--toptr = '\0'; return (toptr); } / 60; } if (dst) hours -= 1; if (mins == 0) for (i = 0, j = NZONES; i < j; i++) if (zones[i].value == hours) { (void) strcpy (buffer, zones[i].key); if (dst && buffer[1] == 'S') buffer[1] = 'D'; return buffer; } sprintf (buffer, "%s%02d%02d", zone < 0 ? "-" : "+", abs (hours), abs (mins)); return buffer; } #endif ALTOS > tm_hour, i -> tm_mimmdf/lib/util/gcread.c 444 0 12 5320 3620510417 10063 #include "util.h" /* Modified gcread to use stdio. Jim Lieb SRI International Aug 1980 */ /* returns -1 for error, 0 for eof, positive with count for success */ /* Successive calls will return error or eof after first encounter */ /* Neither eof nor error will be indicated until buffer is emptied */ /* Modified by Phil Cockcroft UCL Mar 85 to handle quoted strings properly */ gcread (fp, obuf, len, endchrs) FILE *fp; int len; /* maximum allowed to xfer */ char *obuf, /* destination buffer */ *endchrs; /* list of chars to terminate on */ /* "abcdef\377" */ { return (qread (fp, obuf, len, endchrs, 0)); } qread (fp, obuf, len, endchrs, quotechar) FILE *fp; int len; /* maximum allowed to xfer */ char *obuf, /* destination buffer */ *endchrs; /* list of chars to terminate on */ /* "abcdef\377" */ char quotechar; /* a single character quote */ { char *bptr; register int c; register short cnt; register char *end; if (feof (fp)) return (OK); if (ferror (fp)) return (NOTOK); for (bptr = obuf, cnt = 0; cnt < len; cnt++) { if ((c = getc(fp)) == EOF) /* maybe end-of-file */ { if (ferror (fp)) return ((cnt == 0) ? NOTOK : cnt); if (feof (fp)) return (cnt); } if(quotechar != '\0' && c == quotechar) { if ((c = getc(fp)) == EOF) /* maybe end-of-file */ { if (bptr != 0) /* what do we do here ???? */ *bptr = quotechar; if (ferror (fp)) return ((cnt == 0) ? NOTOK : cnt); if (feof (fp)) return (cnt); /* EH ? I hope we will never get here */ } if (bptr != 0) /* 0 => just skip the text */ *bptr++ = c; /* xfer one char */ continue; } if (bptr != 0) /* 0 => just skip the text */ *bptr++ = c; /* xfer one char */ if (end = endchrs) /* check if it's in breakset */ while ((int)*end != (-1)) /* Grrrrrr.... */ if (c == *end++) return ++cnt; } return (cnt); /* return num xferred */ } register int c; register short cnt; register char *end; if (feof (fp)) return (OK); if (ferror (fp)) return (NOTOK); for (bptr = obuf, cnt = 0; cnt < len; cnt++) { if ((c = getc(fp)) == EOF) /* maybe end-of-file */ { if (mmdf/lib/util/initstr.c 444 0 12 523 3620510417 10312 /* Check for EXACT match prefix string at beginning of thestring */ initstr (prefix, thestring, preflen) register char *prefix, *thestring; register short preflen; { if (preflen < 0) preflen = strlen (prefix); while (*prefix++ == *thestring++) if (--preflen == 0) return (1); return (0); } here ???? */ *bptr = quotechar; if (ferror (fp)) return ((cnt == 0) ? NOTOK : cnt); if (feof (fp)) mmdf/lib/util/lexequ.c 444 0 12 751 3620510417 10124 #include "util.h" /* */ /* Determine if the two given strings are equivalent. */ /* */ lexequ (str1, str2) register char *str1, *str2; { extern char chrcnv[]; if(str1 == 0 || str2 == 0) return (str1 == str2); while (chrcnv[*str1] == chrcnv[*str2++]) if (*str1++ == 0) return (TRUE); return (FALSE); } if (feof (fp)) mmdf/lib/util/lexrel.c 444 0 12 623 3620510417 10112 lexrel (str1, str2) register char *str1, *str2; { extern char chrcnv[]; while (chrcnv[*str1] == chrcnv[*str2++]) if (*str1++ == '\0') return (0); /* exactly equal */ if (chrcnv[*str1] < chrcnv[*--str2]) return (-1); /* str1 earlier in lexicon */ return (1); /* str2 is earlier */ } r1] == chrcnv[*str2++]) if (*str1++ == 0) return (TRUE); return (FALSE); } if (feof (fp)) mmdf/lib/util/Makefile.real 444 0 12 12007 3635173721 11105 # # Makefile for MMDF general utility routines # MODULES = tty_io ll_err ll_log tai_packages cmdsrch \ cnvtdate creatdir getfpath getwho \ arg2str str2arg cstr2arg gpwlnam ggrlnam \ strindex equwrd equal endstr initstr lexrel \ prefix strequ lexequ chrcnv getutmp getgroup \ compress multcpy multcat strdup gcread gettys \ zero blt expand sstr2arg nexec \ tai_file tai_noop \ gwdir gwdir.4.2 \ getllog.v7 getllog.bsd getllog.fak STANDARD = tty_io.o ll_err.o ll_log.o tai_packages.o cmdsrch.o \ cnvtdate.o creatdir.o getfpath.o getwho.o \ arg2str.o str2arg.o cstr2arg.o gpwlnam.o ggrlnam.o \ strindex.o equwrd.o equal.o endstr.o initstr.o lexrel.o \ prefix.o strequ.o lexequ.o chrcnv.o getutmp.o getgroup.o \ compress.o multcpy.o multcat.o strdup.o gcread.o gettys.o \ zero.o blt.o expand.o sstr2arg.o nexec.o STDSRCS = tty_io.c ll_err.c ll_log.c tai_packages.c cmdsrch.c \ cnvtdate.c creatdir.c getfpath.c getwho.c \ arg2str.c str2arg.c cstr2arg.c gpwlnam.c ggrlnam.c \ strindex.c equwrd.c equal.c endstr.c initstr.c lexrel.c \ prefix.c strequ.c lexequ.c chrcnv.c getutmp.c getgroup.c \ compress.c multcpy.c multcat.c strdup.c gcread.c gettys.c \ zero.c blt.c expand.c sstr2arg.c nexec.c v7objs = getllog.v7.o gwdir.o v7srcs = getllog.v7.c gwdir.c 5.2objs = getllog.fak.o gwdir.o 5.2srcs = getllog.fak.c gwdir.c 4.1objs = getllog.bsd.o gwdir.o 4.1srcs = getllog.bsd.c gwdir.c 4.2objs = getllog.bsd.o gwdir.4.2.o 4.2srcs = getllog.bsd.c gwdir.4.2.c real-default: ../util-made ../util-made: ${STANDARD} ${LOCALUTIL} ../${SYSTEM}-made ar r ../libmmdf.a ${STANDARD} ${LOCALUTIL} -touch ../util-made ../v7-made: ${v7objs} ar r ../libmmdf.a ${v7objs} -touch ../v7-made ../5.2-made: ${5.2objs} ar r ../libmmdf.a ${5.2objs} -touch ../5.2-made ../4.1-made: ${4.1objs} ar r ../libmmdf.a ${4.1objs} -touch ../4.1-made ../4.2-made: ${4.2objs} ar r ../libmmdf.a ${4.2objs} -touch ../4.2-made lint: std-lint ${SYSTEM}-lint cat llib-lutila* llib-lutilb* > llib-lutil.ln std-lint: $(LINT) -Cutila $(LFLAGS) $(STDSRCS) v7-lint: $(LINT) -Cutilb $(LFLAGS) $(v7srcs) 5.2-lint: $(LINT) -Cutilb $(LFLAGS) $(5.2srcs) 4.1-lint: $(LINT) -Cutilb $(LFLAGS) $(4.1srcs) 4.2-lint: $(LINT) -Cutilb $(LFLAGS) $(4.2srcs) clean: -rm -f *.o x.c makedep eddep make.out errs core llib-lutil* # DO NOT DELETE THIS LINE -- make depend uses it tty_io.o: tty_io.c tty_io.o: ../../h/util.h tty_io.o: /usr/include/pwd.h tty_io.o: /usr/include/sys/stat.h tty_io.o: /usr/include/utmp.h ll_err.o: ll_err.c ll_err.o: /usr/include/stdio.h ll_err.o: ../../h/ll_log.h ll_log.o: ll_log.c ll_log.o: ../../h/util.h ll_log.o: ../../h/conf.h ll_log.o: /usr/include/sys/stat.h ll_log.o: ../../h/ll_log.h tai_packages.o: tai_packages.c tai_packages.o: ../../h/util.h tai_packages.o: ../../h/ll_log.h tai_packages.o: ../../h/cmd.h cmdsrch.o: cmdsrch.c cmdsrch.o: ../../h/util.h cmdsrch.o: ../../h/cmd.h cnvtdate.o: cnvtdate.c cnvtdate.o: ../../h/util.h cnvtdate.o: ../../h/conf.h cnvtdate.o: ../../h/cnvtdate.h cnvtdate.o: /usr/include/sys/timeb.h creatdir.o: creatdir.c creatdir.o: ../../h/util.h creatdir.o: /usr/include/sys/stat.h getfpath.o: getfpath.c getfpath.o: ../../h/util.h getwho.o: getwho.c arg2str.o: arg2str.c arg2str.o: ../../h/util.h str2arg.o: str2arg.c str2arg.o: ../../h/util.h cstr2arg.o: cstr2arg.c cstr2arg.o: ../../h/util.h gpwlnam.o: gpwlnam.c gpwlnam.o: ../../h/util.h gpwlnam.o: /usr/include/pwd.h ggrlnam.o: ggrlnam.c ggrlnam.o: ../../h/util.h ggrlnam.o: /usr/include/grp.h strindex.o: strindex.c equwrd.o: equwrd.c equwrd.o: ../../h/util.h equal.o: equal.c endstr.o: endstr.c initstr.o: initstr.c lexrel.o: lexrel.c prefix.o: prefix.c prefix.o: ../../h/util.h strequ.o: strequ.c strequ.o: ../../h/util.h lexequ.o: lexequ.c lexequ.o: ../../h/util.h chrcnv.o: chrcnv.c getutmp.o: getutmp.c getutmp.o: ../../h/util.h getutmp.o: /usr/include/utmp.h getgroup.o: getgroup.c compress.o: compress.c compress.o: ../../h/util.h multcpy.o: multcpy.c multcat.o: multcat.c multcat.o: ../../h/util.h strdup.o: strdup.c strdup.o: ../../h/util.h gcread.o: gcread.c gcread.o: ../../h/util.h gettys.o: gettys.c gettys.o: /usr/include/stdio.h gettys.o: ../../h/gettys.h zero.o: zero.c blt.o: blt.c expand.o: expand.c expand.o: ../../h/util.h sstr2arg.o: sstr2arg.c sstr2arg.o: ../../h/util.h nexec.o: nexec.c nexec.o: ../../h/util.h nexec.o: ../../h/nexec.h nexec.o: /usr/include/signal.h nexec.o: /usr/include/fcntl.h tai_file.o: tai_file.c tai_file.o: ../../h/util.h tai_file.o: /usr/include/sys/stat.h tai_noop.o: tai_noop.c tai_noop.o: ../../h/util.h gwdir.o: gwdir.c gwdir.o: ../../h/conf.h gwdir.o: ../../h/util.h gwdir.o: /usr/include/sys/stat.h gwdir.4.2.o: gwdir.4.2.c gwdir.4.2.o: ../../h/util.h getllog.v7.o: getllog.v7.c getllog.v7.o: ../../h/util.h getllog.v7.o: /usr/include/utmp.h getllog.bsd.o: getllog.bsd.c getllog.bsd.o: ../../h/util.h getllog.bsd.o: /usr/include/pwd.h getllog.bsd.o: /usr/include/utmp.h getllog.bsd.o: /usr/include/lastlog.h getllog.fak.o: getllog.fak.c # DEPENDENCIES MUST END AT END OF FILE # IF YOU PUT STUFF HERE IT WILL GO AWAY # see make depend above AGS) $(STDSRCS) v7-lint: $(LINT) -Cutilb $(LFLAGS) $(v7srcs) 5.2-lint: $(LINT) -Cutilb $(LFLAGS) $(5.2srcs) 4.1-lint: $(LINT) -Cutilb $(LFLAGS) $(4.1srcs) 4.2-lint: $(LINT) -Cutilb $(LFLAGS) $(4.2srcs) clean: -rm -f *.o x.c makedep eddep make.out errs core llib-lutil* # DO NOT DELETE THIS LINE -- make depend uses it tty_io.o: tty_io.c tty_io.o: ../../h/util.h tty_io.o: /usr/include/pwd.h tty_io.o: /usr/include/sys/stat.h tty_io.o: /usr/include/utmp.h ll_err.o: ll_err.c ll_err.o: /usr/incmmdf/lib/util/multcat.c 444 0 12 1214 3664425166 10323 #include "util.h" #include <varargs.h> /* */ /* Concatenate a series of arrays */ /*VARARGS0*/ char *multcat (va_alist) va_dcl { register va_list ap; register char *oldstr, *ptr; char *newstr; extern char *malloc(); unsigned newlen; va_start(ap); for (newlen = 1; oldstr = va_arg(ap, char *);) newlen += strlen (oldstr); va_end(ap); if ((ptr = newstr = malloc (newlen+1)) == 0) return((char *)0); va_start(ap); for (; oldstr = va_arg(ap, char *); ptr--) while(*ptr++ = *oldstr++); va_end(ap); return (newstr); } requ.c strequ.o: ../../h/util.h lexequ.o: lexequ.c lexequ.o: ../../h/util.h chrcnv.o: chrcnv.c getutmp.o: getutmp.c getutmp.o: ../../h/util.h getutmp.o: /usr/include/utmp.h getgroup.o: getgroup.c compress.o: compress.c compress.o: ../../h/util.h multcpy.o: multcpy.c multcat.o: multcat.c multcat.o: ../../h/util.h strdup.o: strdup.c strdup.o: ../../h/util.h gcread.o: gcremmdf/lib/util/multcpy.c 444 0 12 1017 3657737705 10357 #include <varargs.h> /* */ /* Copy the list of strings to the given location. */ /* */ char * multcpy (to, va_alist) register char *to; va_dcl { register va_list ap; register char *from; va_start(ap); while(from = va_arg(ap, char *)){ while(*to++ = *from++); to--; } va_end(ap); return (to); /* return pointer to end of str */ } ; va_start(ap); for (; oldstr = va_arg(ap, char *); ptr--) while(*ptr++ = *oldstr++); va_end(ap); return (newstr); } requ.c strequ.o: ../../h/util.h lexequ.o: lexequ.c lexequ.o: ../../h/util.h chrcnv.o: chrcnv.c getutmp.o: getutmp.c getutmp.o: ../../h/util.h getutmp.o: /usr/include/utmp.h getgroup.o: getgroup.c compress.o: compress.c compress.o: ../../h/util.h multcpy.o: multcpy.c multcat.o: multcat.c multcat.o: ../../h/util.h strdup.o: strdup.c strdup.o: ../../h/util.h gcread.o: gcremmdf/lib/util/gwdir.c 444 0 12 7414 3671073206 7766 #include "conf.h" #include "util.h" #include <sys/stat.h> LOCVAR char wd_DOT[] = "."; LOCVAR char wd_DOTDOT[] = ".."; LOCVAR char wd_read[] = "r"; LOCVAR char wd_name[128]; /* max size of path string */ LOCVAR jmp_buf wd_retloc; LOCVAR union wd /* unioned for making i/o cleaner */ { char io[sizeof (struct direct)]; struct direct dir; } wd_entry; LOCVAR FILE * wd_fp; char * gwdir () /* what is current working directory? */ { struct stat wd_stat; short lastd_ino; /* last inode num */ char buf[BUFSIZ]; if (!setjmp (wd_retloc)) { /* end due to eof or err */ if (wd_fp == (FILE *) EOF || wd_fp == (FILE *) NULL || fclose (wd_fp) == EOF || wd_name[0] == '\0' || chdir (wd_name) < OK) return ((char *) NOTOK); return (wd_name); } wd_name[0] = '\0'; lastd_ino = -1; /* force to be set on first pass */ if (stat (wd_DOT, &wd_stat) < 0 || /* get inode num for this dir */ (wd_fp = fopen (wd_DOTDOT, wd_read)) == NULL) longjmp (wd_retloc, TRUE); /* set to compare DOTDOT inodes */ setbuf (wd_fp, buf); for (;;) { for (;;) { /* cycle thru dir's inodes */ fread (wd_entry.io, sizeof (struct direct), 1, wd_fp); if (ferror (wd_fp) || feof (wd_fp)) longjmp (wd_retloc, TRUE); if (wd_entry.dir.d_ino == wd_stat.st_ino) break; /* got a match */ } if (wd_fp == (FILE *) EOF || wd_fp == (FILE *) NULL || fclose (wd_fp) == EOF || ferror (wd_fp) || feof (wd_fp)) longjmp (wd_retloc, TRUE); if (wd_entry.dir.d_ino == 1) /* hit the root directory */ wd_ckroot (); /* try to find its name */ if (wd_entry.dir.d_ino == lastd_ino) /* we are cycling at top */ longjmp (wd_retloc, TRUE); else /* remember for next level up */ lastd_ino = wd_entry.dir.d_ino; wd_cat (); /* add current name to end */ if (chdir (wd_DOTDOT) < OK || /* set up for next pass this dir */ stat (wd_DOT, &wd_stat) < OK || freopen (wd_DOTDOT, wd_read, wd_fp) == NULL) longjmp (wd_retloc, TRUE); } } LOCFUN wd_ckroot () /* check root dir for filesys name */ { static char wd_root[] = "/"; struct stat wd_stat, owd_stat; /* to save device info */ if (stat (wd_entry.dir.d_name, &wd_stat) < 0) longjmp (wd_retloc, TRUE); owd_stat.st_dev = wd_stat.st_dev; if (chdir (wd_root) < OK) longjmp (wd_retloc, TRUE); if (freopen (wd_root, wd_read, wd_fp) == NULL) longjmp (wd_retloc, TRUE); for (;;) { /* read thru "/" dir */ fread (wd_entry.io, sizeof (struct direct), 1, wd_fp); if (ferror (wd_fp) || feof (wd_fp)) longjmp (wd_retloc, TRUE); if (wd_entry.dir.d_ino == 0) /* not allocated */ continue; if (stat (wd_entry.dir.d_name, &wd_stat) < 0) longjmp (wd_retloc, TRUE); if (wd_stat.st_dev == owd_stat.st_dev) { /* device types match */ wd_stat.st_mode &= S_IFMT; if (wd_stat.st_mode == S_IFDIR) break; /* and it is a directory */ } } if (strcmp (wd_entry.dir.d_name, wd_DOT) != 0 && strcmp (wd_entry.dir.d_name, wd_DOTDOT) != 0) wd_cat (); /* it has a real text name, too */ wd_entry.dir.d_name[0] = '\0'; /* hack, to force "/" at front */ wd_cat (); longjmp (wd_retloc, TRUE); } LOCFUN wd_cat () { extern char *strcpy (), *strcat (); if (wd_name[0] == 0) (void) strcpy (wd_name, wd_entry.dir.d_name); else { strcat (wd_name, "/"); strcat (wd_name, wd_entry.dir.d_name); } } y to find its name */ if (wd_entry.dir.d_ino == lastd_ino) /* we are cycling at top */ longjmp (wd_retloc, TRUE); else /* remember for next level up */ lastd_ino = wd_entry.dir.d_ino; mmdf/lib/util/rplstr.c 444 0 12 276 3620510420 10143 /* replace an alloc'd string with a new one */ rplstr (oldstr, newstr) char **oldstr, *newstr; { if (*oldstr != 0) free (*oldstr); *oldstr = newstr; } TDOT, wd_read, wd_fp) == NULL) longjmp (wd_retloc, TRUE); } } LOCFUN wd_ckroot () /* check root dir for filesys name */ { static char wd_root[] = "/"; struct stat wd_stat, owd_stat; /* to save device info */ if (stat (wd_entry.dir.d_name, &wd_stat) < 0) lommdf/lib/util/sptotab.c 444 0 12 522 3620510421 10264 # sptotab (pcurcol, firstab, tabinc) int pcurcol, firstab, tabinc; { register short curcol, diff; curcol = pcurcol; firstab++; if (curcol <= firstab) return (firstab - curcol); else return ((diff = (--curcol % tabinc)) == 0 ? 0 : (tabinc - diff)); } static char wd_root[] = "/"; struct stat wd_stat, owd_stat; /* to save device info */ if (stat (wd_entry.dir.d_name, &wd_stat) < 0) lommdf/lib/util/strdup.c 444 0 12 1011 3620510421 10143 #include "util.h" /* */ /* Create a duplicate copy of the given string. */ /* Modified for V7 Unix by Jim lieb SRI International Aug 80 */ char * strdup (str) register char *str; { extern char *malloc (); register char *newptr, *newstr; if ((newstr = malloc ((unsigned) (strlen (str) + 1))) == 0) return ((char *) 0); for (newptr = newstr; *newptr++ = *str++; ); return (newstr); } _retloc, TRUE); owd_stat.st_dev = wd_stat.st_dev; if (chdir (wd_root) < OK) longjmp (wd_retloc, TRUE); if (freopen (wd_root, wd_read, wd_fp) == NULL) longjmp (wd_retloc, TRUE); for (;;) { /* read thru "/" dir */ fread (wd_entry.io, sizeof (struct direct), 1, wd_fp); if (ferror (wd_fp) || feof (wd_fp)) longjmp (wd_retloc, TRUE); if (wd_entry.dir.d_ino == 0) /* not allocated */ continue; if (stat (wd_entry.dir.d_nammmdf/lib/util/strequ.c 444 0 12 623 3620510421 10135 #include "util.h" /* */ /* Determine if the two given strings are equivalent. */ /* */ strequ (str1, str2) register char *str1, *str2; { while (*str1 == *str2++) { if (*str1++ == '\0') return (TRUE); } return (FALSE); } en (str) + 1))) == 0) return ((char *) 0); for (newptr = newstr; *newptr++ = *str++; ); return (nemmdf/lib/util/zero.c 444 0 12 520 3620510421 7565 /* */ /* Zero out the given string of the given length. */ /* */ zero (str, length) register char *str; register short length; { while (length--) *str++ = '\0'; } (*str1++ == '\0') return (TRUE); } return (FALSE); } en (str) + 1))) == 0) return ((char *) 0); for (newptr = newstr; *newptr++ = *str++; ); return (nemmdf/lib/util/nexec.c 444 0 12 10530 3623126410 7756 #include "util.h" #include "nexec.h" #include <signal.h> /* generic fork/exec procedure, passing re-positioned fd's */ /* Jun 81 D. Crocker make regfdary loop tmp<=HIGHFD May 82 D. Crocker make close of fd tmp<=HIGHFD Jul 82 D. Crocker make vax version, to use vfork Mar 84 D. Kingston Changed to use _NFILE from stdio.h instead of HIGHFD. Steve Kille: you don't have to shorten the array if you have less fds. */ extern int numfds; static char *zap_env[1] = { (char *)0 }; /* VARARGS4 */ #ifdef NO_VARARGS nexecl (proctyp, pgmflags, fdarray, pgm, pgmparm, a,b,c,d,e,f,g,h,i,j,k,l,m) #else nexecl (proctyp, pgmflags, fdarray, pgm, pgmparm) #endif NO_VARARGS int proctyp, /* exec / fork / fork-exec */ pgmflags, /* parent wait? disable interrupts? */ *fdarray; /* what current fd's go where? */ char *pgm, /* what program to exec? */ *pgmparm; /* its parameters vector */ { return (nexecv (proctyp, pgmflags, fdarray, pgm, &pgmparm)); } nexecv (proctyp, pgmflags, fdarray, pgm, pgmparm) int proctyp, pgmflags, *fdarray; char *pgm, *pgmparm[]; { register int fd; int status; int childid; int (*(osig1))(); int (*(osig2))(); int (*(osig3))(); if (proctyp != PUREXEC) { /* printf ("This is a forking call.\n"); */ childid = tryfork (); if (childid == -1) return (NOTOK); /* printf ("Successful fork\n"); */ if (childid != 0) { /* parent process */ if (pgmflags & FRKPIGO) { /* disable parent signals */ /* printf ("Parent to be non-interruptible\n"); */ osig1 = signal (SIGHUP, SIG_IGN); osig2 = signal (SIGINT, SIG_IGN); osig3 = signal (SIGQUIT, SIG_IGN); } if (((proctyp == FRKEXEC) && (pgmflags & FRKWAIT)) || proctyp == SPNEXEC) { /* printf ("Parent is to wait\n"); */ status = pgmwait (childid); if (pgmflags & FRKPIGO) { (void)signal (SIGHUP, osig1); (void)signal (SIGINT, osig2); (void)signal (SIGQUIT, osig3); } return (status); } return (childid); } if (proctyp == SPNEXEC) { /* want it to be a spawn */ /* printf ("This is a spawn\n"); */ switch (childid = tryfork ()) { case 0: break; /* gets to do its thing */ case NOTOK: /* oops */ exit (NOTOK); default: /* grandchild: kill middle proc */ exit (0); } } } if (fdarray) { /* re-align fd array list */ for (fd = 0; fd < numfds; fd++) { /* first do the re-positions */ if (fdarray[fd] != CLOSEFD && fdarray[fd] != fd) { #ifndef NODUP2 (void)dup2( fdarray[fd], fd ); #else NODUP2 (void)close (fd); #ifndef NOFCNTL #include <fcntl.h> fcntl (fdarray[fd], F_DUPFD, fd); #else NOFCNTL { int dupfd; while ((dupfd = dup (fdarray[fd])) < fd && dupfd != -1); /* did we get right fd? */ } #endif NOFCNTL #endif NODUP2 } } for (fd = 0; fd < numfds; fd++) if (fdarray[fd] == CLOSEFD) { /* printf ("Closing %2d\n", fd); */ (void)close (fd); /* get rid of unwanted ones */ } } if (pgmflags & FRKCIGO) { /* printf ("Child's interrupts to be disabled\n"); */ (void)signal (1, SIG_IGN); (void)signal (2, SIG_IGN); (void)signal (3, SIG_IGN); } /* printf ("Execing %s\n", pgm); */ execve (pgm, pgmparm, zap_env); /* printf ("Exec not successful\n"); */ switch (proctyp) { case PUREXEC: return (NOTOK); default: /* caller can deal with it */ if (pgmflags & FRKERRR) return (NOTOK); } exit (NOTOK); /* NOTREACHED */ } pgmwait (childid) int childid; /* process id of child to collect */ { int status; register int retval; while ((retval = wait (&status)) != childid) if (retval == NOTOK) return (NOTOK); return (gwaitval (status)); } LOCFUN tryfork () { register int childid; register int tried; tried = NUMTRY; do { if(( childid = fork ()) < 0 ) sleep( 1 ); else return( childid ); } while( tried-- ); return( childid ); } rom stdio.h instead of HIGHFD. Steve Kille: you don't have to shorten the array if you have less fds. */ extern int numfds; static char *zap_env[1] = { (chmmdf/lib/util/ll_log.c 444 0 12 16230 3671073210 10131 #include "util.h" #include "conf.h" #include <sys/stat.h> #include "ll_log.h" /* Package for logging entries into files */ /* David H. Crocker, Dept. of Electrical Engineering */ /* University of Delaware; October 1979 */ /* Failure to open a log file, other than due to its being busy, * causes the fd associated with ll_struct to be set to -1. * Later calls then result in a no-op, except for openllog & closellog */ #define _LLRETRY 5 /* number of retries if log locked */ #define _LLSLEEP 5 /* time between tries */ #define SHORTIM 0 /* Short time format */ #define LONGTIM 1 /* Long time format */ #define _LLDIDC 0200 /* Already did cycle, this opening */ extern char *strdup(); extern char *rindex(); LOCVAR char _cycling[] = "\n\n*** CYCLING ***\n", _cycled[] = "*** CYCLED ***\n"; /* ******************* LOG OPENING AND CLOSING ******************** */ ll_open (loginfo) /* physically open the file */ register struct ll_struct *loginfo; { extern int errno; short retries; if (loginfo -> ll_file == 0 || *(loginfo -> ll_file) == '\0') return (loginfo -> ll_fd = NOTOK); /* no pathname => no place to log */ if (loginfo -> ll_fd <= 0) /* now closed; ALWAYS try */ for (retries = _LLRETRY; (loginfo -> ll_fd = open (loginfo -> ll_file, 1)) <= 0; sleep (_LLSLEEP)) { /* exclusive & write access */ if (errno != 26 || /* not simply busy */ !(loginfo -> ll_stat & LLOGWAT) || retries-- <= 0) return (loginfo -> ll_fd = NOTOK); } loginfo -> ll_fp = fdopen(loginfo -> ll_fd, "w"); return (ll_init (loginfo)); } ll_close (loginfo) register struct ll_struct *loginfo; { /* maybe close file; always clear fd */ register int fd; fd = loginfo -> ll_fd; loginfo -> ll_fd = 0; /* clear the fd: erase -1 or handle */ if (loginfo -> ll_stat & _LLDIDC) { /* cycled during this open */ if (fd > 0) /* flag end, from rest of log */ fwrite (&_cycled[5], sizeof(char), (sizeof _cycled) - 6, loginfo -> ll_fp); loginfo -> ll_stat &= ~_LLDIDC; } /* clear cycled flag */ return ((fd > 0) ? fclose (loginfo -> ll_fp) : 0); /* -1 or 0 => nothing was open */ } /* ********************* LOG INITIALIZATION *********************** */ ll_hdinit (loginfo, pref) /* make header field unique */ register struct ll_struct *loginfo; register char *pref; { register char *cp; char tmpstr[16]; if (pref == (char *)0 && (pref = loginfo -> ll_hdr) == (char *)0) pref = "XXXXXX"; if (cp = rindex(pref, '/')) pref = cp+1; /* drop leading path */ sprintf (tmpstr, "%-6.6s%04d", pref, getpid() % 10000); /* use last 4 digits of procid */ loginfo -> ll_hdr = strdup (tmpstr); ll_close (loginfo); /* start with a clean pointer */ } ll_init (loginfo) /* position log [print header] */ register struct ll_struct *loginfo; { if (loginfo -> ll_fd < 0) return (NOTOK); fseek (loginfo -> ll_fp, 0L, 2); if (ll_cycle (loginfo) < OK) return (NOTOK); /* too big and can't cycle */ if (loginfo -> ll_stat & LLOGCLS) return (OK); /* don't do header for opening */ if (loginfo -> ll_hdr == (char *) 0) /* give it a place-holder */ loginfo -> ll_hdr = ""; ll_time (loginfo, LONGTIM); fprintf (loginfo -> ll_fp, "--- OPEN ---(%d)\n", loginfo -> ll_fd); return (OK); } /* ********************* DO THE LOGGING *************************** */ /* VARARGS3 */ ll_log (loginfo, verblev, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a0) register struct ll_struct *loginfo; int verblev; char *format, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9, *a0; { if (verblev > loginfo -> ll_level) return; /* too verbose for this setting */ switch (loginfo -> ll_fd) { case NOTOK: /* error in its past */ return; case 0: if (ll_open (loginfo) < OK) return; /* couldn't open it properly */ break; default: /* check file size */ if (ll_cycle (loginfo) < OK) return; } ll_time (loginfo, (loginfo -> ll_stat & LLOGCLS) ? LONGTIM : SHORTIM); fprintf(loginfo->ll_fp, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a0); (void) putc('\n', loginfo->ll_fp); if (loginfo -> ll_stat & LLOGCLS) ll_close (loginfo); return; } /* ********************* INTERNAL ROUTINES ************************ */ LOCFUN ll_cycle (loginfo) /* file not within limits => cycle */ register struct ll_struct *loginfo; { int stats; register int numblocks, maxblocks; if ((maxblocks = loginfo -> ll_msize) <= 0) return (OK); maxblocks *= 25; /* units of 25 blocks (1 kbyte/block) */ numblocks = ll_size (loginfo); if ((stats = loginfo -> ll_stat) & _LLDIDC) { /* different test if already cycled */ if (numblocks > maxblocks + 1) goto endit; /* the sucker REwrote length of file */ } else if (numblocks >= maxblocks) { /* normal uncycled check */ if ((stats & LLOGCLS) || !(stats & LLOGCYC)) { /* give up if one-liner or no cycling */ endit: ll_close (loginfo); return (loginfo -> ll_fd = NOTOK); } /* record status of llog file */ loginfo -> ll_msize = numblocks / 25; loginfo -> ll_stat |= _LLDIDC; /* make notes in it & goto beginning */ fseek (loginfo->ll_fp, -20L, 1); fwrite(_cycling, sizeof(char), sizeof _cycling - 1, loginfo->ll_fp); fseek (loginfo->ll_fp, 0L, 0); fwrite(_cycled, sizeof(char), sizeof _cycled - 1, loginfo->ll_fp); } return (OK); } /**/ LOCFUN ll_size (loginfo) /* how big is log file? */ register struct ll_struct *loginfo; { struct stat llstat; if(fflush(loginfo->ll_fp) == EOF) /* must show what is buffered too */ return (0); if (fstat(loginfo -> ll_fd, &llstat) < 0) return (0); /*NOSTRICT*/ return ((st_gsize (&llstat) + 1023L)/1024L); } /**/ LOCFUN ll_time (loginfo, timtype) register struct ll_struct *loginfo; int timtype; { /* record real date/time */ extern struct tm *localtime (); time_t clock; register struct tm *tpt; time (&clock); tpt = localtime (&clock); switch (timtype) { case LONGTIM: /* mm/dd hh:mm:ss */ fprintf (loginfo->ll_fp, "%2d/%2d %2d:%02d:%02d %s: ", tpt->tm_mon + 1, tpt->tm_mday, tpt->tm_hour, tpt->tm_min, tpt->tm_sec, loginfo -> ll_hdr); break; case SHORTIM: /* mm:ss */ fprintf (loginfo->ll_fp, "%2d:%02d ", tpt->tm_min, tpt->tm_sec); } } /* start with a clean pointer */ } ll_init (loginfo) /* position log [print header] */ register struct ll_struct *loginfo; { if (loginfo -> ll_fd < 0) return (NOTOK); fseek (loginfo -> ll_fp, 0L, 2); if (ll_cycle (loginfo) < OK) return (NOTOK); /* too big and can't cycle */ if (loginfo -mmdf/lib/util/getllog.fak.c 444 0 12 336 3620510422 11011 /* For systems that have no easy way to find last login time */ /* For example: SYSVR2 -- "Consider it stranded." */ setllog() { } endllog() { } getllog() { return (0); } getllnam(name) char *name; { return (0); } /* position log [print header] */ register struct ll_struct *loginfo; { if (loginfo -> ll_fd < 0) return (NOTOK); fseek (loginfo -> ll_fp, 0L, 2); if (ll_cycle (loginfo) < OK) return (NOTOK); /* too big and can't cycle */ if (loginfo -mmdf/lib/util/ll_err.c 444 0 12 1244 3671073210 10117 #include <stdio.h> #include "ll_log.h" /* Augment ll_io routines to print system call errno value */ extern int sys_nerr; extern char *sys_errlist[]; extern int errno; extern char *sprintf (); /* VARARGS3 */ ll_err (loginfo, level, fmt, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) struct ll_struct *loginfo; int level; char *fmt, *a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9; { char newfmt[128]; sprintf (newfmt, "%s%s", "[SYSERR (%d) %s] ", fmt); ll_log (loginfo, level, newfmt, errno, (errno >= 0 && errno <= sys_nerr) ? sys_errlist[errno] : "<- Illegal errno", a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } loginfo -> ll_hdr = ""; ll_time (loginfo, LONGTIM); fprintf (loginfo -> ll_fp, "--- OPEN ---(%d)\n", loginfo -> ll_fd); return (OK); } /* ********************* DO THE LOGGING *************************** */ /* VARARGS3 */ ll_log (loginfo, verblev, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a0) register struct ll_struct *logimmdf/lib/util/getutmp.c 444 0 12 1520 3620510422 10315 #include "util.h" #include <utmp.h> /* get informtion about who is logged in */ /* this is modeled after the getpw v7 set of routines */ LOCVAR char UTPATH[] = "/etc/utmp"; LOCVAR FILE *utmpfp = NULL; LOCVAR union tmpunion { char io[sizeof (struct utmp)]; struct utmp entry; } utmp; setutmp() { if( utmpfp == NULL ) utmpfp = fopen( UTPATH, "r" ); else rewind( utmpfp ); } endutmp() { if( utmpfp != NULL ){ fclose( utmpfp ); utmpfp = NULL; } } struct utmp * getutmp() { if (utmpfp == NULL) { if( (utmpfp = fopen( UTPATH, "r" )) == NULL ) return(0); } if (fread (utmp.io, sizeof (struct utmp), 1, utmpfp) != 1) return(0); return(&utmp.entry); } struct utmp * getutnam(name) char name[]; { register struct utmp *p; while( (p = getutmp()) && !equal(name,p->ut_name, strlen(name))); return(p); } NULL ) utmpfp = fopen( UTPATH, "r" ); else rewind( utmpfp ); } endutmp() { if( utmpfp != NULL ){ fclose( utmpfp ); utmpfp = NULL; } } struct utmp * getutmp() { ifmmdf/lib/util/tty_io.c 444 0 12 4166 3620510423 10151 #include "util.h" #include <pwd.h> #include <sys/stat.h> #include <utmp.h> extern int errno; extern jmp_buf timerest; extern char *blt(); FILE *tty_fp; /* user sends tty output to this */ /* **************** (tty_) ACCESS USERS' TTYS ********************** */ tty_init () /* setup for ttyecipient's tty */ { setutmp (); /* get the utmp file */ return (OK); } tty_end () /* done with tty access */ { endutmp (); /* done with utmp package */ return (OK); } tty_find (usrname) /* find & open recipient's tty */ char usrname[]; { extern struct utmp *getutnam (); static char tty_num[14] = "/dev/"; /* number of tty receiver is on */ /* name is filled in from getutnam */ struct utmp *utptr; if ((utptr = getutnam (usrname)) == 0) { errno = ENOENT; /* not logged in */ return (NOTOK); } blt (utptr -> ut_line, &tty_num[5], 8); return (tty_open (tty_num)); } /**/ LOCFUN tty_open (tty_num) /* open the named tty */ char tty_num[]; { struct stat statbuf; int tty_fd; if (setjmp (timerest)) /* signal, for timerest, must be */ { /* set by user program */ errno = EBUSY; /* allow a later try */ return (NOTOK); /* timeout during tty open */ } #ifndef SECURETTY if (stat (tty_num, &statbuf) < OK) return (NOTOK); if (!(statbuf.st_mode & (S_IWRITE >> 6))) #else if (access (tty_num, 01) != 0) #endif { /* check write(I) permissions */ errno = EBUSY; return (NOTOK); } s_alarm (15); tty_fd = open (tty_num, 1); s_alarm (0); if (tty_fd < 0) return (NOTOK); tty_fp = fdopen (tty_fd, "w"); return (OK); } tty_close () /* done writing to tty */ { if (ferror (tty_fp) || fclose (tty_fp) == EOF) return (NOTOK); return (OK); } units of 25 blocks (1 kbyte/block) */ numblocks = ll_size (loginfo); if ((stats = loginfo -> ll_stat) & _LLDIDC) { /* different test if already cycled */ if (numblocks > maxblocks + 1) goto endit; /* the sucker REwrote length of file */ } else if (numblocks >= maxblocks) { /* normal uncycled mmdf/lib/util/lk_lock.c 444 0 12 17566 3671073211 10315 #include "util.h" #include <sys/stat.h> /* * Standardized file-locking package (using links) * * This version presumes no o/s support of locking, with * link(2) being the only available atomic o/s operation. */ extern int errno; /* simulate system error problems */ #ifdef DEBUG #include "ll_log.h" extern LLog *logptr; #endif extern char *lckdfldir; LOCVAR int breaktime = 120; /* amount to sleep after breaking lock */ LOCVAR char *NIL = "NIL"; /**/ LOCFUN lk_lock (file, lockdir, lockfile, maxtime) char *file; /* file to be locked */ char *lockdir; /* directory to put parallel file into */ char *lockfile; /* file to lock against */ int maxtime; /* maybe break lock after it is this old */ { int sleepval; int retval; int tries; char lkname[100]; /* full name of locking file */ char tmpname[100]; /* name of tmp file to link from */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_lock (%s,%s,%s,%d)", file, lockdir ? lockdir : NIL, lockfile ? lockfile : NIL, maxtime); #endif if (lk_name (lkname, file, lockdir, lockfile) < OK) return (NOTOK); #ifdef DEBUG ll_log (logptr, LLOGFTR, "lkname '%s'", lkname); #endif for (tries = 0; tries < 2; tries++) switch (lk_test (file, lkname, maxtime)) { case OK: /* lock exists & is ok */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "already locked & ok"); #endif errno = ETXTBSY; return (NOTOK); case NOTOK: /* lock exists and is old */ #ifdef DEBUG ll_log (logptr, LLOGFTR, "old lock"); #endif if (lk_unlock (file, lockdir, lockfile) == NOTOK) return (NOTOK); /* hmmmmmm */ /* * the amount of time to sleep is at least breaktime. * the random number generation is used to try to separate * two processes that may be close to syncrony in checking the lock. */ srand (getpid ()); sleepval = breaktime + (rand ()%100); #ifdef DEBUG ll_log (logptr, LLOGFTR, "sleeping (%d)", sleepval); #endif sleep (sleepval); continue; default: goto lockit; } /* below here, try to lock the guy */ lockit: getfpath (",tmp.XXXXXX", lockdir ? lockdir : lckdfldir, tmpname); mktemp (tmpname); /* have to have something to link from */ if (close (creat (tmpname, 0666)) < OK) { #ifdef DEBUG ll_err (logptr, LLOGFTR, "problem w/lock base file '%s'", tmpname); #endif (void) unlink (tmpname); return (NOTOK); } retval = link (tmpname, lkname); #ifdef DEBUG if (retval < 0) ll_err (logptr, LLOGFTR, "problem linking '%s' to '%s'", tmpname, lkname); else ll_log (logptr, LLOGFTR, "linked '%s' to '%s'", tmpname, lkname); #endif (void) unlink (tmpname); return (retval); } /**/ LOCFUN lk_unlock (file, lockdir, lockfile) char *file; /* file to be locked */ char *lockdir; /* directory to put parallel file into */ char *lockfile; /* file to lock against */ { int retval; char lkname[100]; /* full name of locking file */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_unlock (%s,%s,%s)", file, lockdir ? lockdir : NIL, lockfile ? lockfile : NIL); #endif if (lk_name (lkname, file, lockdir, lockfile) < OK) return (NOTOK); retval = unlink (lkname); #ifdef DEBUG if (retval < 0) ll_err (logptr, LLOGFTR, "problem unlinking '%s'", lkname); #endif return (retval); } /**/ LOCFUN lk_name (lkname, file, lockdir, lockfile) char *lkname; /* put full name of file to use as lock */ char *file; /* resource to be locked */ char *lockdir; /* dir containing locking file, if any */ char *lockfile; /* file to use ask lock */ { struct stat statbuf; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_name (%s,%s,%s)", file, lockdir ? lockdir : NIL, lockfile ? lockfile : NIL); #endif if (lockdir == (char *) 0) lockdir = lckdfldir; if (lockfile && *lockfile ) getfpath (lockfile, lockdir, lkname); else { /* make out own file name */ if (file == (char *) 0) /* no way to create a fixed name */ { errno = EINVAL; return (NOTOK); } else { if (stat (file, &statbuf) < OK) return (NOTOK); sprintf (lkname, "%s/,LK%05.5o%o", lockdir, statbuf.st_dev, statbuf.st_ino); } } return (OK); } /**/ LOCFUN lk_test (file, lkfile, maxtime) char *file; char *lkfile; int maxtime; { time_t curtime; struct stat statbuf; time (&curtime); if (stat (lkfile, &statbuf) < OK) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "lk_test couldn't stat lockfile '%s'", lkfile); #endif return (DONE); /* assume that it does not exist */ } if (maxtime <= 0) return (OK); /* no time limit */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "lockfile (%ld:%ld) minutes", (((curtime - statbuf.st_mtime)+59L)/60L), (long) (maxtime)); #endif if ((((curtime - statbuf.st_mtime)+59L)/60L) > (long) (maxtime)) { /* well, the lock is too old */ if (file != (char *) 0) /* is the resource inactive, also? */ if (stat (file, &statbuf) >= OK) { #ifdef DEBUG ll_log (logptr, LLOGBTR, "resource access (%ld:%ld) minutes", (((curtime - statbuf.st_mtime)+59L)/60L), (long) (maxtime)); #endif if ((((curtime - statbuf.st_atime)+59L)/60L) <= (long) (maxtime)) return (OK); /* let them go */ } return (NOTOK); } return (OK); /* lock still has time */ } /**/ lk_open (file, access, lockdir, lockfile, maxtime) char *file; /* file to be locked */ int access; /* read-write permissions */ char *lockdir; /* directory to put parallel file into */ char *lockfile; /* file to lock against */ int maxtime; /* maybe break lock after it is this old */ { register int fd; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_open (%s,%d,%s,%s,%d)", file, access, lockdir ? lockdir : NIL, lockfile ? lockfile : NIL, maxtime); #endif if (lk_lock (file, lockdir, lockfile, maxtime) < OK) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "open lock notok"); #endif return (NOTOK); } if ((fd = open (file, access)) < 0) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "open notok"); #endif lk_unlock (file, lockdir, lockfile); return (NOTOK); } return (fd); } lk_close (fd, file, lockdir, lockfile) int fd; char *file; /* file to be locked */ char *lockdir; /* directory to put parallel file into */ char *lockfile; /* file to lock against */ { register int retval; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_close (%d,%s,%s,%s)", fd, file, lockdir ? lockdir : NIL, lockfile ? lockfile : NIL); #endif if (fd < 0) return (OK); retval = close (fd); lk_unlock (file, lockdir, lockfile); return (retval); } /**/ FILE * lk_fopen (file, access, lockdir, lockfile, maxtime) char *file; /* file to be locked */ char *access; /* read-write permissions */ char *lockdir; /* directory to put parallel file into */ char *lockfile; /* file to lock against */ int maxtime; /* maybe break lock after it is this old */ { register FILE *fp; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s)", file, access, lockdir ? lockdir : NIL, lockfile ? lockfile : NIL); #endif if (lk_lock (file, lockdir, lockfile, maxtime) < OK) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "fopen lock notok"); #endif return ((FILE *) NULL); } if ((fp = fopen (file, access)) == NULL) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "fopen notok"); #endif lk_unlock (file, lockdir, lockfile); return ((FILE *) NULL); } return (fp); } lk_fclose (fp, file, lockdir, lockfile) FILE *fp; char *file; /* file to be locked */ char *lockdir; /* directory to put parallel file into */ char *lockfile; /* file to lock against */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_fclose (%s,%s,%s)", file, lockdir ? lockdir : NIL, lockfile ? lockfile : NIL); #endif lk_unlock (file, lockdir, lockfile); switch ((int)fp) { case EOF: case NULL: return (OK); } return (fclose (fp)); } * make out own file name */ if (file == (char *) 0) /* no way to create a fixed name */ { errno = EINVAL; return (NOTOK); } mmdf/lib/util/getfpath.c 444 0 12 1011 3671073212 10434 #include "util.h" getfpath (fnam, dflpth, dest) /* build full pathname */ char fnam[], dflpth[]; char *dest; { if (fnam[0] == '/') /* fully specified */ (void) strcpy (dest, fnam); else sprintf (dest, "%s/%s", dflpth, fnam); } char * dupfpath (fnam, dflpth) /* build full pathname & alloc str */ char fnam[], dflpth[]; { extern char *strdup (); char temppath[128]; getfpath (fnam, dflpth, temppath); return (strdup (temppath)); } t exist */ } if (maxtime <= 0) return (OK); /* no time limit */ #ifdef DEBUG ll_log (logptr, LLOGBTR, "lockfile (%ld:%ld) minutes", (((curtime - statbuf.st_mtime)+59L)/60L), (long) (maxtime)); #endif if ((((curtime - statbuf.st_mtime)+59L)/60L) > (long) (maxtime)) { /* well, the lock is too old */ if (file != (char *) 0) /* is the resource inactive, also? */ if (stat (file, &statbuf) >= OK) { #ifdef DEBUG ll_log (logptr, LLOGmmdf/lib/util/creatdir.c 444 0 12 3714 3671073213 10444 #include "util.h" #include <sys/stat.h> /* create a directory * * build intervening directories, if nonexistant * chown new directories to specified uid * * some of the system calls are not checked because they can * fail benignly, as when an intermediate directory is executable, * but not readable or writeable. the only test for ultimate * success is being able to create and stat the final directory. */ creatdir (dirptr, mode, owner, group) register char *dirptr; /* pathname to directory */ int mode; int owner, /* non-zero uid of for dir owner */ group; { extern int errno; struct stat statbuf; int realid, effecid; int uid, gid; char shcmd[128]; register char *partpath; register char *nptr; /* last char in partial pathname */ if (dirptr == (char *) 0 || isnull (*dirptr)) return (NOTOK); /* programming error */ if (owner != 0) /* coerced ownship requested */ { /* noop it, if under requested id's */ getwho (&realid, &effecid); uid = realid; getgroup (&realid, &effecid); gid = realid; if( uid && (uid != owner || gid != group )) return( NOTOK ); } (void) strcpy (shcmd, "mkdir "); /* initialize string with command */ partpath = &shcmd[strlen (shcmd)]; for (nptr = partpath, *nptr++ = *dirptr++; ; *nptr++ = *dirptr++) switch (*dirptr) { case '\0': case '/': *nptr = '\0'; if (stat (partpath, &statbuf) < 0) { /* should we try to creat it? */ #ifndef V4_2BSD system (shcmd); /* don't check if it succeeded */ #else V4_2BSD mkdir (partpath); #endif V4_2BSD if (owner != 0) chown (partpath, owner, group); chmod (partpath, mode); } if (isnull (*dirptr)) return ((stat (partpath, &statbuf) < 0) ? NOTOK : OK); } } (file, access)) < 0) { #ifdef DEBUG ll_err (logmmdf/lib/util/gwdir.4.2.c 444 0 12 307 3620510423 10231 #include "util.h" char * gwdir () /* what is current working directory? */ { static char path[1024]; if (getwd (path) == 0) return ((char *)NOTOK); else return (path); } the system calls are not checked because they can * fail benignly, as when an intermediate directory is executable, * but not readable or writeable. the only test for ultimate * success is being able to create and stat the final directory. */ creatdir (dirptr, mode, owner, group) registemmdf/lib/util/gpwlnam.c 444 0 12 1177 3620510424 10307 #include "util.h" #include <pwd.h> /* modified getpwnam(), to do lexical (case-independent) comparison */ struct passwd * gpwlnam(name) char *name; { struct passwd *getpwent(); char lownam[30]; register struct passwd *p; register int ind; for (ind = 0; ind < ((sizeof lownam) - 1) && !isnull (name[ind]); ind++) lownam[ind] = uptolow (name[ind]); lownam[ind] = '\0'; for (setpwent(); (p = getpwent()) != NULL; ) { for (ind = 0; !isnull (name[ind]); ind++) p -> pw_name[ind] = uptolow (p -> pw_name[ind]); if (strcmp (lownam, p->pw_name) == 0) break; } endpwent(); return(p); } for dir owner */ group; { extern int errno; struct stat statbuf; int realid, effecid; int uid, gid; char shcmd[128]; register char *partpath; register char *nptr; /* last char in partial pathname */ if (dirptr == (char *) 0 || isnull (*dirptr)) return (NOTOK); /* programming error */ if (owner mmdf/lib/util/strindex.c 444 0 12 515 3620510424 10455 strindex (str, target) /* return column str starts in target */ register char *str, *target; { char *otarget; register short slen; for (otarget = target, slen = strlen (str); ; target++) { if (*target == '\0') return (-1); if (equal (str, target, slen)) return (target - otarget); } } ) lownam[ind] = uptolow (name[ind]); lownam[ind] = '\0'; for (setpwent(); (p = getpwent()) != NULL; ) { for (ind = 0; !isnull (name[ind]); ind++) p -> pw_name[mmdf/lib/util/getgroup.c 444 0 12 242 3620510424 10446 getgroup (realid, effecid) /* group info for V7 Unix */ int *realid, *effecid; { *realid = getgid (); *effecid = getegid(); } (otarget = target, slen = strlen (str); ; target++) { if (*target == '\0') return (-1); if (equal (str, target, slen)) return (target - otarget); } } ) lownam[ind] = uptolow (name[ind]); lownam[ind] = '\0'; for (setpwent(); (p = getpwent()) != NULL; ) { for (ind = 0; !isnull (name[ind]); ind++) p -> pw_name[mmdf/lib/util/expand.c 444 0 12 2267 3620510424 10122 /* * E X P A N D . C * * Expand expands a text string expanding macros provided * in a vector of key and value pairs. * * e.g. expandl(buf, "$A+$B=$(RESULT)", array) * with char *array[] = {"A", "1", * "B", "2", * "RESULT", "3", * 0, 0}; * gives "1+2=3" in buf. * * wja@uk.ac.nott.maths Wed Feb 22 15:00:26 GMT 1984 * DPK@BRL, 13 Apr 84 Made more portable and changed array usage. */ #include "util.h" LOCFUN char *exname(), *exlookup(); char * expand(buf, fmt, argp) char *buf, *fmt, **argp; { register char *bp = buf, *cp; char name[64]; while(*fmt) { if(*fmt != '$') *bp++ = *fmt++; else if(*++fmt == '$') *bp++ = *fmt++; else { fmt = exname(name, fmt); cp = exlookup(name, argp); while(*cp) *bp++ = *cp++; } } *bp = '\0'; return buf; } LOCFUN char * exname(buf, str) register char *buf, *str; { if(*str == '(') { str++; while(*str != ')' && *str != '\0') *buf++ = *str++; if(*str != '\0') str++; } else *buf++ = *str++; *buf = '\0'; return str; } LOCFUN char * exlookup(name, list) register char *name, **list; { while(*list) { if(lexequ(name, *(list++))) return (*list); list++; } return (""); } tgroup (&realid, &effecid); gid = realid; if( uid && (uid != owner || gid != group )) return( NOTOK ); } (void) strcpy (shcmd, "mkdir "); /* initialize string with command */ partpath = &shcmd[strlen (shcmd)]; for (nptr = partpath, *nptr++ = *dirptr++; ; *nptr++ = *dirptr++) switch (*dirptr) { mmdf/lib/util/lk_lock.rand.c 444 0 12 4753 3671073214 11215 #include "util.h" /* * Standardized file-locking package (RAND exclusive open) * * This version assumes the existence of the RAND exclusive open * in the operating system. (DPK @ BRL, 21 Dec 82) */ #define FEXCLUSV 04 /* OR this in to get an exclusive open */ extern int errno; /* simulate system error problems */ #ifdef DEBUG #include "ll_log.h" extern struct ll_struct *logptr; #endif /**/ lk_open (file, access, lockdir, lockfile, maxtime) char *file; /* file to be locked */ int access; /* read-write permissions */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ int maxtime; /* maybe break lock after it is this old */ { register int fd; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_open (%s,%d,%s,%s,%d)", file, access, lockdir, lockfile, maxtime); #endif if ((fd = open (file, FEXCLUSV | access)) < 0) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "open notok (err %d)", errno); #endif return (NOTOK); } return (fd); } lk_close (fd, file, lockdir, lockfile) int fd; char *file; /* file to be locked */ char *lockdir; /* directory to put parallel file into */ char *lockfile; /* file to lock against */ { register int retval; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_close (%d,%s,%s,%s)", fd, file, lockdir, lockfile); #endif if (fd < 0) return (OK); retval = close (fd); return (retval); } /**/ FILE * lk_fopen (file, access, lockdir, lockfile, maxtime) char *file; /* file to be locked */ char *access; /* read-write permissions */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ int maxtime; /* maybe break lock after it is this old */ { register int fd; register FILE *fp; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s,%d)", file, access, lockdir, lockfile, maxtime); #endif if ((fd = open(file, FEXCLUSV | (access[0]=='r' && access[1]==0 ? 0 : 2))) < 0) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "open notok"); #endif return( NULL ); } if ((fp = fdopen (fd, access)) == NULL) { (void) close( fd ); return ((FILE *) NULL); } return (fp); } lk_fclose (fp, file, lockdir, lockfile) FILE *fp; char *file; /* --Ignored-- */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_fclose (%s,%s,%s)", file, (lockdir ? lockdir : ""), (lockfile ? lockfile : "")); #endif switch ((int)fp) { case EOF: case NULL: return (OK); } return(fclose (fp)); } ch (*dirptr) { mmdf/lib/util/ggrlnam.c 444 0 12 1175 3620510425 10270 #include "util.h" #include <grp.h> /* modified getgrnam(), to do lexical (case-independent) comparison */ struct group * ggrlnam(name) char *name; { struct group *getgrent(); char lownam[30]; register struct group *g; register int ind; for (ind = 0; ind < ((sizeof lownam) - 1) && !isnull (name[ind]); ind++) lownam[ind] = uptolow (name[ind]); lownam[ind] = '\0'; for (setpwent(); (g = getgrent()) != NULL; ) { for (ind = 0; !isnull (name[ind]); ind++) g -> gr_name[ind] = uptolow (g -> gr_name[ind]); if (strcmp (lownam, g->gr_name) == 0) break; } endpwent(); return(g); } er it is this old */ { register int fd; register FILE *fp; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s,%d)", file, access, lockdir, lockfile, maxtime); #endif if ((fd = open(file, FEXCLUSV | (access[0]=='r' && access[1]==0 ? 0 : 2))) < 0) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "open notok"); #endif return( NULL ); } if ((fp = fdopen mmdf/lib/util/str2arg.c 444 0 12 11135 3620510425 10242 #include "util.h" /* convert string into argument list * * stash a pointer to each field into the passed array. * any common seperators split the words. extra white-space * between fields is ignored. * * if the separator is '=', then the current argument position in * the array points to "=", the next the one is the key and the * value follows it. This permits detecting variable assignment, * in addition to positional arguments. * i.e., key=value -> = key value * * specially-interpreted characters: * space, tab, double-quote, backslash, comma, equal, slash, period, * semi-colon, colon, carriage return, and line-feed (newline). * preceding a special char with a backslash removes its * interpretation. a backslash not followed by a special is used * to preface an octal specification for one character * * a string begun with double-quote has only double-quote and * backslash as special characters. * * a field which begins with semi-colon or a hash (#) is interpreted * as marking the rest of the line as a comment and it is skipped, as * are blank lines */ extern int errno; str2arg (srcptr, max, argv, dlm)/* convert srcptr to argument list */ register char *srcptr; /* source data */ int max; /* maximum number of permitted fields */ char *argv[]; /* where to put the pointers */ char dlm[]; /* list of delimiting characters */ { char gotquote; /* currently parsing quoted string */ char lastdlm; /* last delimeter character */ register int ind; register char *destptr; if (srcptr == 0) { errno = EINVAL; /* emulate system-call failure */ return (NOTOK); } for (lastdlm = (char) NOTOK, ind = 0, max -= 2; *srcptr != '\0'; ind++) { if (ind >= max) { errno = E2BIG; /* emulate system-call failure */ return (NOTOK); } for (argv[ind] = destptr = srcptr; isspace (*srcptr); srcptr++) ; /* skip leading white space */ /**/ for (gotquote = FALSE; ; ) { switch (*srcptr) { default: /* just copy it */ *destptr++ = *srcptr++; break; case '\"': /* beginning or end of string */ gotquote = (gotquote) ? FALSE : TRUE; srcptr++; /* just toggle */ break; case '\\': /* quote next character */ srcptr++; /* just skip the back-slash */ switch (*srcptr) { /* convert octal values */ case 'r': *destptr++ = '\r'; srcptr++; break; case 'n': *destptr++ = '\n'; srcptr++; break; case 'b': *destptr++ = '\b'; srcptr++; break; case 'f': *destptr++ = '\f'; srcptr++; break; default: if (*srcptr >= '0' && *srcptr <= '7') { *destptr = '\0'; do *destptr = (*destptr << 3) | (*srcptr++ - '0'); while (*srcptr >= '0' && *srcptr <= '7'); destptr++; break; } /* otherwise DROP ON THROUGH */ case '\\': case '\"': case ' ': case ',': case '\t': case ';': case '#': case ':': case '=': case '/': case '|': *destptr++ = *srcptr++; break; /* just copy it */ } /* DROP ON THROUGH */ break; case '=': /* make '=' prefix to pair */ if (gotquote) { *destptr++ = *srcptr++; break; } ind++; /* put value ahead of '=' */ if ( dlm != (char *) 0) { dlm[ind - 1] = dlm[ind] = '='; } argv[ind] = argv[ind - 1]; lastdlm = '='; argv[ind - 1] = "="; *destptr = '\0'; srcptr++; goto nextarg; case ' ': case '\t': case '\n': case '\r': case ',': case ';': case '#': case ':': case '/': case '|': if (gotquote) { /* don't interpret the char */ *destptr++ = *srcptr++; break; } if (isspace (lastdlm)) { if (isspace (*srcptr)) { /* shouldn't be possible */ errno = EINVAL; return (NOTOK); } else if (*srcptr != ';' && *srcptr != '#') ind--; /* "xxx , yyy" is only 2 fields */ } lastdlm = *srcptr; if ( dlm != (char *) 0) dlm[ind] = *srcptr; srcptr++; case '\0': *destptr = '\0'; goto nextarg; } lastdlm = (char) NOTOK; /* disable when field started */ } nextarg: if (argv[ind][0] == '\0' && (lastdlm == ';' || lastdlm == '#')) break; /* rest of line is comment */ } argv[ind] = (char *) 0; return (ind); } iod, * semi-colon, colon, carriage return, and line-feed (newline). * preceding a special char with a backslash removes its * interpretation. a backslash not followed by a special is used * to preface an octal specification for one character * * a string begun with double-quote has only double-quote and * backslash as special characters. * * a field which begins with semi-colmmdf/lib/util/gettys.c 444 0 12 5543 3620510425 10163 # /* A set of routines in the style of getpw() to read the ttys * file and return the tty info. * * Steve Manion * */ #include <stdio.h> #include "gettys.h" #define ETCTTYS "/etc/ttys" /* the channel to read the data from */ static FILE *input = NULL; /* the line number last read from the file */ static int linenum = 0; getttyent(userdata) struct ttys *userdata; { char buff[128]; /* if the input file is not yet open, do it */ if (input == NULL) if (openinput(ETCTTYS) < 0) return(BADDATA); /* read the next line from the input */ if (getline(buff) == EOF) { rewind(input); linenum = 0; return(EOF); } /* transfer the data into the user struct */ if (process(buff, userdata) < 0) return(BADDATA); /* return the line number we are on */ return(linenum); } openinput(file) char *file; { input = fopen(file, "r"); if (input == NULL) { printf("File '%s': ", file); fflush(stdout); perror(""); fflush(stderr); return(-1); } return(0); } static int getline(buff) char *buff; { register int c; for(;;) { c = getc(input); /* look for the true end of the file */ if( (c == EOF) && (feof(input) != 0) ) { *buff = '\0'; return(EOF); } /* transfer character to the buffer and check for the * end of the line. */ *buff++ = c; if (c == '\n') { *buff = '\0'; linenum++; return(0); } } } static int process(buff, userdata) char *buff; struct ttys *userdata; { register char *cp; /* check the first char of the line. It should be 0 or 1 as * the line is invalid or valid. */ switch(*buff++) { case '0': userdata->t_valid = 0; break; case '1': userdata->t_valid = 1; break; default: fflush(stdout); fprintf(stderr, "Invalid tty file - notify system manager.\n"); fflush(stderr); return(-1); } /* transfer the control code directly */ userdata->t_code = *buff++; /* transfer the tty name to the buffer */ for(cp = userdata->t_name; *buff != '\n'; ) *cp++ = *buff++; *cp = '\0'; return(0); } getttynam(userdata, name) struct ttys *userdata; char *name; { register int curline, result; /* the strategy of this routine is to cycle through the * tty file, starting from the current position, until * the tty 'name' is located. Note that the getttyent() * routine will automaticly rewind the input when appropriate. * Note also that the call to it ultimately updates the * variable 'linenum'. */ curline = linenum; do { result = getttyent(userdata); if(result == EOF) continue; if(result == BADDATA) break; if(strcmp(name, userdata->t_name) == 0) return(result); } while (curline != linenum); return(BADDATA); } if (gotquote) { *destptr++ = *srcptr++; break; } ind++; /* put value ahead of '=' */ if ( dlm != (char *) 0) { mmdf/lib/util/arg2str.c 444 0 12 6163 3620510425 10227 #include "util.h" /* convert an array of strings to one or more lines of 'arguments'. * * this is intended to be used along with the str2arg() routine. */ /*VARARGS*/ #ifdef NO_VARARGS arg2lstr (linelen, maxlen, buf, arg1, a,b,c,d,e,f,g,h,i,j,k,l,m) #else arg2lstr (linelen, maxlen, buf, arg1) /* convert list to argument array */ #endif NO_VARARGS int linelen; /* when to insert newlines; 0=> don't */ int maxlen; char *buf; char *arg1; /* merely the first of the list */ { arg2vstr (linelen, maxlen, buf, &arg1); } arg2vstr (linelen, maxlen, buf, argv) /* convert the list to a string */ int linelen; int maxlen; /* length of string */ char *buf; /* where to put the output string */ char **argv; /* the argument vector */ { unsigned totlen, /* total length of current line */ len; /* length of current argument */ unsigned gotdelim; /* a delimiter char is in arg */ unsigned gotpair; /* key/value pair */ char tmpstr[256]; /* LINESIZE = 256, string under construction */ register char *src, *dest; for (totlen = gotpair = 0, buf[0] = '\0'; *argv != (char *) 0; argv++) { if (gotpair == 0 && strcmp ("=", *argv) == 0) { dest = tmpstr; gotpair = 2; /* take the next two arguments */ continue; } for (src = *argv, gotdelim = FALSE; *src != '\0'; src++) switch (*src) { case ' ': case '\t': case '=': case ',': case ';': case ':': case '/': case '|': case '.': gotdelim = TRUE; goto nextone; } nextone: if (gotpair == 0) dest = tmpstr; if (gotdelim) *dest++ = '"'; for (src = *argv; *src != '\0'; src++) { switch (*src) { case '\b': *dest++ = '\\'; *dest++ = 'b'; break; case '\t': *dest++ = '\\'; *dest++ = 't'; break; case '\f': *dest++ = '\\'; *dest++ = 'f'; break; case '\r': *dest++ = '\\'; *dest++ = 'r'; break; case '\n': *dest++ = '\\'; *dest++ = 'n'; break; case '\\': /* Added by Doug Kingston */ case '\"': *dest++ = '\\'; *dest++ = *src; break; default: if (iscntrl (*src)) { *dest++ = '\\'; *dest++ = ((*src >> 6) & 07) + '0'; *dest++ = ((*src >> 3) & 07) + '0'; *dest++ = (*src & 07) + '0'; } else *dest++ = *src; } } if (gotdelim) *dest++ = '"'; if (src == *argv) /* null-valued argument data */ { if (totlen != 0) /* beyond the beginning, so double it */ strcat (buf, ","); else strcat (buf, " "); *dest++ = ','; } switch (gotpair) /* handle key/value differently */ { case 2: *dest++ = '='; gotpair--; continue; case 1: gotpair = 0; } *dest = '\0'; len = dest - tmpstr; if (totlen != 0) { if ((linelen > 0) && ((totlen + len) > linelen)) { strcat (buf, "\n\t"); totlen = 8; } else { strcat (buf, " "); totlen += 1; } } strcat (buf, tmpstr); totlen += len; } } d - 1] = "="; *destptr = '\0'; srcptr++; goto nextarg; case ' ': case '\t': case '\n': case '\r': case ',': case ';': case '#': case ':': case '/': case '|': if (gotquote) { /* don't interpret the char */ *destptr++ = *srcptr++; break; } if (isspace (lastdlm)) { if (isspace (*srcptr)) { /* shoummdf/lib/util/prefix.c 444 0 12 430 3620510425 10107 #include "util.h" /* lexical check if prefstr is at start of target */ prefix (prefstr, target) register char *target, *prefstr; { for (; *prefstr; target++, prefstr++) if (uptolow (*target) != uptolow (*prefstr)) return (FALSE); return (TRUE); } maxlen, buf, arg1) /* convert list to argument array */ #endif NO_VARARGS int linelen; /* when to insert newlines; 0=> don't */ int maxlen; char *buf; char *arg1; /* merely the first of the list */mmdf/lib/util/tai_packages.c 444 0 12 12736 3620510426 11302 #include "util.h" #include "ll_log.h" #include "cmd.h" extern int errno; /* put error code into here, like sys call */ extern char *tai_eptr; /* pointer to error text */ /* tailor other packages */ tai_pgm (argc, argv, nam, path) /* get name&path of a program */ int argc; /* number of values */ char *argv[]; /* list of values */ char **nam, /* where to put the name of the program */ **path; /* where to put path to program */ { register int ind; for (ind = 0; ind < argc; ind++) { if (lexequ ("=", argv[ind])) { /* key/value pair */ if ((ind += 2) >= argc) { errno = EFAULT; tai_eptr = argv[ind - 1]; /* point to 'key' */ argv[ind + 1] = 0; return (NOTOK); } if (lexequ ("path", argv[ind - 1])) *path = argv[ind]; else if (lexequ ("name", argv[ind - 1])) *nam = argv[ind]; else { errno = EINVAL; tai_eptr = argv[ind - 1]; /* point to 'key' */ argv[ind + 1] = 0; return (NOTOK); } } else { /* same string for both purposes */ *path = argv[ind]; *nam = argv[ind]; } } return (YES); } /**/ #define CMDLHDR 1 #define CMDLLEVEL 2 #define CMDLSIZE 3 #define CMDLSTAT 4 LOCVAR Cmd cmdlog[] = { "hdr", CMDLHDR, 1, "level", CMDLLEVEL, 1, "size", CMDLSIZE, 1, "stat", CMDLSTAT, 1, 0, 0, 0 }; #define CMDLPFAT 1 #define CMDLPTMP 2 #define CMDLPGEN 3 #define CMDLPBST 4 #define CMDLPFST 5 #define CMDLPPTR 6 #define CMDLPBTR 7 #define CMDLPFTR 8 LOCVAR Cmd cmdlevel[] = { "fat", CMDLPFAT, 0, "tmp", CMDLPTMP, 0, "gen", CMDLPGEN, 0, "bst", CMDLPBST, 0, "fst", CMDLPFST, 0, "ptr", CMDLPPTR, 0, "btr", CMDLPBTR, 0, "ftr", CMDLPFTR, 0, #ifdef NVRCOMPIL these are commented out in order to save a trivial amount of space. if you really want the synonyms, add them. (dhc) "llogfat", CMDLPFAT, 0, "llogtmp", CMDLPTMP, 0, "lloggen", CMDLPGEN, 0, "llogbst", CMDLPBST, 0, "llogfst", CMDLPFST, 0, "llogptr", CMDLPPTR, 0, "llogbtr", CMDLPBTR, 0, "llogftr", CMDLPFTR, 0, #endif 0, 0, 0 }; #define CMDLSCLS 1 #define CMDLSCYC 2 #define CMDLSWAT 3 #define CMDLSSOME 4 LOCVAR Cmd cmdlstat[] = { "close", CMDLSCLS, 0, "wait", CMDLSWAT, 0, "cycle", CMDLSCYC, 0, "some", CMDLSSOME, 0, #ifdef NVRCOMPIL "llogcls", CMDLSCLS, 0, "cls", CMDLSCLS, 0, "wat", CMDLSWAT, 0, "llogwat", CMDLSWAT, 0, "cyc", CMDLSCYC, 0, "llogcyc", CMDLSCYC, 0, "llogsome", CMDLSSOME, 0, #endif 0, 0, 0 }; tai_log (argc, argv, thelog) /* get ll_log structure values */ int argc; /* number of values */ char *argv[]; /* list of values */ LLog *thelog; /* the ll_log struct to modify */ { register int ind; for (ind = 0; ind < argc; ind++) { if (lexequ ("=", argv[ind])) { /* key/value pair */ ind += 2; switch (cmdsrch (argv[ind - 1], argc - ind + 1, cmdlog)) { case CMDLHDR: thelog -> ll_hdr = argv[ind]; break; case CMDLLEVEL: if (isdigit (argv[ind][0])) sscanf (argv[ind], "%d", &(thelog -> ll_level)); else switch (cmdsrch (argv[ind], 0, cmdlevel)) { case CMDLPFAT: thelog -> ll_level = LLOGFAT; break; case CMDLPTMP: thelog -> ll_level = LLOGTMP; break; case CMDLPGEN: thelog -> ll_level = LLOGGEN; break; case CMDLPBST: thelog -> ll_level = LLOGBST; break; case CMDLPFST: thelog -> ll_level = LLOGFST; break; case CMDLPPTR: thelog -> ll_level = LLOGPTR; break; case CMDLPBTR: thelog -> ll_level = LLOGBTR; break; case CMDLPFTR: thelog -> ll_level = LLOGFTR; break; default: errno = EINVAL; tai_eptr = argv[ind - 1]; argv[ind + 1] = 0; return (NOTOK); } break; case CMDLSIZE: thelog -> ll_msize = atoi (argv[ind]); break; case CMDLSTAT: if (isdigit (argv[ind][0])) /* not all stdio's have a test for octal (dhc) */ sscanf (argv[ind], "%o", &(thelog -> ll_stat)); else switch (cmdsrch (argv[ind], 0, cmdlstat)) { case CMDLSCLS: thelog -> ll_stat |= LLOGCLS; break; case CMDLSWAT: thelog -> ll_stat |= LLOGWAT; break; case CMDLSCYC: thelog -> ll_stat |= LLOGCYC; break; case CMDLSSOME: thelog -> ll_stat |= LLOGSOME; break; default: errno = EINVAL; tai_eptr = argv[ind - 1]; argv[ind + 1] = 0; return (NOTOK); } break; default: errno = EINVAL; tai_eptr = argv[ind - 1]; /* point to 'key' */ argv[ind + 1] = 0; return (NOTOK); } } else { if (ind == 0) thelog -> ll_file = argv[ind]; else { errno = EINVAL; tai_eptr = argv[ind - 1]; /* point to 'key' */ argv[ind + 1] = 0; return (NOTOK); } } } /***** disabled due to bug in mm_tai.c:tai_llev() that calles this /***** function with a garbage log pointer. /***** ll_close (thelog); /* start with a clean slate */ return (YES); } t (buf, "\n\t"); totlen = 8; mmdf/lib/util/tai_test.c 444 0 12 604 3671073214 10437 #include "util.h" main () { static char initfile[] = "init.tai"; char *taiargs[100]; int nargs; register int ind; if (tai_init (initfile) < OK) { perror ("init"); exit (); } while ((nargs = tai_get (100, taiargs)) > 0) { for (ind = 0; ind < nargs; ind++) printf ("'%s' ", taiargs[ind]); (void) putchar ('\n'); } if (nargs < 0) perror ("tai_get"); tai_end (); } 0 }; tai_log (argc, argv, thelog) /* get ll_log structure values */ int argc; /* number mmdf/lib/util/cmdsrch.c 444 0 12 1123 3620510426 10256 #include "util.h" #include "cmd.h" extern int errno; cmdsrch (str, argc, cmd) /* find command. return token reference */ char str[]; /* test string */ int argc; /* number of available arguments */ register Cmd *cmd; /* table of known commands */ { for ( ; (cmd -> cmdname) != (char *) 0; cmd++) if (lexequ (str, cmd -> cmdname)) { /* got a hit */ if (argc < cmd -> cmdnargs) { errno = EINVAL; return (NOTOK); } return (cmd -> cmdtoken); } return (NO); } g *thelog; /* the ll_log struct to modify */ { register int ind; for (ind = 0; ind < argc; ind++) { if (lexequ ("=", argv[ind])) { /* key/value pair */ ind += 2; switch (cmdsrch (argv[ind - 1], argc - ind + 1, cmdlog)) { case CMDLHDR: thelog -> ll_hdr = argv[ind]; break; case CMDLLEVEL: if (isdigit (argv[ind][0])) sscanf (argv[inmmdf/lib/util/cstr2arg.c 444 0 12 5715 3620510426 10375 #include "util.h" /* convert string into argument list * * stash a pointer to each field into the passed array. * any common seperators split the words. extra white-space * between fields is ignored. * * if the separator is '=', then the current argument position in * the array points to "=", the next the one is the key and the * value follows it. This permits detecting variable assignment, * in addition to positional arguments. * i.e., key=value -> = key value * * specially-interpreted characters: * space, tab, double-quote, backslash, comma, equal, slash, period, * semi-colon, colon, carriage return, and line-feed (newline). * preceding a special char with a backslash removes its * interpretation. a backslash not followed by a special is used * to preface an octal specification for one character * * a string begun with double-quote has only double-quote and * backslash as special characters. * * a field which begins with semi-colon is interpreted as marking the * rest of the line as a comment and it is skipped, as are blank lines */ /* Steve Kille * Modified version, which splits a string given a specific * separator */ extern int errno; cstr2arg (srcptr, max, argv, dlmchar)/* convert srcptr to argument list */ register char *srcptr; /* source data */ int max; /* maximum number of permitted fields */ char *argv[]; /* where to put the pointers */ char dlmchar; /* Delimiting character */ { char gotquote; /* currently parsing quoted string */ register int ind; register char *destptr; if (srcptr == 0) { errno = EINVAL; /* emulate system-call failure */ return (NOTOK); } for (ind = 0, max -= 2;; ind++) { if (ind >= max) { errno = E2BIG; /* emulate system-call failure */ return (NOTOK); } argv [ind] = srcptr; destptr = srcptr; /**/ for (gotquote = FALSE; ; ) { if (*srcptr == dlmchar) { if (gotquote) { /* don't interpret the char */ *destptr++ = *srcptr++; continue; } srcptr++; *destptr = '\0'; goto nextarg; } else { switch (*srcptr) { default: /* just copy it */ *destptr++ = *srcptr++; break; case '\"': /* beginning or end of string */ gotquote = (gotquote) ? FALSE : TRUE; srcptr++; /* just toggle */ break; case '\\': /* quote next character */ srcptr++; /* just skip the back-slash */ if (*srcptr >= '0' && *srcptr <= '7') { *destptr = '\0'; do *destptr = (*destptr << 3) | (*srcptr++ - '0'); while (*srcptr >= '0' && *srcptr <= '7'); destptr++; break; } /* otherwise DROP ON THROUGH */ else *destptr++ = *srcptr++; break; case '\0': *destptr = '\0'; ind++; argv[ind] = (char *) 0; return (ind); } } } nextarg: continue; } } aracters. * * a field which begins with semi-colmmdf/lib/util/getllog.v7.c 444 0 12 1546 3620510427 10635 #include "util.h" #include <utmp.h> /* get informtion about who is logged in */ /* this is modeled after the getpw v7 set of routines */ LOCVAR char LLPATH[] = "/usr/adm/lastlog"; LOCVAR FILE *llogfp = NULL; LOCVAR union tmpunion { char io[sizeof (struct utmp)]; struct utmp entry; } llogentry; setllog() { if( llogfp == NULL ) llogfp = fopen( LLPATH, "r" ); else rewind( llogfp ); } endllog() { if( llogfp != NULL ){ fclose( llogfp ); llogfp = NULL; } } struct utmp * getllog() { if (llogfp == NULL) { if( (llogfp = fopen( LLPATH, "r" )) == NULL ) return(0); } if (fread (llogentry.io, sizeof (struct utmp), 1, llogfp) != 1) return(0); return(&llogentry.entry); } struct utmp * getllnam(name) char name[]; { register struct utmp *p; while( (p = getllog()) && !equal(name,p->ut_name, strlen(name))); return(p); } ; ind++; argv[ind] = (char *) 0; return (ind); } } } nextarg: continue; } } aracters. * * a field which begins with semi-colmmdf/lib/util/sstr2arg.c 444 0 12 4657 3620510427 10422 #include "util.h" /* convert string into argument list * * This version takes an array of delimiters. <DPK@BRL> * * stash a pointer to each field into the passed array. * any common seperators split the words. extra white-space * between fields is ignored. * * specially-interpreted characters: * double-quote, backslash (preceding a special char with a * backslash removes its interpretation. A backslash not * followed by a special is used to preface an octal specification * for one character a string begun with double-quote has only * double-quote and backslash as special characters. */ extern int errno; /* convert srcptr to argument list */ sstr2arg (srcptr, max, argv, dlmstr) register char *srcptr; /* source data */ int max; /* maximum number of permitted fields */ char *argv[]; /* where to put the pointers */ char *dlmstr; /* Delimiting character */ { char gotquote; /* currently parsing quoted string */ register int ind; register char *destptr; if (srcptr == 0) { errno = EINVAL; /* emulate system-call failure */ return (NOTOK); } for (ind = 0, max -= 2;; ind++) { if (ind >= max) { errno = E2BIG; /* emulate system-call failure */ return (NOTOK); } /* Skip leading white space */ for (; *srcptr == '\t' || *srcptr == ' '; srcptr++); argv [ind] = srcptr; destptr = srcptr; for (gotquote = FALSE; ; ) { register char *cp; for (cp = dlmstr; (*cp != '\0') && (*cp != *srcptr); cp++); if (*cp != '\0') { if (gotquote) { /* don't interpret the char */ *destptr++ = *srcptr++; continue; } srcptr++; *destptr = '\0'; goto nextarg; } else { switch (*srcptr) { default: /* just copy it */ *destptr++ = *srcptr++; break; case '\"': /* beginning or end of string */ gotquote = (gotquote) ? FALSE : TRUE; srcptr++; /* just toggle */ break; case '\\': /* quote next character */ srcptr++; /* just skip the back-slash */ if (*srcptr >= '0' && *srcptr <= '7') { *destptr = '\0'; do *destptr = (*destptr << 3) | (*srcptr++ - '0'); while (*srcptr >= '0' && *srcptr <= '7'); destptr++; break; } else *destptr++ = *srcptr++; break; case '\0': *destptr = '\0'; ind++; argv[ind] = (char *) 0; return (ind); } } } nextarg: continue; } } /* where to put the pointers */ char dlmchar; /* Delimiting charactermmdf/lib/util/getwho.c 444 0 12 241 3620510427 10111 getwho (realid, effecid) /* user info for V7 Unix */ int *realid, *effecid; { *realid = getuid (); *effecid = geteuid(); } o = E2BIG; /* emulate system-call failure */ return (NOTOK); } /* Skip leading white space */ for (; *srcptr == '\t' || *srcptr == ' '; srcptr++); argv [ind] = srcptr; destptr = srcptr; for (gotquote = FALSE; ; ) { register char *cp; for (cp = dlmstr; (*cp != '\0') && (*cp != *srcptr); cp++); if (*cp != '\mmdf/lib/util/lk_lock.4.2.c 444 0 12 4527 3671073215 10574 #include "util.h" #include <sys/file.h> /* * Standardized file-locking package (4.2BSD) * * This version assumes the existence of the 4.2BSD flock() system call. */ extern int errno; /* simulate system error problems */ #ifdef DEBUG #include "ll_log.h" extern LLog *logptr; #endif /**/ lk_open (file, access, lockdir, lockfile, maxtime) char *file; /* file to be locked */ int access; /* read-write permissions */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ int maxtime; /* maybe break lock after it is this old */ { register int fd; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_open (%s,%d,%d)", file, access, maxtime); #endif if ((fd = open (file, access | O_NDELAY)) < 0 || flock (fd, LOCK_EX|LOCK_NB) < 0) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "open notok (err %d)", errno); #endif if (fd >= 0) (void) close (fd); return (NOTOK); } return (fd); } lk_close (fd, file, lockdir, lockfile) int fd; char *file; /* -- Ignored -- */ char *lockdir; /* -- Ignored -- */ char *lockfile; /* -- Ignored -- */ { register int retval; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_close (%d)", fd); #endif if (fd < 0) return (OK); retval = close (fd); return (retval); } /**/ FILE * lk_fopen (file, access, lockdir, lockfile, maxtime) char *file; /* file to be locked */ char *access; /* read-write permissions */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ int maxtime; /* maybe break lock after it is this old */ { register int fd; register FILE *fp; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%d)", file, access, maxtime); #endif if ((fd = open(file, ((access[0]=='r' && access[1]==0) ? 0 : 2) | O_NDELAY)) < 0 || flock (fd, LOCK_EX|LOCK_NB) < 0) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "open notok"); #endif if (fd >= 0) (void) close (fd); return (NULL); } if ((fp = fdopen (fd, access)) == NULL) { (void) close (fd); return ((FILE *) NULL); } return (fp); } lk_fclose (fp, file, lockdir, lockfile) FILE *fp; char *file; /* --Ignored-- */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ { #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_fclose ()"); #endif switch ((int)fp) { case EOF: case NULL: return (OK); } return (fclose (fp)); } argv[ind] = (char *) 0; return (ind); } } } nextarg: continue; } } /* where to put the pointers */ char dlmchar; /* Delimiting charactermmdf/lib/util/gwdir.2.9.c 444 0 12 10226 3620510430 10275 #include "util.h" #include "conf.h" #include <sys/stat.h> struct kb_dir { short kd_ino; char kd_name[14]; }; LOCVAR char wd_DOT[] = "."; LOCVAR char wd_DOTDOT[] = ".."; LOCVAR char wd_read[] = "r"; LOCVAR char wkd_name[128]; /* max size of path string */ LOCVAR jmp_buf wd_retloc; LOCVAR union wd /* unioned for making i/o cleaner */ { char io[sizeof (struct kb_dir)]; struct kb_dir dir; } wd_entry; LOCVAR char dummy; /* will be null - kludge to stop 14 char filenames */ /* breaking this routine */ LOCVAR FILE * wd_fp; char * gwdir () /* what is current working directory? */ { struct stat wd_stat; short lastkd_ino; /* last inode num */ char buf[BUFSIZ]; if (!setjmp (wd_retloc)) { /* end due to eof or err */ if(wd_fp != (FILE *)EOF && wd_fp != NULL) setbuf (wd_fp, NULL); if (wd_fp == (FILE *) EOF || wd_fp == (FILE *) NULL || fclose (wd_fp) == EOF || wkd_name[0] == '\0' || chdir (wkd_name) < OK) return ((char *) NOTOK); return (wkd_name); } wkd_name[0] = '\0'; lastkd_ino = -1; /* force to be set on first pass */ if (stat (wd_DOT, &wd_stat) < 0 || /* get inode num for this dir */ (wd_fp = fopen (wd_DOTDOT, wd_read)) == NULL) longjmp (wd_retloc, TRUE); /* set to compare DOTDOT inodes */ setbuf (wd_fp, buf); for (;;) { for (;;) { /* cycle thru dir's inodes */ fread (wd_entry.io, sizeof (struct kb_dir), 1, wd_fp); if (ferror (wd_fp) || feof (wd_fp)) longjmp (wd_retloc, TRUE); if (wd_entry.dir.kd_ino == wd_stat.st_ino) break; /* got a match */ } if (wd_fp == (FILE *) EOF || wd_fp == (FILE *) NULL || fclose (wd_fp) == EOF || ferror (wd_fp) || feof (wd_fp)) longjmp (wd_retloc, TRUE); if (wd_entry.dir.kd_ino == 1) /* hit the root directory */ wd_ckroot (); /* try to find its name */ if (wd_entry.dir.kd_ino == lastkd_ino) /* we are cycling at top */ longjmp (wd_retloc, TRUE); else /* remember for next level up */ lastkd_ino = wd_entry.dir.kd_ino; wd_cat (); /* add current name to end */ if (chdir (wd_DOTDOT) < OK || /* set up for next pass this dir */ stat (wd_DOT, &wd_stat) < OK || freopen (wd_DOTDOT, wd_read, wd_fp) == NULL) longjmp (wd_retloc, TRUE); } } LOCFUN wd_ckroot () /* check root dir for filesys name */ { static char wd_root[] = "/"; struct stat wd_stat, owd_stat; /* to save device info */ if (stat (wd_entry.dir.kd_name, &wd_stat) < 0) longjmp (wd_retloc, TRUE); owd_stat.st_dev = wd_stat.st_dev; if (chdir (wd_root) < OK) longjmp (wd_retloc, TRUE); if (freopen (wd_root, wd_read, wd_fp) == NULL) longjmp (wd_retloc, TRUE); for (;;) { /* read thru "/" dir */ fread (wd_entry.io, sizeof (struct kb_dir), 1, wd_fp); if (ferror (wd_fp) || feof (wd_fp)) longjmp (wd_retloc, TRUE); if (wd_entry.dir.kd_ino == 0) /* not allocated */ continue; if (stat (wd_entry.dir.kd_name, &wd_stat) < 0) longjmp (wd_retloc, TRUE); if (wd_stat.st_dev == owd_stat.st_dev) { /* device types match */ wd_stat.st_mode &= S_IFMT; if (wd_stat.st_mode == S_IFDIR) break; /* and it is a directory */ } } if (strcmp (wd_entry.dir.kd_name, wd_DOT) != 0 && strcmp (wd_entry.dir.kd_name, wd_DOTDOT) != 0) wd_cat (); /* it has a real text name, too */ wd_entry.dir.kd_name[0] = '\0'; /* hack, to force "/" at front */ wd_cat (); longjmp (wd_retloc, TRUE); } LOCFUN wd_cat () { extern char *strcpy (), *strcat (); if (wkd_name[0] == 0) strcpy (wkd_name, wd_entry.dir.kd_name); else { strcat (wkd_name, "/"); strcat (wkd_name, wd_entry.dir.kd_name); } } |= LLOGWAT; break; case CMDLSCYC: thelog -> ll_stat |= LLOGCYC; break; case CMDLSSOME: thelog -> ll_stat |= LLOGSOME; break; default: errno = EINVAL; tai_eptr = argv[ind - 1]; argv[ind + 1] = 0; return (NOTOK); } break; default: errno = EINVAL; tai_eptr = argv[ind - 1]; /* pmmdf/lib/util/lk_lock.5.2.c 444 0 12 5352 3671073216 10573 #include "util.h" /* <lockf.h> may have to be replaced with <unistd.h> */ /* on some systems such as the 3B2 */ #include <lockf.h> #include <fcntl.h> /* * Standardized file-locking package (System 5 Rel 2) * * This version assumes the existence of the System 5 lockf() system call * Adapted by Mark Vasoll <vasoll@a.cs.okstate.edu> from the 4.2 BSD version */ extern int errno; /* simulate system error problems */ #ifdef DEBUG #include "ll_log.h" extern LLog *logptr; #endif /**/ lk_open (file, access, lockdir, lockfile, maxtime) char *file; /* file to be locked */ int access; /* read-write permissions */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ int maxtime; /* maybe break lock after it is this old */ { register int fd; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_open (%s,%d,%s,%s,%d)", file, access, lockdir, lockfile, maxtime); #endif if ((fd = open (file, access | O_CREAT)) < 0 || lockf (fd, F_TLOCK, 0) < 0) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "open notok (err %d)", errno); #endif if (fd >= 0) (void) close (fd); return (NOTOK); } return (fd); } lk_close (fd, file, lockdir, lockfile) int fd; char *file; /* file to be locked */ char *lockdir; /* directory to put parallel file into */ char *lockfile; /* file to lock against */ { register int retval; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_close (%d,%s,%s,%s)", fd, file, lockdir, lockfile); #endif if (fd < 0) return (OK); retval = close (fd); return (retval); } /**/ FILE * lk_fopen (file, access, lockdir, lockfile, maxtime) char *file; /* file to be locked */ char *access; /* read-write permissions */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ int maxtime; /* maybe break lock after it is this old */ { register int fd; register FILE *fp; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s,%d)", file, access, lockdir, lockfile, maxtime); #endif if ((fd = open(file, (access[0]=='r' && access[1]==0 ? 0 : 2) | O_CREAT)) < 0 || lockf (fd, F_TLOCK, 0) < 0) { #ifdef DEBUG ll_err (logptr, LLOGBTR, "open notok"); #endif if (fd >= 0) (void) close (fd); return (NULL); } if ((fp = fdopen (fd, access)) == NULL) { (void) close (fd); return ((FILE *) NULL); } return (fp); } lk_fclose (fp, file, lockdir, lockfile) FILE *fp; char *file; /* --Ignored-- */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ { register int retval; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_fclose (%s,%s,%s)", file, lockdir, lockfile); #endif switch (fp) { case EOF: case NULL: return (OK); } retval = fclose (fp); return (retval); } OT) != 0) wd_cat (); /* it has a real text name, too */ wd_entry.dir.kd_name[0] = '\0'; /* hack, to force "/" at front */ wd_cat (); longjmp (wd_retloc, TRUE); } LOCFUN wd_cat () { extern char *strcpy (), *strcat (); if (mmdf/lib/libfix 555 0 12 271 3620510430 6673 set -x set -e mkdir LIBFIX.$$ cp $1 LIBFIX.$$/oldlib.a (cd LIBFIX.$$; ar x oldlib.a; rm -f oldlib.a; \ ar cr newlib.a `lorder *.o | tsort`) cp LIBFIX.$$/newlib.a $1 rm -rf LIBFIX.$$ le, lockdir, lockfile) int fd; char *file; /* file to be locked */ char *lockdir; /* directory to put parallel file into */ char *lockfile; /* file to lock against */ { register int retval; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_close (%d,%s,%s,%s)", fd, file, lockdir, lockfile); #endif if (fd < 0) retummdf/lib/Makefile 444 0 12 2236 3652767212 7174 # # Makefile for the MMDF library (libmmdf.a) # # The paths below are relative from the directories below this one. # # The RANLIB define should be "sh libfix" for systems that don't have # the ranlib command. Systems that do have ranlib should set # RANLIB to "ranlib". "libfix" is a Bourne shell file that will # use lorder and tsort to reorder the library. # MAKE = make RANLIB = ranlib SUBDIRS = addr dial mmdf table util ALLSUBDIRS = addr dial mmdf table util default: remake reorder remake: for dir in $(SUBDIRS); \ do (cd $${dir}; echo "Running make in $${dir}"; \ gen ${MFLAGS} -${MAKEFLAGS}); \ done install: default @echo "Library is up to date, nothing else to do." reorder: reorder-done reorder-done: libmmdf.a -$(RANLIB) libmmdf.a -touch reorder-done depend: for dir in $(SUBDIRS); \ do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done lint: for dir in $(SUBDIRS); \ do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done cat */llib-l*.ln > llib-lmmdf.ln clean: -rm -f *-made libmmdf.a make.out core llib-lmmdf.ln reorder-done for dir in $(SUBDIRS); \ do (cd $${dir}; $(MAKE) -f Makefile.real clean); done ALWAYS: */ char *access; /* read-write permissions */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ int maxtime; /* maybe break lock after it is this old */ { register int fd; register FILE *fp; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s,%d)", file, access, lockdir, lockfile, maxtime); #endif if ((mmdf/libndir/ 755 0 12 0 3635165674 6330 mmdf/libndir/Makefile 444 0 12 1134 3620510431 10025 # @(#)Makefile 4.2 (Berkeley) 8/2/82 DESTDIR = CFLAGS= -O OBJS= closedir.o opendir.o readdir.o seekdir.o telldir.o libndir.a: ${OBJS} ar rv libndir.a ${OBJS} ranlib libndir.a ${DESTDIR}/usr/include/dir.h: dir.h cp dir.h ${DESTDIR}/usr/include/dir.h install: libndir.a cp libndir.a ${DESTDIR}/usr/lib/libndir.a cp directory.3 /usr/man/man3/directory.3 cp dir.h ${DESTDIR}/usr/include/dir.h clean: rm -f libndir.a ${OBJS} .c.o: ${CC} ${CFLAGS} -c $*.c closedir.o: closedir.c dir.h opendir.o: opendir.c dir.h readdir.o: readdir.c dir.h seekdir.o: seekdir.c dir.h telldir.o: telldir.c dir.h do (cd $${dir}; $(MAKE) -f Makefile.real clean); done ALWAYS: */ char *access; /* read-write permissions */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ int maxtime; /* maybe break lock after it is this old */ { register int fd; register FILE *fp; #ifdef DEBUG ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s,%d)", file, access, lockdir, lockfile, maxtime); #endif if ((mmdf/libndir/Read_Me 444 0 12 1132 3620510431 7602 The purpose of this library is to simulate the new flexable length directory names on top of the old directory structure. It allows programs to be converted to the new directory access interface, so that they need only be relinked when 4.2bsd becomes available. The following files should be placed in /usr/src/lib/libndir; then run ``make'' followed by ``make install''. The manual page is available as ``man directory''. The library is accessed by specifying "-lndir" as the last argument to the compile line. (eg ``cc -o foo foo.c -lndir'') Report problems to mckusick@berkeley or ucbvax!mckusick. of the old directory structure. It allows programs to be converted to the new directory access interface, so that they need only be relinked when 4.2bsd becomes available. The following files should be placed in /usr/src/lib/libndir; then run ``make'' followed by ``make install''. The manual page is available as ``man directory''. The library is accessed by specifying "-lndir" as the last argument to the compile line.mmdf/libndir/closedir.c 444 0 12 456 3620510431 10323 /* Copyright (c) 1982 Regents of the University of California */ static char sccsid[] = "@(#)closedir.c 4.2 3/10/82"; #include <sys/types.h> #include "dir.h" /* * close a directory. */ closedir(dirp) register DIR *dirp; { close(dirp->dd_fd); dirp->dd_fd = -1; dirp->dd_loc = 0; free(dirp); } /usr/src/lib/libndir; then run ``make'' followed by ``make install''. The manual page is available as ``man directory''. The library is accessed by specifying "-lndir" as the last argument to the compile line.mmdf/libndir/dir.h 444 0 12 2245 3620510431 7320 /* Copyright (c) 1982 Regents of the University of California */ /* @(#)ndir.h 4.4 3/30/82 */ /* * This sets the "page size" for directories. * Requirements are DEV_BSIZE <= DIRBLKSIZ <= MINBSIZE with * DIRBLKSIZ a power of two. * Dennis Ritchie feels that directory pages should be atomic * operations to the disk, so we use DEV_BSIZE. */ #define DIRBLKSIZ 512 /* * This limits the directory name length. Its main constraint * is that it appears twice in the user structure. (u. area) */ #define MAXNAMLEN 255 struct direct { long d_ino; short d_reclen; short d_namlen; char d_name[MAXNAMLEN + 1]; /* typically shorter */ }; struct _dirdesc { int dd_fd; long dd_loc; long dd_size; char dd_buf[DIRBLKSIZ]; }; /* * useful macros. */ #undef DIRSIZ #define DIRSIZ(dp) \ ((sizeof(struct direct) - MAXNAMLEN + (dp)->d_namlen + sizeof(ino_t) - 1) &\ ~(sizeof(ino_t) - 1)) typedef struct _dirdesc DIR; #ifndef NULL #define NULL 0 #endif /* * functions defined on directories */ extern DIR *opendir(); extern struct direct *readdir(); extern long telldir(); extern long lseek(); extern seekdir(); #define rewinddir(dirp) seekdir((dirp), 0L) extern closedir(); if if (fd >= 0) (void) close (fd); return (NULL); } if ((fp = fdopen (fd, access)) == NULL) { (void) close (fd); return ((FILE *) NULL); } return (fp); } lk_fclose (fp, file, lockdir, lockfile) FILE *fp; char *file; /* --Ignored-- */ char *lockdir; /* --Ignored-- */ char *lockfile; /* --Ignored-- */ { regismmdf/libndir/directory.3 444 0 12 4312 3620510432 10457 .TH DIRECTORY 3 3/1/82 .UC 4 .SH NAME opendir, readdir, telldir, seekdir, rewinddir, closedir \- directory operations .SH SYNOPSIS .B #include <ndir.h> .PP .SM .B DIR .B *opendir(filename) .br .B char *filename; .PP .SM .B struct direct .B *readdir(dirp) .br .B DIR *dirp; .PP .SM .B long .B telldir(dirp) .br .B DIR *dirp; .PP .SM .B seekdir(dirp, loc) .br .B DIR *dirp; .br .B long loc; .PP .SM .B rewinddir(dirp) .br .B DIR *dirp; .PP .SM .B closedir(dirp) .br .B DIR *dirp; .SH DESCRIPTION .I Opendir opens the directory named by .I filename and associates a .I directory stream with it. .I Opendir returns a pointer to be used to identify the .I directory stream in subsequent operations. The pointer .SM .B NULL is returned if .I filename cannot be accessed or is not a directory. .PP .I Readdir returns a pointer to the next directory entry. It returns .B NULL upon reaching the end of the directory or detecting an invalid .I seekdir operation. .PP .I Telldir returns the current location associated with the named .I directory stream. .PP .I Seekdir sets the position of the next .I readdir operation on the .I directory stream. The new position reverts to the one associated with the .I directory stream when the .I telldir operation was performed. Values returned by .I telldir are good only for the lifetime of the DIR pointer from which they are derived. If the directory is closed and then reopened, the .I telldir value may be invalidated due to undetected directory compaction. It is safe to use a previous .I telldir value immediately after a call to .I opendir and before any calls to .I readdir. .PP .I Rewinddir resets the position of the named .I directory stream to the beginning of the directory. .PP .I Closedir causes the named .I directory stream to be closed, and the structure associated with the DIR pointer to be freed. .PP The preferred way to search the current directory for entry ``name'' is: .br len = strlen(name); .br dirp = opendir("."); .br for (dp = readdir(dirp); dp != NULL; dp = readdir(dir)) .br if (dp->d_namlen == len && !strcmp(dp->d_name, name)) { .br closedir(dirp); .br return FOUND; .br } .br closedir(dirp); .br return NOT_FOUND; .SH "SEE ALSO" open(2), close(2), read(2), lseek(2) helog -> ll_stat |= LLOGCYC; break; case CMDLSSOME: thelog -> ll_stat |= LLOGSOME; break; default: errno = EINVAL; tai_eptr = argv[ind - 1]; argv[ind + 1] = 0; return (NOTOK); } break; default: errno = EINVAL; tai_eptr = argv[ind - 1]; /* pmmdf/libndir/opendir.c 444 0 12 1067 3620510432 10177 /* Copyright (c) 1982 Regents of the University of California */ static char sccsid[] = "@(#)opendir.c 4.2 3/10/82"; #include <sys/types.h> #include <sys/stat.h> #include "dir.h" extern char *malloc(); /* * open a directory. */ DIR * opendir(name) char *name; { register DIR *dirp; struct stat sbuf; dirp = (DIR *)malloc(sizeof(DIR)); dirp->dd_fd = open(name, 0); if (dirp->dd_fd == -1) { free(dirp); return NULL; } fstat(dirp->dd_fd, &sbuf); if ((sbuf.st_mode & S_IFDIR) == 0) { free(dirp); return NULL; } dirp->dd_loc = 0; return dirp; } directory stream with it. .I Opendir returns a pointer to be used to identify the .I directory stream in subsequent operations. The pointer .SM .B NULL is returned if .I filename cannot be accessed or is not a directory. .PP .I Readdir returns a pointer to the next directory entry. It returns .B NULL upon reaching the end of the directory or detecting an invalid .I seekdir operation. .PP .I Telldir returns the current location associated with the named mmdf/libndir/readdir.c 444 0 12 2045 3620510432 10146 /* Copyright (c) 1982 Regents of the University of California */ static char sccsid[] = "@(#)readdir.c 4.2 3/12/82"; #include <sys/types.h> #include "dir.h" /* * read an old stlye directory entry and present it as a new one */ #define ODIRSIZ 14 struct olddirect { ino_t d_oino; char d_oname[ODIRSIZ]; }; /* * get next entry in a directory. */ struct direct * readdir(dirp) register DIR *dirp; { register struct olddirect *dp; static struct direct dir; for (;;) { if (dirp->dd_loc == 0) { dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); if (dirp->dd_size <= 0) return NULL; } if (dirp->dd_loc >= dirp->dd_size) { dirp->dd_loc = 0; continue; } dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc); dirp->dd_loc += sizeof(struct olddirect); if (dp->d_oino == 0) continue; dir.d_ino = dp->d_oino; strncpy(dir.d_name, dp->d_oname, ODIRSIZ); dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */ dir.d_namlen = strlen(dir.d_name); dir.d_reclen = DIRSIZ(&dir); return (&dir); } } ets the position of the next .I readdir operation on the .I directory stream. The new position reverts to the one associated with the .I directory stream when the .I telldir operation was performed. Values returned by .I telldir are good only for the lifetime of the DIR pointer from which they are derived. If the directory is closed and then reopened, the .I telldir value may be invalidated due to undetected directory compaction. It is safe to use a previous .I telldir vmmdf/libndir/seekdir.c 444 0 12 1336 3620510432 10164 /* Copyright (c) 1982 Regents of the University of California */ static char sccsid[] = "@(#)seekdir.c 4.3 2/25/82"; #include <sys/types.h> #include "dir.h" /* * seek to an entry in a directory. * Only values returned by ``telldir'' should be passed to seekdir. */ seekdir(dirp, loc) register DIR *dirp; long loc; { long curloc, base, offset; struct direct *dp; curloc = telldir(dirp); if (loc == curloc) return; base = loc & ~(DIRBLKSIZ - 1); offset = loc & (DIRBLKSIZ - 1); if (dirp->dd_loc != 0 && (curloc & ~(DIRBLKSIZ - 1)) == base) { dirp->dd_loc = offset; return; } lseek(dirp->dd_fd, base, 0); dirp->dd_loc = 0; while (dirp->dd_loc < offset) { dp = readdir(dirp); if (dp == NULL) return; } } ->dd_buf + dirp->dd_loc); dirp->dd_loc += sizeof(struct olddirect); if (dp->d_oino == 0) continue; dir.d_ino = dp->d_oino; strncpy(dir.d_name, dp->d_oname, ODIRSIZ); dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */ dir.d_namlen = strlen(dir.d_name); dir.d_reclen =mmdf/libndir/telldir.c 444 0 12 463 3620510433 10156 /* Copyright (c) 1982 Regents of the University of California */ static char sccsid[] = "@(#)telldir.c 4.1 2/21/82"; #include <sys/types.h> #include "dir.h" /* * return a pointer into a directory */ long telldir(dirp) DIR *dirp; { return (lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc); } rp; long loc; { long curloc, base, offset; struct direct *dp; curloc = telldir(dirp); if (loc == curloc) return; base = loc & ~(DIRBLKSIZ - 1); offset = loc & (DIRBLKSIZ - 1); if (dirp->dd_loc !mmdf/libndir/test.c 444 0 12 636 3620510433 7500 #include <sys/types.h> #include "dir.h" main(argc, argv) char **argv; { int len; DIR *dirp; struct direct *dp; len = strlen( argv[1] ); dirp = opendir( "." ); for( dp = readdir(dirp); dp != NULL; dp = readdir(dirp) ) if( dp->d_namlen == len && !strcmp(dp->d_name, argv[1])){ closedir(dirp); printf("Found %s.\n", dp->d_name); exit(0); } closedir(dirp); printf("Not found.\n"); exit( 1 ); } oc) return; base = loc & ~(DIRBLKSIZ - 1); offset = loc & (DIRBLKSIZ - 1); if (dirp->dd_loc !mmdf/libndir/llib-lndir.ln 444 0 12 2103 3620510433 10747 3] llib-lndir:closedir.c $ closedir ` € %] $ close € >] $ free Þ] llib-lndir:opendir.c h ` opendir £] "