static char sccsid
[] = "@(#)cico.c 5.18 (Berkeley) %G%";
#if defined(VMS) && defined(BSDTCP)
/* Arguments to setdebug(): */
#define DBG_TEMP 0 /* Create a temporary audit file */
#define DBG_PERM 1 /* Create a permanent audit file */
#define DBG_CLEAN 2 /* Cleanup, discard temp file */
int turntime
= 30 * 60; /* 30 minutes expressed in seconds */
extern char MaxGrade
, DefMaxGrade
;
extern char Myfullname
[];
long Bytes_Sent
, Bytes_Received
;
char **Argv
= NULL
; /* pointer to argument vector */
char *LastArgv
= NULL
; /* end of argv */
* this program is used to place a call to a
* remote machine, login, and copy files between the two machines.
char wkpre
[NAMESIZE
], file
[NAMESIZE
];
char msg
[MAXFULLNAME
], *q
;
extern onintr(), timeout(), dbg_signal();
char rflags
[MAXFULLNAME
];
strcpy(Progname
, "uucico");
sigsetmask(0L); /* in case we inherit blocked signals */
signal(SIGPIPE
, onintr
); /* 4.1a tcp-ip stupidity */
signal(SIGUSR1
, dbg_signal
);
ret
= guinfo(getuid(), User
, msg
);
syslog(LOG_ERR
, "can't get uid");
while ((ret
= getopt(argc
, argv
, "RLd:g:p:r:s:x:t:")) != EOF
)
MaxGrade
= DefMaxGrade
= *optarg
;
strncpy(Rmtname
, optarg
, MAXBASENAME
);
Rmtname
[MAXBASENAME
] = '\0';
strcat(rflags
, argv
[optind
-1]);
turntime
= atoi(optarg
)*60;/* minutes to seconds */
case 'L': /* local calls only */
Hostnumber
= inet_addr(&argv
[1][2]);
fprintf(stderr
, "unknown flag %s (ignored)\n",
fprintf(stderr
, "unknown argument %s (ignored)\n",
if (Debug
&& Role
== MASTER
)
* Save start and extent of argv for setproctitle.
LastArgv
= argv
[argc
- 1] + strlen(argv
[argc
- 1]);
* detach uucico from controlling terminal
* to defend against rlogind sending us a SIGKILL (!!!)
if (Role
== MASTER
&& (ret
= open("/dev/tty", 2)) >= 0) {
ioctl(ret
, TIOCNOTTY
, STBNULL
);
if (getpgrp(0) == 0) { /* We have no controlling terminal */
openlog("uucico", LOG_PID
, LOG_UUCP
);
openlog("uucico", LOG_PID
);
unsetenv("TZ"); /* We don't want him resetting our time zone */
if (subchdir(Spool
) < 0) {
syslog(LOG_ERR
, "chdir(%s) failed: %m", Spool
);
setdebug ((Role
== SLAVE
) ? DBG_TEMP
: DBG_PERM
);
logent ("Local Enabled", "DEBUG");
* First time through: If we're the slave, do initial checking.
/* check for /etc/nologin */
if (access(NOLOGIN
, 0) == 0) {
logent(NOLOGIN
, "UUCICO SHUTDOWN");
logent("DEBUGGING", "continuing anyway");
* Determine if we are on TCPIP
DEBUG(4, "TCPIP connection -- ioctl-s disabled\n", CNULL
);
ret
= ioctl(Ifn
, TCGETA
, &Savettyb
);
Savettyb
.c_cflag
= (Savettyb
.c_cflag
& ~CS8
) | CS7
;
Savettyb
.c_oflag
|= OPOST
;
Savettyb
.c_lflag
|= (ISIG
|ICANON
|ECHO
);
ret
= ioctl(Ifn
, TIOCGETP
, &Savettyb
);
Savettyb
.sg_flags
|= ECHO
;
Savettyb
.sg_flags
&= ~RAW
;
* Initial Message -- tell them we're here, and who we are.
sprintf(msg
, "here=%s", Myfullname
);
signal(SIGALRM
, timeout
);
alarm(IsTcpIp
?MAXMSGTIME
*4:MAXMSGTIME
);
ret
= ioctl(Ifn
, TCSETA
, &Savettyb
);
ret
= ioctl(Ifn
, TIOCSETP
, &Savettyb
);
ret
= ioctl(Ifn
, TCSETA
, &Savettyb
);
ret
= ioctl(Ifn
, TIOCSETP
, &Savettyb
);
strncpy(Rmtname
, q
, MAXBASENAME
);
Rmtname
[MAXBASENAME
] = '\0';
* Now that we know who they are, give the audit file the right
DEBUG(4, "sys-%s\n", Rmtname
);
/* The versys will also do an alias on the incoming name */
/* If we don't know them, we won't talk to them... */
syslog(LOG_WARNING
, "Unknown host: %s", Rmtname
);
omsg('R', "You are unknown to me", Ofn
);
/* we must make sure they are really who they say they
* are. We compare the hostnumber with the number in the hosts
* table for the site they claim to be.
char *cpnt
, *inet_ntoa();
extern char PhoneNumber
[];
from
.sin_addr
.s_addr
= Hostnumber
;
from
.sin_family
= AF_INET
;
if (getpeername(Ifn
, &from
, &fromlen
) < 0) {
logent(Rmtname
, "NOT A TCP CONNECTION");
omsg('R', "NOT TCP", Ofn
);
hp
= gethostbyaddr(&from
.sin_addr
,
sizeof (struct in_addr
), from
.sin_family
);
/* security break or just old host table? */
logent(Rmtname
, "UNKNOWN IP-HOST Name =");
cpnt
= inet_ntoa(from
.sin_addr
),
logent(cpnt
, "UNKNOWN IP-HOST Number =");
sprintf(wkpre
, "%s/%s isn't in my host table",
logent(Rmtname
,"Request from IP-Host name =");
* The following is to determine if the name given us by
* the Remote uucico matches any of the names
* given its network number (remote machine) in our
* We could check the aliases, but that won't work in
* all cases (like if you are running the domain
* server, where you don't get any aliases). The only
* reliable way I can think of that works in ALL cases
* is too look up the site in L.sys and see if the
* sitename matches what we would call him if we
/* PhoneNumber contains the official network name of the host we are checking. (set in versys.c) */
if (sncncmp(PhoneNumber
, hp
->h_name
, SYSNSIZE
) == 0) {
logent(q
,"Found in host Tables");
logent(hp
->h_name
, "FORGED HOSTNAME");
logent(inet_ntoa(from
.sin_addr
), "ORIGINATED AT");
logent(PhoneNumber
, "SHOULD BE");
sprintf(wkpre
, "You're not who you claim to be: %s != %s", hp
->h_name
, PhoneNumber
);
else if (callback(Loginuser
)) {
logent("CALLBACK", "REQUIRED");
/* set up for call back */
systat(Rmtname
, SS_CALLBACK
, "CALLING BACK");
gename(CMDPRE
, Rmtname
, 'C', file
);
close(creat(subfile(file
), 0666));
logent("Remote Enabled", "DEBUG");
DEBUG(1, "Remote debug request ignored\n",
MaxGrade
= DefMaxGrade
= *++p
;
DEBUG(4, "MaxGrade set to %c\n", MaxGrade
);
if (strncmp(p
, "grade", 5) == 0) {
MaxGrade
= DefMaxGrade
= *p
++;
DEBUG(4, "MaxGrade set to %c\n", MaxGrade
);
setproctitle("%s: startup", Rmtname
);
if (callok(Rmtname
) == SS_BADSEQ
) {
logent("BADSEQ", "PREVIOUS");
omsg('R', "BADSEQ", Ofn
);
if ((ret
= gnxseq(Rmtname
)) == seq
) {
systat(Rmtname
, Stattype
[7], Stattext
[7]);
logent("BAD SEQ", "FAILED HANDSHAKE");
omsg('R', "BADSEQ", Ofn
);
if(setjmp(Pipebuf
)) { /* come here on SIGPIPE */
/* reenable logins on dialout */
setproctitle("looking for work");
ret
= gnsys(Rmtname
, Spool
, CMDPRE
);
setproctitle("%s: startup", Rmtname
);
} else if (Role
== MASTER
&& callok(Rmtname
) != 0) {
logent("SYSTEM STATUS", "CAN NOT CALL");
sprintf(wkpre
, "%c.%.*s", CMDPRE
, SYSNSIZE
, Rmtname
);
Bytes_Sent
= Bytes_Received
= 0L;
signal(SIGQUIT
, SIG_IGN
);
/* check for /etc/nologin */
if (access(NOLOGIN
, 0) == 0) {
logent(NOLOGIN
, "UUCICO SHUTDOWN");
logent("DEBUGGING", "continuing anyway");
if (Ifn
!= -1 && Role
== MASTER
) {
write(Ofn
, EOTMSG
, strlen(EOTMSG
));
if (mlock(Rmtname
) != SUCCESS
) {
DEBUG(1, "LOCKED: call to %s\n", Rmtname
);
setproctitle("%s: starting call", Rmtname
);
Ofn
= Ifn
= conn(Rmtname
);
sprintf(msg
, "(call to %s via %s)", Rmtname
, LineType
);
/* avoid excessive 'wrong time' info */
if (Stattype
[-Ofn
] != SS_WRONGTIME
){
systat(Rmtname
, Stattype
[-Ofn
], Stattext
[-Ofn
]);
logent(msg
, "SUCCEEDED");
* Determine if we are on TCPIP
DEBUG(4, "TCPIP connection -- ioctl-s disabled\n", CNULL
);
signal(SIGALRM
, timeout
);
alarm(IsTcpIp
?MAXMSGTIME
*4:MAXMSGTIME
*2);
DEBUG(4,"\nimsg failed: errno %d\n", errno
);
logent("imsg 1", _FAILED
);
alarm(IsTcpIp
?MAXMSGTIME
*4:MAXMSGTIME
);
if (MaxGrade
!= '\177') {
DEBUG(2, "Max Grade this transfer is %c\n", MaxGrade
);
sprintf(msg
, "%s -Q%d -p%c -vgrade=%c %s",
Myname
, seq
, MaxGrade
, MaxGrade
, rflags
);
sprintf(msg
, "%s -Q%d %s", Myname
, seq
, rflags
);
DEBUG(4, "msg-%s\n", msg
);
logent("imsg 2", _FAILED
);
logent("BAD SEQ", "FAILED HANDSHAKE");
systat(Rmtname
, SS_BADSEQ
, Stattext
[SS_BADSEQ
]);
if (strcmp(&msg
[1], "OK") != SAME
) {
logent(&msg
[1], "FAILED HANDSHAKE");
systat(Rmtname
, SS_INPROGRESS
,
strcmp(&msg
[1], "CB") == SAME
?
"AWAITING CALLBACK": "FAILED HANDSHAKE");
DEBUG(1, "Rmtname %s, ", Rmtname
);
DEBUG(1, "Role %s, ", Role
? "MASTER" : "SLAVE");
DEBUG(1, "Ifn - %d, ", Ifn
);
DEBUG(1, "Loginuser - %s\n", Loginuser
);
setproctitle("%s: %s", Rmtname
, Role
? "MASTER" : "SLAVE");
alarm(IsTcpIp
?MAXMSGTIME
*4:MAXMSGTIME
);
logent("(startup)", _FAILED
);
systat(Rmtname
, SS_FAIL
, ret
> 0 ? "CONVERSATION FAILED" :
char smsg
[BUFSIZ
], gmsg
[10], pmsg
[20], bpsmsg
[20];
extern char UsingProtocol
;
sprintf(bpsmsg
, " %s %d bps", &ttyn
[5], linebaudrate
);
if (UsingProtocol
!= 'g')
sprintf(pmsg
, " %c protocol", UsingProtocol
);
sprintf(gmsg
, " grade %c", MaxGrade
);
sprintf(smsg
, "(startup%s%s%s)", bpsmsg
, pmsg
, gmsg
);
systat(Rmtname
, SS_INPROGRESS
, "TALKING");
ret
= cntrl(Role
, wkpre
);
DEBUG(1, "cntrl - %d\n", ret
);
signal(SIGALRM
, timeout
);
sprintf(smsg
, "(conversation complete %ld sent %ld received)",
Bytes_Sent
, Bytes_Received
);
systat(Rmtname
, SS_FAIL
, "CONVERSATION FAILED");
alarm(IsTcpIp
?MAXMSGTIME
*4:MAXMSGTIME
);
DEBUG(4, "send OO %d,", ret
);
* cleanup and exit with "code" status
sleep(5); /* Wait for any pending output */
Savettyb
.c_cflag
|= HUPCL
;
(void) ioctl(0, TCSETA
, &Savettyb
);
(void) ioctl(0, TIOCHPCL
, STBNULL
);
(void) ioctl(0, TIOCCDTR
, STBNULL
);
(void) ioctl(0, TIOCSDTR
, STBNULL
);
(void) ioctl(0, TIOCGETP
, &Hupvec
);
(void) ioctl(0, TIOCSETP
, &Hupvec
);
(void) ioctl(0, TIOCSETP
, &Savettyb
);
/* make *sure* exclusive access is off */
(void) ioctl(0, TIOCNXCL
, STBNULL
);
write(Ofn
, EOTMSG
, strlen(EOTMSG
));
/* reenable logins on dialout */
DEBUG(1, "exit code %d\n", code
);
#ifdef DO_CONNECT_ACCOUNTING
fp
= fopen(DO_CONNECT_ACCOUNTING
, "a");
syslog(LOG_ALERT
, "fopen(%s) failed: %m",DO_CONNECT_ACCOUNTING
);
tm
= localtime(&StartTime
);
flags
= fcntl(fileno(fp
), F_GETFL
, 0);
fcntl(fileno(fp
), F_SETFL
, flags
|O_APPEND
);
fprintf(fp
,"%s %d %d%.2d%.2d %.2d%.2d %d %ld %s %ld %ld\n",
fprintf(fp
,"%s %d %d%02d%02d %02d%02d %d %ld %s %ld %ld\n",
Rmtname
, InitialRole
, tm
->tm_year
, tm
->tm_mon
+ 1,
tm
->tm_mday
, tm
->tm_hour
, tm
->tm_min
, tm
->tm_wday
,
(Now
.time
- StartTime
+ 59) / 60,
ttyn
== NULL
? "ttyp0" : &ttyn
[5],
Bytes_Sent
, Bytes_Received
);
#endif /* DO_CONNECT_ACCOUNTING */
* on interrupt - remove locks and exit
sprintf(str
, "(SIGNAL %d)", inter
);
if (*Rmtname
&& strncmp(Rmtname
, Myname
, MAXBASENAME
))
systat(Rmtname
, SS_FAIL
, str
);
sprintf(str
, "(conversation complete %ld sent %ld received)",
Bytes_Sent
, Bytes_Received
);
if (inter
== SIGPIPE
&& !onesys
)
* (SIGUSR1), and toggle debugging between 0 and 30.
* Handy for looking in on long running uucicos.
Debug
= (Debug
== 0) ? 30 : 0;
logent("Signal Enabled", "DEBUG");
* Check debugging requests, and open RMTDEBUG audit file if necessary. If an
* audit file is needed, the parm argument indicates how to create the file:
* DBG_TEMP - Open a temporary file, with filename = RMTDEBUG/pid.
* DBG_PERM - Open a permanent audit file, filename = RMTDEBUG/Rmtname.
* If a temp file already exists, it is mv'ed to be permanent.
* DBG_CLEAN - Cleanup; unlink temp files.
* Restrictions - this code can only cope with one open debug file at a time.
* Each call creates a new file; if an old one of the same name exists it will
char buf
[BUFSIZ
]; /* Buffer for building filenames */
static char *temp
= NULL
; /* Ptr to temporary file name */
static int auditopen
= 0; /* Set to 1 when we open a file */
struct stat stbuf
; /* File status buffer */
* If movement or cleanup of a temp file is indicated, we do it no
if (temp
!= CNULL
&& parm
== DBG_PERM
) {
sprintf(buf
, "%s/%s", RMTDEBUG
, Rmtname
);
if (link(temp
, buf
) != 0) {
syslog(LOG_ERR
, "RMTDEBUG link(%s,%s) failed: %m",
return; /* Gotta be in debug to come here. */
* If we haven't opened a file already, we can just return if it's
* alright to use the stderr we came in with. We can if:
* Role == MASTER, and Stderr is a regular file, a TTY or a pipe.
* Caution: Detecting when stderr is a pipe is tricky, because the 4.2
* man page for fstat(2) disagrees with reality, and System V leaves it
* undefined, which means different implementations act differently.
if (!auditopen
&& Role
== MASTER
) {
if (isatty(fileno(stderr
)))
else if (fstat(fileno(stderr
), &stbuf
) == 0) {
/* Is Regular File or Fifo */
if ((stbuf
.st_mode
& 0060000) == 0)
if ((stbuf
.st_mode
& S_IFMT
) == S_IFREG
||
stbuf
.st_mode
== 0) /* Is a pipe */
/* Is Regular File or Pipe */
if ((stbuf
.st_mode
& S_IFMT
) == S_IFREG
)
* We need RMTDEBUG directory to do auditing. If the file doesn't exist,
* then we forget about debugging; if it exists but has improper owner-
* ship or modes, we gripe about it in ERRLOG.
if (stat(RMTDEBUG
, &stbuf
) != SUCCESS
) {
if ((geteuid() != stbuf
.st_uid
) || /* We must own it */
((stbuf
.st_mode
& 0170700) != 040700)) { /* Directory, rwx */
syslog(LOG_ERR
, "%s: invalid directory mode: %o", RMTDEBUG
,
sprintf(buf
, "%s/%d", RMTDEBUG
, getpid());
temp
= malloc(strlen (buf
) + 1);
syslog(LOG_ERR
, "RMTDEBUG malloc failed: %m");
sprintf(buf
, "%s/%s", RMTDEBUG
, Rmtname
);
if (freopen(buf
, "w", stderr
) != stderr
) {
syslog(LOG_ERR
, "RMTDEBUG freopen(%s) failed: %m", buf
);
logent(Rmtname
, "TIMEOUT");
if (*Rmtname
&& strncmp(Rmtname
, Myname
, MAXBASENAME
)) {
systat(Rmtname
, SS_FAIL
, "TIMEOUT");
* clobber argv so ps will show what we're doing.
setproctitle(fmt
, a
, b
, c
)
(void) sprintf(buf
, fmt
, a
, b
, c
);
/* make ps print "(sendmail)" */
if (i
> LastArgv
- p
- 2) {