BSD 4_3_Net_2 development
[unix-history] / usr / src / contrib / isode / others / tsbridge / tsbridge.c
/* tsbridge.c: transport bridge - jpo version ! */
#ifndef lint
static char *rcsid = "$Header: /f/osi/others/tsbridge/RCS/tsbridge.c,v 7.10 91/02/22 09:34:37 mrose Interim $";
#endif
/*
* $Header: /f/osi/others/tsbridge/RCS/tsbridge.c,v 7.10 91/02/22 09:34:37 mrose Interim $
*
* Contributed by Julian Onions, Nottingham University in the UK
*
*
* $Log: tsbridge.c,v $
* Revision 7.10 91/02/22 09:34:37 mrose
* Interim 6.8
*
* Revision 7.9 91/01/24 14:52:19 mrose
* update
*
* Revision 7.8 90/11/04 19:15:29 mrose
* update
*
* Revision 7.6 90/08/08 14:04:48 mrose
* stuff
*
* Revision 7.5 90/07/09 14:43:01 mrose
* sync
*
* Revision 7.4 90/03/19 14:27:00 mrose
* jpo
*
* Revision 7.2 90/01/11 18:36:55 mrose
* real-sync
*
* Revision 7.1 89/11/27 05:43:28 mrose
* sync
*
* Revision 7.0 89/11/23 22:11:12 mrose
* Release 6.0
*
*/
/*
* NOTICE
*
* Acquisition, use, and distribution of this module and related
* materials are subject to the restrictions of a license agreement.
* Consult the Preface in the User's Manual for the full terms of
* this agreement.
*
*/
#include <signal.h>
#include <stdio.h>
#include <varargs.h>
#include "manifest.h"
#include <sys/ioctl.h>
#ifdef BSD42
#include <sys/file.h>
#endif
#ifdef SYS5
#include <fcntl.h>
#endif
#include "tsap.h"
#include "logger.h"
#include "psap.h"
#include "tailor.h"
/* \f */
static int debug = 0;
static int nbits = FD_SETSIZE;
static LLog _pgm_log = {
"tsbridge.log", NULLCP, NULLCP,
LLOG_FATAL | LLOG_EXCEPTIONS | LLOG_NOTICE, LLOG_FATAL,
-1, LLOGCLS | LLOGCRT | LLOGZER, NOTOK
};
LLog *pgm_log = &_pgm_log;
static char *myname = "tsbridge";
typedef struct ContTbl {
struct TSAPaddr src;
struct TSAPaddr dest;
unsigned int flags;
#define CONN_STRICT 01
#define CONN_TRANS 02
#define CONN_NOMUNGE 04
#define CONN_FORCEMUNGE 010
} ContTbl;
ContTbl con_tbl[FD_SETSIZE];
int con_tbl_cnt = 0;
static struct TSAPaddr *maketa ();
static struct TSAPaddr *getnewta ();
static ContTbl *find_connection ();
static void read_file ();
static void adios (), advise ();
static void ts_adios (), ts_advise ();
static void ts_close (), ts_discon ();
static void tsbridge (), do_the_biz (), copy_tsdu (), arginit (), envinit ();
/* \f */
/* ARGSUSED */
main (argc, argv, envp)
int argc;
char **argv,
**envp;
{
struct TSAPdisconnect tds;
register struct TSAPdisconnect *td = &tds;
struct TSAPaddr tas, *ta = &tas;
int vecp;
char *vec[4];
arginit (argv);
envinit ();
for (vecp = 0; vecp < con_tbl_cnt; vecp++) {
advise (LLOG_TRACE, NULLCP, "Listening on %s",
taddr2str (&con_tbl[vecp].src));
if (TNetListen (&con_tbl[vecp].src, td) == NOTOK) {
advise (LLOG_FATAL, NULLCP, "Listen failed on \"%s\"",
taddr2str (&con_tbl[vecp].src));
ts_adios (td, "listen failed");
}
}
for (;;) {
if (TNetAcceptAux (&vecp, vec, NULLIP, ta, 0, NULLFD, NULLFD,
NULLFD, NOTOK, td) == NOTOK)
ts_adios (td, "accept failed");
if (vecp <= 0)
continue;
advise (LLOG_TRACE, NULLCP, "accepted new connection");
switch (TNetFork (vecp, vec, td)) {
case OK:
ll_hdinit (pgm_log, myname);
tsbridge (vecp, vec, ta);
exit (1);
/* NOTREACHED */
case NOTOK:
ts_advise (td, LLOG_EXCEPTIONS, "TNetFork failed");
break;
default:
break;
}
}
}
/* \f */
static void tsbridge (vecp, vec, ta)
int vecp;
char **vec;
struct TSAPaddr *ta;
{
struct TSAPstart tss;
register struct TSAPstart *ts = &tss;
struct TSAPdisconnect tds;
register struct TSAPdisconnect *td = &tds;
struct TSAPaddr *tota;
struct TSAPaddr *fromta;
struct TSAPconnect tcs;
struct TSAPconnect *tc = &tcs;
int sd;
ContTbl *ctp;
if (TInit (vecp, vec, ts, td) == NOTOK)
ts_adios (td, "T-CONNECT.INDICATION failed");
sd = ts -> ts_sd;
advise (LLOG_NOTICE, NULLCP,
"T-CONNECT.INDICATION: <%d, %s, %s, %d, %d>",
ts -> ts_sd, taddr2str (&ts -> ts_calling),
taddr2str (&ts -> ts_called),
ts -> ts_expedited, ts -> ts_tsdusize);
ctp = find_connection (ta);
if (ctp == NULL) {
ts_close (sd, "Unknown listener address");
exit (1);
}
advise (LLOG_TRACE, NULLCP, "Accepted from address %s",
taddr2str (&ctp -> src));
tota = getnewta (&ts -> ts_called, sd, ctp);
fromta = maketa (&ts -> ts_calling, tota -> ta_addrs[0].na_stack, ctp);
if ((ctp -> flags & CONN_TRANS) == 0) {
ts -> ts_expedited = 0;
if (ts -> ts_cc > 0) {
advise (LLOG_EXCEPTIONS, NULLCP,
"%d octets initial user-data",
ts -> ts_cc);
ts_close (sd, "initial user-data not allowed");
exit (1);
}
}
advise (LLOG_NOTICE, NULLCP,
"T-CONNECT.REQUEST: <%s, %s, %d, 0x%x/%d>",
taddr2str (fromta), taddr2str (tota), ts -> ts_expedited,
ts -> ts_data, ts -> ts_cc);
if (TConnRequest (fromta, tota, ts -> ts_expedited,
ts -> ts_data, ts -> ts_cc, &ts -> ts_qos,
tc, td) == NOTOK) {
ts_close (sd, "connection establishment failed");
ts_adios(td, "T-CONNECT.REQUEST");
}
if (TConnResponse (sd, NULLTA,
tc -> tc_expedited, tc -> tc_data, tc -> tc_cc,
&tc -> tc_qos, td) == NOTOK) {
ts_close (sd, "connection establishment failed");
ts_close (tc -> tc_sd, "connection establishment failed");
ts_adios (td, "T-CONNECT.RESPONSE");
}
do_the_biz (sd, tc -> tc_sd);
}
/* \f */
static void do_the_biz (sd1, sd2)
int sd1, sd2;
{
int nfds = 0;
fd_set rmask, imask;
struct TSAPdisconnect tds;
register struct TSAPdisconnect *td = &tds;
FD_ZERO (&rmask);
if (TSelectMask (sd1, &rmask, &nfds, td) == NOTOK
|| TSelectMask (sd2, &rmask, &nfds, td) == NOTOK)
ts_adios (td, "TSelectMask failed");
for (;;) {
imask = rmask;
if (xselect (nfds, &imask, NULLFD, NULLFD, NOTOK) == NOTOK)
adios ("select", "failed");
if (FD_ISSET (sd1, &imask))
copy_tsdu (sd1, sd2);
if (FD_ISSET (sd2, &imask))
copy_tsdu (sd2, sd1);
}
}
/* \f */
static void copy_tsdu (s1, s2)
int s1, s2;
{
struct TSAPdisconnect tds;
register struct TSAPdisconnect *td = &tds;
struct TSAPdata txs;
register struct TSAPdata *tx = &txs;
int result;
char *p;
SLOG (pgm_log, LLOG_DEBUG, NULLCP, ("copy_tsdu (%d -> %d)", s1, s2));
if (TReadRequest (s1, tx, OK, td) == NOTOK) {
switch (td -> td_reason) {
case DR_TIMER:
case DR_WAITING:
case DR_OPERATION:
case DR_PARAMETER:
ts_advise (td, LLOG_TRACE, "TReadRequest");
return;
case DR_NORMAL:
ts_discon (td, s2);
break;
default:
ts_adios (td, "TReadRequest");
}
}
if (tx -> tx_expedited) {
SLOG (pgm_log, LLOG_DEBUG, NULLCP, ("TExpdRequest"));
p = qb2str (&tx -> tx_qbuf);
result = TExpdRequest (s2, p, tx -> tx_cc, td);
free (p);
}
else {
struct qbuf *qb;
int uiocnt = 0;
struct udvec uvec[100];
int total;
total = uiocnt = 0;
for (qb = tx-> tx_qbuf.qb_forw; qb != &tx -> tx_qbuf;
qb = qb -> qb_forw) {
uvec[uiocnt].uv_base = qb -> qb_data;
uvec[uiocnt++].uv_len = qb -> qb_len;
total += qb -> qb_len;
if (uiocnt > 100)
adios (NULLCP, "Internal buffer overflow");
}
uvec[uiocnt].uv_base = NULLCP;
uvec[uiocnt].uv_len = 0;
if (tx -> tx_cc != total)
advise (LLOG_EXCEPTIONS, NULLCP,
"Mismatch in data %d != %d",
tx -> tx_cc, total);
SLOG (pgm_log, LLOG_DEBUG, NULLCP, ("TWriteRequest"));
result = TWriteRequest (s2, uvec, td);
}
TXFREE (tx);
if (result == NOTOK) {
if (td -> td_reason == DR_NORMAL)
ts_discon (td, s1);
ts_adios (td, tx -> tx_expedited ? "T-EXPEDITED-DATA.REQUEST"
: "T-DATA.REQUEST");
}
}
/* \f */
static void ts_discon (td, sd)
struct TSAPdisconnect *td;
int sd;
{
ts_close (sd, "Normal Disconnect");
ts_advise (td, LLOG_NOTICE, "T-DISCONNECT.INDICATION");
exit (0);
}
/* \f */
static void ts_close (sd, event)
int sd;
char *event;
{
struct TSAPdisconnect tds;
register struct TSAPdisconnect *td = &tds;
if (strlen (event) >= TD_SIZE)
event = NULLCP;
if (TDiscRequest (sd, event, event ? strlen (event) + 1: 0, td)
== NOTOK)
ts_advise (td, LLOG_EXCEPTIONS, "T-DISCONNECT.REQUEST");
}
/* \f */
static void ts_adios (td, event)
register struct TSAPdisconnect *td;
char *event;
{
ts_advise (td, LLOG_EXCEPTIONS, event);
exit (1);
}
/* \f */
static void ts_advise (td, code, event)
register struct TSAPdisconnect *td;
int code;
char *event;
{
char buffer[BUFSIZ];
if (td -> td_cc > 0)
(void) sprintf (buffer, "[%s] %*.*s",
TErrString (td -> td_reason),
td -> td_cc, td -> td_cc, td -> td_data);
else
(void) sprintf (buffer, "[%s]", TErrString (td -> td_reason));
advise (code, NULLCP, "%s: %s", event, buffer);
}
/* \f */
static int isnew = 1;
static struct TSAPaddr *getnewta (ta, sd, ctp)
struct TSAPaddr *ta;
int sd;
ContTbl *ctp;
{
static struct TSAPaddr newta;
struct TSAPaddr *nta = &newta;
char buffer[TSSIZE + 1];
int n, m;
isnew = 1;
if (ctp -> flags & CONN_TRANS) { /* make transparent address */
*nta = ctp -> dest; /* struct copy */
nta -> ta_selectlen = ta -> ta_selectlen;
bcopy (ta -> ta_selector, nta -> ta_selector,
ta -> ta_selectlen);
return nta;
}
/* do the real TS bridge stuff */
if ((m = ta -> ta_selectlen) == 0) {
ts_close (sd, "no transport selector");
adios (NULLCP, "no transport selector");
}
/* does this look like an encoded TSEL? */
n = ta -> ta_selector[0];
/*
advise (LLOG_TRACE, NULLCP, "n=%d,m=%d s[0] = %d, s[1] = %d",
n, m, ta -> ta_selector[0], ta -> ta_selector[1]);
*/
if (m > 4 &&
ta -> ta_selector[0] == ta -> ta_selector[1] &&
n > 2 && n <= m - 2) { /* encoded! */
bzero ((char *)nta, sizeof *nta);
nta -> ta_selectlen = m - n - 2;
if (nta -> ta_selectlen > 0)
bcopy (&ta -> ta_selector[n+2], nta -> ta_selector,
nta -> ta_selectlen);
if (norm2na (&ta -> ta_selector[2], n, nta -> ta_addrs) != OK) {
ts_close (sd, "undecodable address");
adios (NULLCP, "Can't decode address");
}
nta -> ta_naddr = 1;
return nta;
}
isnew = 0;
/* try old form... */
bcopy (ta -> ta_selector, buffer, ta -> ta_selectlen);
buffer[ta -> ta_selectlen] = NULL;
if ((nta = str2taddr (buffer)) == NULLTA) {
ts_close (sd, "unable to translate address");
adios (NULLCP, "unable to translate \"%s\"", buffer);
}
newta = *nta;
return &newta;
}
/* \f */
static struct TSAPaddr *maketa (ta, type, ctp)
struct TSAPaddr *ta;
long type;
ContTbl *ctp;
{
static struct TSAPaddr newta;
register struct TSAPaddr *nta = &newta;
char *p;
int i;
struct PSAPaddr pas;
struct PSAPaddr *pa = &pas;
if (ctp -> flags & CONN_NOMUNGE) {
*nta = *ta; /* struct copy */
}
if (!(ctp -> flags & CONN_NOMUNGE) || (ctp -> flags & CONN_FORCEMUNGE)) {
if (isnew == 0) {
bzero ((char *)pa, sizeof *pa);
pa -> pa_addr.sa_addr = *ta;
if ((p = _paddr2str (pa, NULLNA, -1)) == NULL ||
(nta -> ta_selectlen = strlen (p)) >= TSSIZE) {
if (ctp -> flags & CONN_STRICT)
adios (NULLCP, "new selector not encodable");
advise (LLOG_NOTICE, NULLCP,
"new selector not encodable");
nta -> ta_selectlen = 0;
}
else
bcopy (p, nta -> ta_selector, TSSIZE);
}
else {
struct NSAPaddr *nna = na2norm (&ta -> ta_addrs[0]);
if ((nta -> ta_selectlen = 2 + nna -> na_addrlen +
ta -> ta_selectlen) >= TSSIZE)
nta -> ta_selectlen = 0;
else {
bcopy (nna -> na_address, &nta -> ta_selector[2],
nna -> na_addrlen);
bcopy (ta -> ta_selector,
&nta -> ta_selector[2 + nna -> na_addrlen],
ta -> ta_selectlen);
nta -> ta_selector[0] = nta -> ta_selector[1] =
nna -> na_addrlen;
}
}
}
for (i = 0; i < ctp -> src.ta_naddr; i++) {
if (ctp -> src.ta_addrs[i].na_stack == type) {
/* our address */
nta -> ta_addrs[0] = ctp->src.ta_addrs[i];
nta -> ta_naddr = 1;
return nta;
}
}
/*
* This requires an explanation:
* If NOMUNGE && FORCEMUNGE we have a semi-transparent bridge
* and since [at least on my machine] the recipient of a "transparent"
* call sees it as coming from the bridge host, ie the effect is that
* of a strict call, the structure that is now in nta, viz:
* "calling address"/calling address
* is going to get clobbered and appear at the final host as originating
* "calling address"/bridge host
* anyway. This is what I want.
* => return nta
*/
if ((ctp -> flags & CONN_NOMUNGE)
&& (ctp -> flags & CONN_FORCEMUNGE)
&& !(ctp -> flags & CONN_STRICT)) {
return nta;
}
if (ctp -> flags & CONN_STRICT)
adios (NULLCP, "not listening on this network (%d)", type);
advise (LLOG_NOTICE, NULLCP,
"not listening on this network (%d)", type);
return ta;
}
/* \f */
static ContTbl *find_connection (ta)
struct TSAPaddr *ta;
{
ContTbl *ctp;
struct NSAPaddr *na1, *na2;
for (ctp = con_tbl; ctp < &con_tbl[con_tbl_cnt]; ctp ++) {
for (na1 = &ctp -> src.ta_addrs[0];
na1 < &ctp -> src.ta_addrs[ctp->src.ta_naddr]; na1++) {
for (na2 = &ta -> ta_addrs[0];
na2 < &ta -> ta_addrs[ta->ta_naddr]; na2 ++) {
if (na1 -> na_stack != na2 -> na_stack)
continue;
switch (na1 -> na_stack) {
case NA_NSAP:
if (na1 -> na_addrlen == na2 -> na_addrlen &&
bcmp (na1 -> na_address, na2 -> na_address,
na1 -> na_addrlen) == 0 &&
ta -> ta_selectlen == ctp -> src.ta_selectlen &&
bcmp (ta -> ta_selector, ctp -> src.ta_selector,
ta -> ta_selectlen) == 0)
return ctp;
break;
case NA_TCP:
if (na1 -> na_port == na2 -> na_port &&
strcmp (na1 -> na_domain, na2 -> na_domain) == 0)
return ctp;
break;
case NA_X25:
case NA_BRG:
if (na1 -> na_dtelen == na2 -> na_dtelen &&
bcmp (na1 -> na_dte, na2 -> na_dte,
na1 -> na_dtelen) == 0 &&
na1 -> na_pidlen == na2 -> na_pidlen &&
bcmp (na1 -> na_pid, na2 -> na_pid,
na1 -> na_pidlen) == 0)
return ctp;
break;
}
}
}
}
return NULL;
}
/* \f */
static void arginit (vec)
char **vec;
{
register char *ap;
register struct TSAPaddr *ta;
if (myname = rindex (*vec, '/'))
myname++;
if (myname == NULL || *myname == NULL)
myname = *vec;
for (vec++; ap = *vec; vec++) {
if (*ap == '-' && ap[1])
switch (*++ap) {
case 'T':
if ((ap = *++vec) == NULL || *ap == '-')
adios (NULLCP, "usage: %s -T tailorfile", myname);
(void) isodesetailor (ap);
isodetailor (myname, 0);
ll_hdinit (pgm_log, myname);
continue;
case 'a':
if ((ap = *++vec) == NULL || *ap == '-')
adios (NULLCP, "usage: %s -a address", myname);
if ((ta = str2taddr (ap)) == NULLTA)
adios (NULLCP, "bad address \"%s\"", ap);
con_tbl[0].src = *ta; /* struct copy */
con_tbl[0].flags = 0;
con_tbl_cnt = 1;
continue;
case 's':
con_tbl[0].flags |= CONN_STRICT;
continue;
default:
adios (NULLCP, "unknown switch -%s", ap);
}
else
break;
}
isodetailor (myname, 0);
ll_hdinit (pgm_log, myname);
for (; ap = *vec; vec++)
read_file (ap);
if (con_tbl_cnt <= 0) {
if ((ta = str2taddr (tsb_default_address)) == NULLTA)
adios (NULLCP, "bad default address \"%s\"",
tsb_default_address);
con_tbl[0].src = *ta; /* struct copy */
con_tbl_cnt = 1;
}
}
/* \f */
static void read_file (file)
char *file;
{
FILE *fp;
char buf[BUFSIZ];
char *vec[50];
char *ap;
int vecp, i;
ContTbl *ctp;
struct TSAPaddr *ta;
if (strcmp (file, "-") == 0)
fp = stdin;
else if ((fp = fopen (file, "r")) == NULL)
adios (file, "Can't open ");
while (fgets (buf, sizeof buf, fp) != NULLCP) {
if (buf[0] == '#' || buf[0] == '\n')
continue;
vecp = sstr2arg (buf, 50, vec, " \t,\n");
if (vecp <= 0)
continue;
if ((ta = str2taddr (vec[0])) == NULLTA)
adios (NULLCP, "Bad address \"%s\" in file %s", vec[0], file);
ctp = &con_tbl[con_tbl_cnt];
ctp -> src = *ta; /* struct copy */
con_tbl_cnt ++;
for (i = 1; i < vecp; i++) {
ap = vec[i];
if (*ap == '\0')
continue;
if (*ap == '-') {
switch (*++ap) {
case 's':
ctp -> flags |= CONN_STRICT;
break;
case 't':
ctp -> flags |= CONN_TRANS;
break;
case 'n':
ctp -> flags |= CONN_NOMUNGE;
break;
case 'f':
ctp -> flags |= CONN_FORCEMUNGE;
break;
default:
adios (NULLCP, "Unknown option -%c", *ap);
}
}
else {
if ((ta = str2taddr (ap)) == NULLTA)
adios (NULLCP, "Bad address \"%s\" in file %s",
ap, file);
ctp -> dest = *ta; /* struct copy */
ctp -> flags |= (CONN_TRANS|CONN_NOMUNGE);
}
}
}
if (strcmp (file, "-") != 0)
(void) fclose (fp);
}
/* \f */
static void envinit () {
int i,
sd;
nbits = getdtablesize ();
if (!(debug = isatty (2))) {
for (i = 0; i < 5; i++) {
switch (fork ()) {
case NOTOK:
sleep (5);
continue;
case OK:
break;
default:
_exit (0);
}
break;
}
(void) chdir ("/");
if ((sd = open ("/dev/null", O_RDWR)) == NOTOK)
adios ("/dev/null", "unable to read");
if (sd != 0)
(void) dup2 (sd, 0), (void) close (sd);
(void) dup2 (0, 1);
(void) dup2 (0, 2);
#ifdef SETSID
if (setsid () == NOTOK)
advise (LLOG_EXCEPTIONS, "failed", "setsid");
#endif
#ifdef TIOCNOTTY
if ((sd = open ("/dev/tty", O_RDWR)) != NOTOK) {
(void) ioctl (sd, TIOCNOTTY, NULLCP);
(void) close (sd);
}
#else
#ifdef SYS5
(void) setpgrp ();
(void) signal (SIGINT, SIG_IGN);
(void) signal (SIGQUIT, SIG_IGN);
#endif
#endif
}
else
ll_dbinit (pgm_log, myname);
#ifndef sun /* damn YP... */
for (sd = 3; sd < nbits; sd++)
if (pgm_log -> ll_fd != sd)
(void) close (sd);
#endif
(void) signal (SIGPIPE, SIG_IGN);
ll_hdinit (pgm_log, myname);
advise (LLOG_NOTICE, NULLCP, "starting");
}
/* \f ERRORS */
#ifndef lint
static void adios (va_alist)
va_dcl
{
va_list ap;
va_start (ap);
_ll_log (pgm_log, LLOG_FATAL, ap);
va_end (ap);
_exit (1);
}
#else
/* VARARGS */
static void adios (what, fmt)
char *what,
*fmt;
{
adios (what, fmt);
}
#endif
#ifndef lint
static void advise (va_alist)
va_dcl
{
int code;
va_list ap;
va_start (ap);
code = va_arg (ap, int);
_ll_log (pgm_log, code, ap);
va_end (ap);
}
#else
/* VARARGS */
static void advise (code, what, fmt)
char *what,
*fmt;
int code;
{
advise (code, what, fmt);
}
#endif