#include "uucp.h"
#if USE_RCS_ID
const char tli_rcsid[] = "$Id: tli.c,v 1.8 2002/03/05 19:10:42 ian Rel $";
#endif
#if HAVE_TLI
#include "sysdep.h"
#include "uudefs.h"
#include "uuconf.h"
#include "conn.h"
#include "system.h"
#include <errno.h>
#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#if HAVE_TIUSER_H
#include <tiuser.h>
#else
#if HAVE_XTI_H
#include <xti.h>
#else
#if HAVE_SYS_TLI_H
#include <sys/tli.h>
#endif
#endif
#endif
#if HAVE_STROPTS_H
#include <stropts.h>
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#else
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#endif
#ifndef O_RDONLY
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#endif
#ifndef FD_CLOEXEC
#define FD_CLOEXEC 1
#endif
#ifndef T_BIND
#define T_BIND T_BIND_STR
#endif
#ifndef T_CALL
#define T_CALL T_CALL_STR
#endif
extern int t_errno;
extern char *t_errlist[];
extern int t_nerr;
#ifndef HAVE_TIUSER_H
#ifndef t_alloc
extern pointer t_alloc ();
#endif
#endif
static const char *ztlierror P((void));
static void utli_free P((struct sconnection *qconn));
static boolean ftli_push P((struct sconnection *qconn));
static boolean ftli_open P((struct sconnection *qconn, long ibaud,
boolean fwait, boolean fuser));
static boolean ftli_close P((struct sconnection *qconn,
pointer puuconf,
struct uuconf_dialer *qdialer,
boolean fsuccess));
static boolean ftli_dial P((struct sconnection *qconn, pointer puuconf,
const struct uuconf_system *qsys,
const char *zphone,
struct uuconf_dialer *qdialer,
enum tdialerfound *ptdialer));
static const struct sconncmds stlicmds =
{
utli_free,
NULL,
NULL,
ftli_open,
ftli_close,
ftli_dial,
fsysdep_conn_read,
fsysdep_conn_write,
fsysdep_conn_io,
NULL,
NULL,
NULL,
fsysdep_conn_chat,
NULL
};
static const char *
ztlierror ()
{
if (t_errno == TSYSERR)
return strerror (errno);
if (t_errno < 0 || t_errno >= t_nerr)
return "Unknown TLI error";
return t_errlist[t_errno];
}
boolean
fsysdep_tli_init (qconn)
struct sconnection *qconn;
{
struct ssysdep_conn *q;
q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn));
q->o = -1;
q->ord = -1;
q->owr = -1;
q->zdevice = NULL;
q->iflags = -1;
q->iwr_flags = -1;
q->fterminal = FALSE;
q->ftli = TRUE;
q->ibaud = 0;
qconn->psysdep = (pointer) q;
qconn->qcmds = &stlicmds;
return TRUE;
}
static void
utli_free (qconn)
struct sconnection *qconn;
{
xfree (qconn->psysdep);
}
static boolean
ftli_push (qconn)
struct sconnection *qconn;
{
#ifdef I_PUSH
struct ssysdep_conn *qsysdep;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
if (qconn->qport->uuconf_u.uuconf_stli.uuconf_pzpush != NULL)
{
char **pz;
for (pz = qconn->qport->uuconf_u.uuconf_stli.uuconf_pzpush;
*pz != NULL;
pz++)
{
if (ioctl (qsysdep->o, I_PUSH, *pz) < 0)
{
ulog (LOG_ERROR, "ioctl (I_PUSH, %s): %s", *pz,
strerror (errno));
return FALSE;
}
}
}
else if (qconn->qport->uuconf_u.uuconf_stli.uuconf_fstream)
{
if (ioctl (qsysdep->o, I_PUSH, "tirdwr") < 0)
{
ulog (LOG_ERROR, "ioctl (I_PUSH, tirdwr): %s",
strerror (errno));
return FALSE;
}
}
if (qconn->qport->uuconf_u.uuconf_stli.uuconf_fstream)
qsysdep->ftli = FALSE;
#endif
return TRUE;
}
static boolean
ftli_open (qconn, ibaud, fwait, fuser)
struct sconnection *qconn;
long ibaud;
boolean fwait;
boolean fuser ATTRIBUTE_UNUSED;
{
struct ssysdep_conn *qsysdep;
const char *zdevice;
char *zfreedev;
const char *zservaddr;
char *zfreeaddr;
uid_t ieuid;
gid_t iegid;
boolean fswap;
struct t_bind *qtbind;
struct t_call *qtcall;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
zdevice = qconn->qport->uuconf_u.uuconf_stli.uuconf_zdevice;
if (zdevice == NULL)
zdevice = qconn->qport->uuconf_zname;
zfreedev = NULL;
if (*zdevice != '/')
{
zfreedev = zbufalc (sizeof "/dev/" + strlen (zdevice));
sprintf (zfreedev, "/dev/%s", zdevice);
zdevice = zfreedev;
}
fswap = fwait && geteuid () != 0;
if (fswap)
{
if (! fsuser_perms (&ieuid, &iegid))
{
ubuffree (zfreedev);
return FALSE;
}
}
qsysdep->o = t_open (zdevice, O_RDWR, (struct t_info *) NULL);
if (qsysdep->o < 0)
{
if (fswap)
(void) fsuucp_perms ((long) ieuid, (long) iegid);
ulog (LOG_ERROR, "t_open (%s): %s", zdevice, ztlierror ());
ubuffree (zfreedev);
return FALSE;
}
if (fcntl (qsysdep->o, F_SETFD,
fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0)
{
if (fswap)
(void) fsuucp_perms ((long) ieuid, (long) iegid);
ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
ubuffree (zfreedev);
(void) t_close (qsysdep->o);
qsysdep->o = -1;
return FALSE;
}
qsysdep->iflags = fcntl (qsysdep->o, F_GETFL, 0);
if (qsysdep->iflags < 0)
{
if (fswap)
(void) fsuucp_perms ((long) ieuid, (long) iegid);
ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
ubuffree (zfreedev);
(void) t_close (qsysdep->o);
qsysdep->o = -1;
return FALSE;
}
qsysdep->ipid = getpid ();
if (! fwait)
{
ubuffree (zfreedev);
if (t_bind (qsysdep->o, (struct t_bind *) NULL,
(struct t_bind *) NULL) < 0)
{
ulog (LOG_ERROR, "t_bind: %s", ztlierror ());
(void) t_close (qsysdep->o);
qsysdep->o = -1;
return FALSE;
}
return TRUE;
}
qtbind = (struct t_bind *) t_alloc (qsysdep->o, T_BIND, T_ALL);
if (qtbind == NULL)
{
if (fswap)
(void) fsuucp_perms ((long) ieuid, (long) iegid);
ulog (LOG_FATAL, "t_alloc (T_BIND): %s", ztlierror ());
}
zservaddr = qconn->qport->uuconf_u.uuconf_stli.uuconf_zservaddr;
if (zservaddr == NULL)
{
if (fswap)
(void) fsuucp_perms ((long) ieuid, (long) iegid);
ulog (LOG_FATAL, "Can't run as TLI server; no server address");
}
zfreeaddr = zbufcpy (zservaddr);
qtbind->addr.len = cescape (zfreeaddr);
if (qtbind->addr.len > qtbind->addr.maxlen)
{
if (fswap)
(void) fsuucp_perms ((long) ieuid, (long) iegid);
ulog (LOG_FATAL, "%s: TLI server address too long (max %d)",
zservaddr, qtbind->addr.maxlen);
}
memcpy (qtbind->addr.buf, zfreeaddr, qtbind->addr.len);
ubuffree (zfreeaddr);
qtbind->qlen = 5;
if (t_bind (qsysdep->o, qtbind, (struct t_bind *) NULL) < 0)
{
if (fswap)
(void) fsuucp_perms ((long) ieuid, (long) iegid);
ulog (LOG_FATAL, "t_bind (%s): %s", zservaddr, ztlierror ());
}
if (fswap)
{
if (! fsuucp_perms ((long) ieuid, (long) iegid))
ulog (LOG_FATAL, "Could not swap back to UUCP user permissions");
}
(void) t_free ((pointer) qtbind, T_BIND);
qtcall = (struct t_call *) t_alloc (qsysdep->o, T_CALL, T_ALL);
if (qtcall == NULL)
ulog (LOG_FATAL, "t_alloc (T_CALL): %s", ztlierror ());
while (! FGOT_SIGNAL ())
{
int onew;
pid_t ipid;
DEBUG_MESSAGE0 (DEBUG_PORT,
"ftli_open: Waiting for connections");
if (t_listen (qsysdep->o, qtcall) < 0)
ulog (LOG_FATAL, "t_listen: %s", ztlierror ());
onew = t_open (zdevice, O_RDWR, (struct t_info *) NULL);
if (onew < 0)
ulog (LOG_FATAL, "t_open (%s): %s", zdevice, ztlierror ());
if (fcntl (onew, F_SETFD,
fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0)
ulog (LOG_FATAL, "fcntl (FD_CLOEXEC): %s", strerror (errno));
if (t_bind (onew, (struct t_bind *) NULL, (struct t_bind *) NULL) < 0)
ulog (LOG_FATAL, "t_bind: %s", ztlierror ());
if (t_accept (qsysdep->o, onew, qtcall) < 0)
{
if (t_errno != TLOOK)
ulog (LOG_FATAL, "t_accept: %s", ztlierror ());
if (t_rcvdis (qsysdep->o, (struct t_discon *) NULL) < 0)
ulog (LOG_FATAL, "t_rcvdis: %s", ztlierror ());
(void) t_close (onew);
continue;
}
DEBUG_MESSAGE0 (DEBUG_PORT,
"ftli_open: Got connection; forking");
ipid = ixsfork ();
if (ipid < 0)
ulog (LOG_FATAL, "fork: %s", strerror (errno));
if (ipid == 0)
{
ulog_close ();
(void) t_close (qsysdep->o);
qsysdep->o = onew;
if (! ftli_push (qconn))
_exit (EXIT_FAILURE);
ipid = ixsfork ();
if (ipid < 0)
{
ulog (LOG_ERROR, "fork: %s", strerror (errno));
_exit (EXIT_FAILURE);
}
if (ipid != 0)
_exit (EXIT_SUCCESS);
ulog_id (getpid ());
return TRUE;
}
(void) t_close (onew);
(void) ixswait ((unsigned long) ipid, (const char *) NULL);
}
usysdep_exit (FALSE);
return FALSE;
}
static boolean
ftli_close (qconn, puuconf, qdialer, fsuccess)
struct sconnection *qconn;
pointer puuconf;
struct uuconf_dialer *qdialer;
boolean fsuccess;
{
struct ssysdep_conn *qsysdep;
boolean fret;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
fret = TRUE;
if (qsysdep->o >= 0)
{
if (qsysdep->ftli)
{
if (t_close (qsysdep->o) < 0)
{
ulog (LOG_ERROR, "t_close: %s", ztlierror ());
fret = FALSE;
}
}
else
{
if (close (qsysdep->o) < 0)
{
ulog (LOG_ERROR, "close: %s", strerror (errno));
fret = FALSE;
}
}
qsysdep->o = -1;
}
if (qsysdep->ipid != getpid ())
fret = FALSE;
return fret;
}
static boolean
ftli_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialerfound)
struct sconnection *qconn;
pointer puuconf;
const struct uuconf_system *qsys;
const char *zphone;
struct uuconf_dialer *qdialer;
enum tdialerfound *ptdialerfound;
{
struct ssysdep_conn *qsysdep;
char **pzdialer;
const char *zaddr;
struct t_call *qtcall;
char *zescape;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
*ptdialerfound = DIALERFOUND_FALSE;
pzdialer = qconn->qport->uuconf_u.uuconf_stli.uuconf_pzdialer;
if (*pzdialer == NULL)
pzdialer = NULL;
zaddr = zphone;
if (pzdialer != NULL
&& (strcmp (pzdialer[0], "TLI") == 0
|| strcmp (pzdialer[0], "TLIS") == 0))
{
if (pzdialer[1] == NULL)
++pzdialer;
else
{
if (strcmp (pzdialer[1], "\\D") != 0
&& strcmp (pzdialer[1], "\\T") != 0)
zaddr = pzdialer[1];
pzdialer += 2;
}
}
if (zaddr == NULL)
{
ulog (LOG_ERROR, "No address for TLI connection");
return FALSE;
}
qtcall = (struct t_call *) t_alloc (qsysdep->o, T_CALL, T_ADDR);
if (qtcall == NULL)
{
ulog (LOG_ERROR, "t_alloc (T_CALL): %s", ztlierror ());
return FALSE;
}
zescape = zbufcpy (zaddr);
qtcall->addr.len = cescape (zescape);
if (qtcall->addr.len > qtcall->addr.maxlen)
{
ulog (LOG_ERROR, "%s: TLI address too long (max %d)", zaddr,
qtcall->addr.maxlen);
ubuffree (zescape);
return FALSE;
}
memcpy (qtcall->addr.buf, zescape, qtcall->addr.len);
ubuffree (zescape);
if (t_connect (qsysdep->o, qtcall, (struct t_call *) NULL) < 0)
{
if (t_errno != TLOOK)
ulog (LOG_ERROR, "t_connect: %s", ztlierror ());
else
{
if (t_rcvdis (qsysdep->o, (struct t_discon *) NULL) < 0)
ulog (LOG_ERROR, "t_rcvdis: %s", ztlierror ());
else
ulog (LOG_ERROR, "Connection refused");
}
return FALSE;
}
if (! ftli_push (qconn))
return FALSE;
if (pzdialer != NULL && *pzdialer != NULL)
{
if (! fconn_dial_sequence (qconn, puuconf, pzdialer, qsys, zphone,
qdialer, ptdialerfound))
return FALSE;
}
return TRUE;
}
#endif