* Frank Pronk (...!ubc-vision!pronk)
* Laboratory for Computational Vision
* University of British Columbia
#include <netccitt/x25.h>
#define MAXARGS 10 /* maximum size of server argument list */
#define X25NET 0 /* no ITI parameters */
#define CCITT1978 1 /* 1978 CCITT standard parameter set */
#define CCITT1980 2 /* 1980 CCITT standard parameter set */
char pibuf
[BUFSIZ
], fibuf
[BUFSIZ
];
char console
[] = "/dev/console";
char *tracefn
; /* trace file name */
struct termios pt
, old_pt
;
struct sockaddr_x25 sock
;
char ccitt1978_prof
[] = { /* initial profile */
Q_BIT
, X29_SET_AND_READ_PARMS
,
X29_ECHO_CODE
, 1, /* echo on */
X29_FORWARDING_SIGNAL_CODE
, 126, /* forward on all cntl */
X29_IDLE_TIMER_CODE
, 0, /* off */
X29_AUX_DEV_CONTROL_CODE
, 0, /* off */
X29_RECEIVE_NET_MSGS_CODE
, 1, /* xmit network msgs */
X29_BREAK_PROCEDURE_CODE
, 21,
X29_PADDING_CODE
, 0, /* off */
X29_LINE_FOLDING_CODE
, 0, /* off */
X29_TRANSMISSION_SPEED_CODE
, 0,
X29_XON_XOFF_CODE
, 1, /* enable XON/XOFF */
char ccitt1980_prof
[] = { /* initial profile */
Q_BIT
, X29_SET_AND_READ_PARMS
,
X29_ECHO_CODE
, 1, /* echo on */
X29_FORWARDING_SIGNAL_CODE
, 126, /* forward on all cntl */
X29_IDLE_TIMER_CODE
, 0, /* off */
X29_AUX_DEV_CONTROL_CODE
, 0, /* off */
X29_RECEIVE_NET_MSGS_CODE
, 1, /* xmit network msgs */
X29_BREAK_PROCEDURE_CODE
, 21,
X29_PADDING_CODE
, 0, /* off */
X29_LINE_FOLDING_CODE
, 0, /* off */
X29_TRANSMISSION_SPEED_CODE
, 0,
X29_XON_XOFF_CODE
, 1, /* enable XON/XOFF */
X29_LF_AFTER_CR
, 4, /* lf after cr from terminal */
X29_CHARACTER_DELETE
, CERASE
,
X29_LINE_DISPLAY
, CRPRNT
,
char datapac_prof
[] = { /* Canadian X.25 network */
Q_BIT
, X29_SET_AND_READ_PARMS
,
X29_ECHO_CODE
, 1, /* echo on */
X29_FORWARDING_SIGNAL_CODE
, 126, /* forward on all cntl */
X29_IDLE_TIMER_CODE
, 0, /* off */
X29_AUX_DEV_CONTROL_CODE
, 0, /* off */
X29_RECEIVE_NET_MSGS_CODE
, 1, /* xmit network msgs */
X29_BREAK_PROCEDURE_CODE
, 21,
X29_PADDING_CODE
, 0, /* off */
X29_LINE_FOLDING_CODE
, 0, /* off */
X29_TRANSMISSION_SPEED_CODE
, 0,
X29_XON_XOFF_CODE
, 1, /* enable XON/XOFF */
X29_LF_AFTER_CR
, 4, /* lf after cr from terminal */
X29_CHARACTER_DELETE
, CERASE
,
X29_LINE_DISPLAY
, CRPRNT
,
* This rubbish can be removed when Datapac
* adopts the 1980 standard parameter set.
0, 0, /* national parameter marker */
char *n_name
; /* generic name */
short n_type
; /* see defines above */
char *n_profile
; /* initial profile */
short n_proflen
; /* length of n_profile */
"1978", CCITT1978
, ccitt1978_prof
, sizeof(ccitt1978_prof
),
"ccitt1978", CCITT1978
, ccitt1978_prof
, sizeof(ccitt1978_prof
),
"1980", CCITT1980
, ccitt1980_prof
, sizeof(ccitt1980_prof
),
"ccitt1980", CCITT1980
, ccitt1980_prof
, sizeof(ccitt1980_prof
),
"datapac", CCITT1980
, datapac_prof
, sizeof(datapac_prof
),
* If this host doesn't support X.25, give up.
s
= socket(AF_CCITT
, SOCK_STREAM
, 0);
if (s
< 0 && errno
== EPROTONOSUPPORT
)
fatal(2, "X.25 is not supported on this machine");
netp
= lookup ("ccitt1978");
sock
.x25_family
= AF_CCITT
;
sock
.x25_len
= sizeof(sock
);
sock
.x25_opts
.op_flags
= X25_MQBIT
;
sock
.x25_udata
[0] = ITI_CALL
;
for (argv
++; argc
> 1; argc
--, argv
++)
for (p
= *argv
+1; *p
; p
++)
if ((netp
= lookup (*argv
)) == 0)
fatal(1, "Unknown network type");
strcpy (sock
.x25_udata
, *argv
);
sock
.x25_opts
.op_flags
|= X25_REVERSE_CHARGE
;
else fatal(1, "missing trace file");
fatal (1, "usage: x29d -b -c nettype -p protocol -r -t trace_file server");
fatal (1, "no server specified");
while ((s
= socket(AF_CCITT
, SOCK_STREAM
, 0)) < 0)
while (bind(s
, (caddr_t
)&sock
, sizeof (sock
)) < 0)
signal(SIGCHLD
, reapchild
);
struct sockaddr_x25 from
;
int fromlen
= sizeof (from
);
if ((net
= accept(s
, (caddr_t
)&from
, &fromlen
)) < 0) {
while ((pid
= fork()) < 0)
signal(SIGCHLD
, SIG_DFL
);
for (np
= nets
; np
->n_name
; np
++)
if (strcmp (np
->n_name
, name
) == 0)
while (wait3(&status
, WNOHANG
, 0) > 0)
char *envinit
[] = { "TERM=ccitt", 0 };
* Get a pty, scan input lines.
struct sockaddr_x25
*who
;
packet_size
= 1 << who
->x25_opts
.op_psize
;
i
= forkpty(&pty
, line
, &term
, 0);
fatalperror("fork", errno
);
struct sockaddr_x25
*who
;
register struct hostent
*hp
= 0;
struct hostent
*getx25hostbyaddr();
while (*p
&& *p
!= ' ' && *p
!= '\t') /* split program from args */
while (*p
== ' ' || *p
== '\t')
if (ap
< &args
[MAXARGS
-2])
if (strcmp(p
, "-ccitt") == 0)
while (*p
&& *p
!= ' ' && *p
!= '\t')
if (stat (server
, &st
) < 0)
fatalperror (server
, errno
);
* For security: if running as root, switch to user
* and group id of server. This prevents privately
* maintainted or bogus servers from getting super-
if (hp
= getx25hostbyaddr (who
->x25_addr
))
*ap
++ = (char *)who
->x25_addr
;
* If the -ccitt flag was given, add another argument
* to tell login if charging is being reversed or not.
*ap
++ = (who
->x25_opts
.op_flags
& X25_REVERSE_CHARGE
) ? "y" : "n";
fatalperror (server
, errno
);
char buf
[BUFSIZ
], *index();
(void) write(f
, p
, (index(p
, '\n')-p
)+1);
extern char *sys_errlist
[];
strcat(buf
, sys_errlist
[err
]);
* Main loop. Select from pty and network, and
* hand data to iti receiver.
register int pcc
, fcc
, cc
;
int pgrp
, x25_interrupt(), on
= 1;
ioctl(net
, FIONBIO
, (char *)&on
);
ioctl(pty
, FIONBIO
, (char *)&on
);
/*ioctl(pty, TIOCREMECHO, (char *)&on); /* enable special pty mode */
/* new equivalent is no processing in pty, no echo, but let
user set modes and have either remote end do line mode processing
ioctl(pty
, TIOCEXT
, (char *)&on
);
ioctl(pty
, TIOCPKT
, (char *)&on
);
ioctl(pty
, TIOCGETA
, (char *)&pt
);
signal(SIGPIPE
, SIG_IGN
); /* why not cleanup? --kwl */
signal(SIGTSTP
, SIG_IGN
);
signal(SIGCHLD
, cleanup
);
signal(SIGTTOU
, SIG_IGN
);
signal(SIGURG
, x25_interrupt
); /* for out-of-band data */
(void) write(net
, netp
->n_profile
, netp
->n_proflen
);
* Show banner that getty never gave.
gethostname(hostname
, sizeof (hostname
));
strcpy(pibuf
+1, "\r\n\r\n4.3 BSD UNIX (");
strcpy(pibuf
+1, "\r\n\r\n4.2 BSD UNIX (");
strcat(pibuf
+1, hostname
);
strcat(pibuf
+1, ")\r\n\r\n");
pcc
= strlen(pibuf
+1) + 1;
* Never look for input if there's still
* stuff in the corresponding output buffer
if (fcc
>= 0) /* net connection alive? */
if (fcc
&& pcc
>= 0) /* output pending? */
if (pcc
>= 0) /* pty still alive? */
if (pcc
>= 0) /* pty connection alive? */
if (pcc
&& fcc
>= 0) /* output pending? */
if (fcc
>= 0) /* net still alive? */
if (ibits
== 0 && obits
== 0)
(void) select(16, &ibits
, &obits
, (int *)0, 0);
if (ibits
== 0 && obits
== 0) {
* Something to read from the network...
if (fcc
== 0 && (ibits
& (1 << net
))) {
fcc
= read(net
, fibuf
, BUFSIZ
);
if (fcc
< 0 && errno
== EWOULDBLOCK
)
x29d_trace("netread", fibuf
, fcc
);
* Something to read from the pty...
if (ibits
& (1 << pty
)) {
pcc
= read(pty
, pibuf
, packet_size
+1);
if (pcc
< 0 && errno
== EWOULDBLOCK
)
else if (pibuf
[0] != 0) { /* non-data packet */
if (pibuf
[0] & TIOCPKT_IOCTL
) {
bcopy(pibuf
+ 1, (char *)&pt
, pcc
);
pcc
= set_x29_parameters();
if ((obits
& (1<<net
)) && pcc
> 0)
if ((cc
= write(net
, pibuf
, pcc
)) == pcc
) {
x29d_trace("netwrite", pibuf
, pcc
);
extern char *sys_errlist
[];
strlen(sys_errlist
[errno
]));
if ((obits
& (1 << pty
)) && fcc
> 0) {
* Send interrupt to process on other side of pty.
* If it is in raw mode, just write NULL;
* otherwise, write intr char.
signal(SIGURG
, x25_interrupt
);
tcsetattr(pty
, TCSAFLUSH
, &tt
);
(void) write(pty
, &tt
.c_cc
[VINTR
], 1);
(void) write(pty
, "\0", 1);
p
= line
+ sizeof(_PATH_DEV
) - 1;
* Map unix tty modes and special characters
char *lim
= p
+ sizeof (pt
);
if (netp
->n_type
== X25NET
)
if ((old_pt
.c_lflag
& ICANON
) != (pt
.c_lflag
& ICANON
)) {
/* this precipitates more junk of the same
* sort that caused our call here, but we can't
* turn it off since something may be going on in our progeny.
* Instead, we'll check the next time around to see if nothing
* has changed, and skip informing the network.
if (bcmp((char *)&pt
, (char *)&old_pt
, sizeof (pt
)) == 0)
/* *p++ = X29_ESCAPE_TO_CMD_CODE; *p++ = (f & (RAW|CBREAK)) == 0;*/
*p
++ = X29_ESCAPE_TO_CMD_CODE
; *p
++ = (pt
.c_lflag
& ICANON
) != 0;
*p
++ = X29_ECHO_CODE
; *p
++ = (pt
.c_lflag
& ECHO
) != 0;
*p
++ = X29_FORWARDING_SIGNAL_CODE
;
*p
++ = (pt
.c_lflag
& ISIG
) ? 0 : 126;
* The value of 10 (0.5 seconds) for the idle timer when
* in raw or cbreak mode is a compromise value. For good
* interactive response this value should be as low as
* possible; for reasonable efficiency with file transfers
* this value should be at fairly high. This number should
* be changed to suit local requirements.
/**p++ = X29_IDLE_TIMER_CODE; *p++ = (f & (RAW|CBREAK)) ? 10 : 0;*/
*p
++ = X29_IDLE_TIMER_CODE
; *p
++ = (pt
.c_lflag
& ICANON
) ? 0 : 10;
/**p++ = X29_AUX_DEV_CONTROL_CODE;*p++ = (f & TANDEM) != 0;*/
*p
++ = X29_AUX_DEV_CONTROL_CODE
;*p
++ = (pt
.c_iflag
& IXOFF
) != 0;
*p
++ = X29_XON_XOFF_CODE
; *p
++ = (pt
.c_iflag
& IXON
) != 0;
if (netp
->n_type
== CCITT1980
) {
/* *p++ = (f & (RAW|CBREAK) || (f & ECHO) == 0) ? 0 : 4; */
*p
++ = ((pt
.c_lflag
& (ICANON
| ECHO
)) != (ICANON
| ECHO
)) ?
*p
++ = X29_EDITING
; *p
++ = (pt
.c_lflag
& ICANON
) != 0;
(0 == (pt.c_lflag & ICANON) || pt.c_cc[x] == _POSIX_VDISABLE) ? 0 : pt.c_cc[x]
*p
++ = X29_CHARACTER_DELETE
; *p
++ = ctlchar(VERASE
);
*p
++ = X29_LINE_DELETE
; *p
++ = ctlchar(VKILL
);
*p
++ = X29_LINE_DISPLAY
; *p
++ = ctlchar(VREPRINT
);
/* Have to be careful writing to pty. The pad will forward control
* characters without necessarily sending an interrupt so if ISIG and
* ICANNON are set, must inspect line for quit or interrupt or suspend.
#define is_ctl(x) (pt.c_cc[x] == *(cc_t *)cp)
if ((pt
.c_lflag
& EXTPROC
) && (pt
.c_lflag
& ISIG
)) {
for (cp
= buf
, lim
= buf
+ n
; cp
< lim
; cp
++) {
if (is_ctl(VSUSP
) || is_ctl(VDSUSP
) ||
is_ctl(VINTR
) || is_ctl(VQUIT
)) {
tcflag_t old_echo
= pt
.c_lflag
& ECHO
;
ioctl(pty
, TIOCPKT
, (char *)&onoff
);
ioctl(pty
, FIONBIO
, (char *)&onoff
);
ioctl(pty
, TIOCEXT
, (char *)&onoff
);
ioctl(pty
, TIOCSETA
, (char *)&pt
);
ioctl(pty
, TIOCSETA
, (char *)&pt
);
ioctl(pty
, TIOCEXT
, (char *)&onoff
);
ioctl(pty
, FIONBIO
, (char *)&onoff
);
ioctl(pty
, TIOCPKT
, (char *)&onoff
);
return write(pty
, buf
, n
);
* Process Q BIT (control) packets from the net.
* The only message that we are interested in are
* those indicating output is being discarded.
case X29_SET_AND_READ_PARMS
:
case X29_PARAMETER_INDICATION
:
case X29_INDICATION_OF_BREAK
:
for (p
= &fibuf
[2]; p
< fibuf
+n
; p
++) {
if (*p
== X29_TRANSMISSION_SPEED_CODE
) {
B110
, B0
, B300
, B1200
, B600
,
B0
, B0
, B0
, B0
, B0
, B0
, B0
,
B2400
, B4800
, B9600
, EXTA
};
if (*++p
>= 0 && *p
< sizeof(speeds
)) {
cfsetspeed(&pt
, speeds
[*p
]);
tcsetattr(pty
, TCSANOW
, &pt
);
} else if (*p
== X29_DISCARD_OUTPUT_CODE
&& *++p
!= 0) {
* Always re-enable normal output
message
[1] = X29_SET_PARMS
;
message
[2] = X29_DISCARD_OUTPUT_CODE
;
(void) write(net
, message
, sizeof(message
));
x29d_trace("netwrite", message
, 4);
* Bad news - we received an x29 error message or
* some other unknown packet. Dump the contents
* of the packet on the console.
for (p2
= "x29d: unknown q-bit packet: "; *p
++ = *p2
++; );
for (p2
= fibuf
+1; p2
< fibuf
+n
; p2
++)
if (*p2
>= ' ' && *p2
< 0177)
*p
++ = ((*p2
& 0300) >> 6) + '0';
*p
++ = ((*p2
& 070) >> 3) + '0';
(void) write(fd
, buf
, p
-buf
);
for (p1
= buf
; *s
; *p1
++ = *s
++);
for (p2
=bp
; p2
< bp
+n
; p2
++)
if (*p2
>= ' ' && *p2
< 0177)
*p1
++ = ((*p2
& 0300) >> 6) + '0';
*p1
++ = ((*p2
& 070) >> 3) + '0';
*p1
++ = (*p2
& 07) + '0';
fd
= creat(tracefn
, 0666);
(void) write(fd
, buf
, p1
-buf
);