/* * X.29 server * * Frank Pronk (...!ubc-vision!pronk) * April, September 1984 * * Laboratory for Computational Vision * University of British Columbia * Copyright (c) */ #include #include #include #include #include #include #include #include #include #include #include #include "../h/x29.h" #define BUFSIZ 1024 #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]; int pty, net; extern char **environ; extern int errno; char line[MAXPATHLEN]; char console[] = "/dev/console"; short packet_size; short debug; char *tracefn; /* trace file name */ char *server; short send_banner; struct termios pt, old_pt; struct sockaddr_x25 sock; int reapchild(); struct net *lookup (); 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_EDITING, 1, /* on */ X29_CHARACTER_DELETE, CERASE, X29_LINE_DELETE, CKILL, 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_EDITING, 1, /* on */ X29_CHARACTER_DELETE, CERASE, X29_LINE_DELETE, CKILL, X29_LINE_DISPLAY, CRPRNT, /* * This rubbish can be removed when Datapac * adopts the 1980 standard parameter set. */ 0, 0, /* national parameter marker */ 123, 0, /* parity off */ }; struct net { char *n_name; /* generic name */ short n_type; /* see defines above */ char *n_profile; /* initial profile */ short n_proflen; /* length of n_profile */ } *netp, nets[] = { "x.25", X25NET, 0, 0, "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), 0, 0, 0, 0 }; main(argc, argv) register char **argv; { register int s, pid; register char *p; /* * 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"); close(s); 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; sock.x25_udlen = 4; for (argv++; argc > 1; argc--, argv++) if (**argv == '-') for (p = *argv+1; *p; p++) switch (*p) { case 'b': send_banner++; break; case 'c': if (argc > 1) { argc--; argv++; if ((netp = lookup (*argv)) == 0) fatal(1, "Unknown network type"); } break; case 'p': if (argc > 1) { argc--; argv++; strcpy (sock.x25_udata, *argv); } break; case 'r': sock.x25_opts.op_flags |= X25_REVERSE_CHARGE; break; case 'd': debug++; break; case 't': if (argc > 1) { argc--; argv++; tracefn = *argv; } else fatal(1, "missing trace file"); break; default: fatal (1, "usage: x29d -b -c nettype -p protocol -r -t trace_file server"); } else server = *argv; if (server == 0) fatal (1, "no server specified"); if (debug == 0) daemon(0, 0); while ((s = socket(AF_CCITT, SOCK_STREAM, 0)) < 0) sleep(60); while (bind(s, (caddr_t)&sock, sizeof (sock)) < 0) sleep(60); signal(SIGCHLD, reapchild); listen(s, 5); for (;;) { struct sockaddr_x25 from; int fromlen = sizeof (from); if ((net = accept(s, (caddr_t)&from, &fromlen)) < 0) { if (errno != EINTR) sleep (60); continue; } while ((pid = fork()) < 0) sleep(60); if (pid == 0) { signal(SIGCHLD, SIG_DFL); doit(&from); } close(net); } /*NOTREACHED*/ } struct net * lookup (name) char *name; { register struct net *np; for (np = nets; np->n_name; np++) if (strcmp (np->n_name, name) == 0) return (np); return (0); } reapchild() { union wait status; while (wait3(&status, WNOHANG, 0) > 0) ; } char *envinit[] = { "TERM=ccitt", 0 }; int cleanup(); struct termios term; /* * Get a pty, scan input lines. */ doit(who) struct sockaddr_x25 *who; { register char *cp; int i, p, t; packet_size = 1 << who->x25_opts.op_psize; i = forkpty(&pty, line, &term, 0); if (i > 0) x29d(); if (i < 0) fatalperror("fork", errno); environ = envinit; call_server (who); /*NOTREACHED*/ } call_server (who) struct sockaddr_x25 *who; { register struct hostent *hp = 0; register char *p, **ap; char *args[MAXARGS]; struct stat st; struct hostent *getx25hostbyaddr(); int ccitt = 0; p = server; while (*p && *p != ' ' && *p != '\t') /* split program from args */ p++; if (*p) *p++ = '\0'; ap = args; while (*p) { while (*p == ' ' || *p == '\t') p++; if (ap < &args[MAXARGS-2]) *ap++ = p; if (strcmp(p, "-ccitt") == 0) ccitt = 1; while (*p && *p != ' ' && *p != '\t') p++; if (*p) *p++ = '\0'; } 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- * user permissions. */ if (getuid() == 0) { setgid (st.st_gid); setuid (st.st_uid); } if (hp = getx25hostbyaddr (who->x25_addr)) *ap++ = hp->h_name; else *ap++ = (char *)who->x25_addr; /* * If the -ccitt flag was given, add another argument * to tell login if charging is being reversed or not. */ if (ccitt) *ap++ = (who->x25_opts.op_flags & X25_REVERSE_CHARGE) ? "y" : "n"; *ap = 0; execv (server, args); fatalperror (server, errno); /*NOTREACHED*/ } fatal(f, msg) int f; char *msg; { register char *p; char buf[BUFSIZ], *index(); p = buf; if (f == net) *p++ = 0; strcpy(p, "x29d: "); strcat(p, msg); strcat(p, "\n"); (void) write(f, p, (index(p, '\n')-p)+1); exit(1); } fatalperror(msg, err) char *msg; { char buf[BUFSIZ]; extern char *sys_errlist[]; strcpy(buf, msg); strcat(buf, ": "); strcat(buf, sys_errlist[err]); fatal(net, buf); } /* * Main loop. Select from pty and network, and * hand data to iti receiver. */ x29d() { register int pcc, fcc, cc; register char *fbp; int pgrp, x25_interrupt(), on = 1; char hostname[32]; 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 or do it in daemon */ 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(SIGHUP, cleanup); signal(SIGTTOU, SIG_IGN); signal(SIGURG, x25_interrupt); /* for out-of-band data */ if (netp->n_proflen) (void) write(net, netp->n_profile, netp->n_proflen); /* * Show banner that getty never gave. */ if (send_banner) { gethostname(hostname, sizeof (hostname)); #ifdef BSD4_3 strcpy(pibuf+1, "\r\n\r\n4.3 BSD UNIX ("); #else strcpy(pibuf+1, "\r\n\r\n4.2 BSD UNIX ("); #endif strcat(pibuf+1, hostname); strcat(pibuf+1, ")\r\n\r\n"); pcc = strlen(pibuf+1) + 1; } else pcc = 0; fcc = 0; for (;;) { int ibits, obits; ibits = obits = 0; /* * 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? */ obits |= (1 << pty); else if (pcc >= 0) /* pty still alive? */ ibits |= (1 << net); if (pcc >= 0) /* pty connection alive? */ if (pcc && fcc >= 0) /* output pending? */ obits |= (1 << net); else if (fcc >= 0) /* net still alive? */ ibits |= (1 << pty); if (ibits == 0 && obits == 0) break; (void) select(16, &ibits, &obits, (int *)0, 0); if (ibits == 0 && obits == 0) { sleep(5); continue; } /* * Something to read from the network... */ if (fcc == 0 && (ibits & (1 << net))) { fcc = read(net, fibuf, BUFSIZ); fbp = fibuf+1; if (fcc < 0 && errno == EWOULDBLOCK) fcc = 0; else if (fcc <= 0) fcc = -1; else { if (tracefn) x29d_trace("netread", fibuf, fcc); if (fibuf[0] & Q_BIT) { x29_qbit(fcc); fcc = 0; } else fcc--; } } /* * Something to read from the pty... */ if (ibits & (1 << pty)) { pcc = read(pty, pibuf, packet_size+1); if (pcc < 0 && errno == EWOULDBLOCK) pcc = 0; else if (pcc <= 0) pcc = -1; else if (pibuf[0] != 0) { /* non-data packet */ if (pibuf[0] & TIOCPKT_IOCTL) { if (--pcc > sizeof(pt)) pcc = sizeof(pt); old_pt = pt; bcopy(pibuf + 1, (char *)&pt, pcc); pcc = set_x29_parameters(); } else pcc = 0; } else /* data packet */ pibuf[0] = 0; } if ((obits & (1< 0) if ((cc = write(net, pibuf, pcc)) == pcc) { if (tracefn) x29d_trace("netwrite", pibuf, pcc); pcc = 0; } else { extern char *sys_errlist[]; if (tracefn) x29d_trace("netwrite", sys_errlist[errno], strlen(sys_errlist[errno])); } if ((obits & (1 << pty)) && fcc > 0) { cc = ptywrite(fbp, fcc); if (cc > 0) { fcc -= cc; fbp += cc; } } } cleanup(); } /* * Send interrupt to process on other side of pty. * If it is in raw mode, just write NULL; * otherwise, write intr char. */ x25_interrupt() { struct termios tt; int zero = 0; signal(SIGURG, x25_interrupt); tcgetattr(pty, &tt); if (tt.c_lflag & ISIG) { tcsetattr(pty, TCSAFLUSH, &tt); (void) write(pty, &tt.c_cc[VINTR], 1); } else (void) write(pty, "\0", 1); } cleanup() { char *p; p = line + sizeof(_PATH_DEV) - 1; if (logout(p)) logwtmp(p, "", ""); (void)chmod(line, 0666); (void)chown(line, 0, 0); *p = 'p'; (void)chmod(line, 0666); (void)chown(line, 0, 0); shutdown(net, 2); exit(1); } /* * Map unix tty modes and special characters * into x29 parameters. */ set_x29_parameters() { register char *p; int f; char *lim = p + sizeof (pt); if (netp->n_type == X25NET) return (0); if ((old_pt.c_lflag & ICANON) != (pt.c_lflag & ICANON)) { f = pt.c_lflag & ICANON; ioctl(pty, TIOCEXT, &f); /* 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) return; p = pibuf; *p++ = Q_BIT; *p++ = X29_SET_PARMS; /* *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++ = X29_LF_AFTER_CR; /* *p++ = (f & (RAW|CBREAK) || (f & ECHO) == 0) ? 0 : 4; */ *p++ = ((pt.c_lflag & (ICANON | ECHO)) != (ICANON | ECHO)) ? 0 : 4; *p++ = X29_EDITING; *p++ = (pt.c_lflag & ICANON) != 0; #define ctlchar(x) \ (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); } #undef ctlchar return (p - pibuf); } /* 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. */ ptywrite(buf, n) char *buf; int n; { register char *cp, *lim; char *last; #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(VLNEXT)) { cp++; continue; } if (is_ctl(VSUSP) || is_ctl(VDSUSP) || is_ctl(VINTR) || is_ctl(VQUIT)) { int onoff = 0; tcflag_t old_echo = pt.c_lflag & ECHO; ioctl(pty, TIOCPKT, (char *)&onoff); ioctl(pty, FIONBIO, (char *)&onoff); ioctl(pty, TIOCEXT, (char *)&onoff); if (old_echo) { pt.c_lflag &= ~ECHO; ioctl(pty, TIOCSETA, (char *)&pt); } n = write(pty, buf, n); onoff = 1; if (old_echo) { pt.c_lflag |= ECHO; ioctl(pty, TIOCSETA, (char *)&pt); } ioctl(pty, TIOCEXT, (char *)&onoff); ioctl(pty, FIONBIO, (char *)&onoff); ioctl(pty, TIOCPKT, (char *)&onoff); return (n); } } } 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. */ x29_qbit(n) { register char *p; switch (fibuf[1]) { case X29_SET_PARMS: 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) { static char speeds[] = { 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) { char message[4]; /* * Always re-enable normal output */ message[0] = Q_BIT; message[1] = X29_SET_PARMS; message[2] = X29_DISCARD_OUTPUT_CODE; message[3] = 0; (void) write(net, message, sizeof(message)); if (tracefn) x29d_trace("netwrite", message, 4); } } return; default: { register char *p2; char buf[BUFSIZ*4]; static int fd; /* * Bad news - we received an x29 error message or * some other unknown packet. Dump the contents * of the packet on the console. */ p = buf; for (p2 = "x29d: unknown q-bit packet: "; *p++ = *p2++; ); for (p2 = fibuf+1; p2 < fibuf+n; p2++) if (*p2 >= ' ' && *p2 < 0177) *p++ = *p2; else { *p++ = '\\'; *p++ = ((*p2 & 0300) >> 6) + '0'; *p++ = ((*p2 & 070) >> 3) + '0'; *p++ = (*p2 & 07) + '0'; } *p++ = '\n'; if (fd <= 0) fd = open(console, 1); (void) write(fd, buf, p-buf); } } } x29d_trace(s, bp, n) char *s, *bp; { static int fd; char buf[BUFSIZ*4]; register char *p1, *p2; for (p1 = buf; *s; *p1++ = *s++); *p1++ = ':'; *p1++ = ' '; for (p2=bp; p2 < bp+n; p2++) if (*p2 >= ' ' && *p2 < 0177) *p1++ = *p2; else { *p1++ = '\\'; *p1++ = ((*p2 & 0300) >> 6) + '0'; *p1++ = ((*p2 & 070) >> 3) + '0'; *p1++ = (*p2 & 07) + '0'; } *p1++ = '\n'; if (fd <= 0) fd = creat(tracefn, 0666); (void) write(fd, buf, p1-buf); }