#include "ckcsym.h" char *cknetv = "Stratus VOS Network Support, 5A(004) 19 Oct 94"; /* C K C N E T -- Network support */ /* Authors: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET), Columbia University Center for Computing Activities. netopen() routine for TCP/IP originally by Ken Yap, Rochester University (ken@cs.rochester.edu) (no longer at that address). Missing pieces for Excelan sockets library from William Bader, Moravian College . TELNET protocol by Frank da Cruz. TGV MultiNet code by Frank da Cruz. MultiNet code adapted to WIN/TCP by Ray Hunter of TWG. MultiNet code adapted to DEC TCP/IP by Lee Tibbert of DEC and Frank da Cruz. SunLink X.25 support by Marcello Frutig, Catholic University, Rio de Janeiro, Brazil (frutig@rnp.impa.br) with fixes from Stefaan Eeckels, Eurokom, Luxembourg. Most non-BSD sockets code removed, converted for Stratus VOS, renamed to cklnet.c by David R. Lane, SoftCom Systems, Inc. Other contributions as indicated below. Copyright (C) 1985, 1993, Trustees of Columbia University in the City of New York. The C-Kermit software may not be, in whole or in part, licensed or sold for profit as a software product itself, nor may it be included in or distributed with commercial products or otherwise distributed by commercial concerns to their clients or customers without written permission of the Office of Kermit Development and Distribution, Columbia University. This copyright notice must not be removed, altered, or obscured. */ #include "ckcdeb.h" #include "ckcker.h" #include "ckcnet.h" #ifdef NETCONN /* Don't need these if there is no network support. */ #include #include #include #include /* only need these includes if using OS TCP/IP */ #ifdef TCPSOCKET #include #include #endif /* TCPSOCKET */ /* only need these for X.25 support */ #ifdef STRATUSX25 _PROTOTYP( extern VOID s$read_event, (long *event_id, long *event_count, short *event_status, short *status) ); _PROTOTYP( extern VOID s$vc_call_full, (CV(32) *gateway, CV(15) *address, CV(63) *facilities, CV(128) *call_data, void *opts, long *timeout, short *vcid, long *event_id, short *status) ); _PROTOTYP( extern VOID s$vc_call_full_sts, (CV(66) *destination, short *extension, CV(63) *facilities, CV(128) *call_data, void *opts, long *timeout, short *vcid, long *event_id, short *status) ); _PROTOTYP( extern VOID s$vc_clear, (short *vcid, short *diag, short *status) ); _PROTOTYP( extern VOID s$vc_recv_packet, (short *vcid, short *buff_len, void *buff, short *packet_size, short *level, short *more, short *status) ); _PROTOTYP( extern VOID s$vc_recv_interrupt, (short *vcid, char *data, short *status) ); _PROTOTYP( extern VOID s$vc_reset, (short *vcid, short *diag, short *status) ); _PROTOTYP( extern VOID s$vc_send_interrupt, (short *vcid, char *data, short *status) ); _PROTOTYP( extern VOID s$vc_send_packet, (short *vcid, short *buff_len, void *buff, short *level, short *more, short *status) ); _PROTOTYP( extern VOID s$vc_set_no_wait_mode, (void) ); _PROTOTYP( extern VOID s$vc_set_wait_mode, (void) ); _PROTOTYP( extern VOID s$vc_status, (short *vcid, short *diag_code, short *status) ); extern int linkid, lcn, x25ver; extern int revcall, closgr, cudata; extern CHAR udata[MAXCUDATA]; extern CHAR padparms[MAXPADPARMS+1]; static long vcid_event; /* have to save this from open */ static short x25qbit; /* qbit out flag */ #endif /* STRATUSX25 */ static int netnowait; extern SIGTYP (*saval)(); /* For saving alarm handler */ _PROTOTYP( VOID bgchk, (void) ); _PROTOTYP( long congspd, (void) ); /* returns speed of console device */ extern int /* External variables */ duplex, debses, seslog, sessft, ttyfd, quiet, msgflg; #define NAMECPYL 100 /* Local copy of hostname */ static char namecopy[NAMECPYL]; char ipaddr[20] = { '\0' }; /* Global copy of IP address */ #endif /* NETCONN */ int ttnet = NET_NONE; /* Network type */ int ttnproto = NP_NONE; /* Network virtual terminal protocol */ int tn_init = 0; /* Telnet protocol initialized flag */ int tn_duplex = 1; /* Initial echo status */ char *tn_term = NULL; /* Terminal type override */ int tn_nlm = 1; /* Telnet CR -> CR LF mode */ #ifndef NETCONN /* Network support not defined. Dummy functions here in case #ifdef's forgotten elsewhere. */ int /* Open network connection */ netopen(name, lcl, nett) char *name; int *lcl, nett; { return(-1); } int /* Close network connection */ netclos() { return(-1); } int /* Check network input buffer */ nettchk() { return(-1); } int /* Flush network input buffer */ netflui() { return(-1); } int /* Send network BREAK */ netbreak() { return(-1); } int /* Input character from network */ netinc(timo) int timo; { return(-1); } int /* Output character to network */ #ifdef CK_ANSIC nettoc(char c) #else nettoc(c) char c; #endif /* CK_ANSIC */ /* nettoc */ { return(-1); } int nettol(s,n) char *s; int n; { return(-1); } #else /* NETCONN is defined (rest of this module...) */ /* For buffered network reads... */ #define TTXBUFL 1024 /* Maybe 8K?... */ extern CHAR ttxbuf[TTXBUFL+1]; extern int ttxbp, ttxbn = 0; /* Read bytes from network into internal buffer ttxbuf[], the same buffer that is used by ttbufr routine in ASYNC terminal io. To be called when input buffer is empty, i.e. when ttxbn == 0. Other network reading routines, like ttinc, ttinl, ttxin, should check the internal buffer first, and call this routine for a refill if necessary. Returns -1 on error, 0 if nothing happens. When data is read successfully, returns number of bytes read, and sets global ttxbn to that number and ttxbp (the buffer pointer) to zero. */ int netbufr(int timo) { /* TT Buffer Read */ int count; short status; if (ttnet != NET_TCPB && /* First make sure current net is */ ttnet != NET_VX25) { /* supported (VOS X.25 & OS TCP/IP) */ return(-1); /* if not, do nothing. */ } if (ttxbn > 0) /* Out internal buffer is not empty, */ return(ttxbn); /* so keep using it. */ count = nettchk(); /* Check network input buffer, */ if (ttxbn > 0) return(ttxbn); /* which can put a char there! */ if (count < 0) /* Read error */ return(-1); else if (count > TTXBUFL) /* Too many to read */ count = TTXBUFL; else if (count == 0) /* None, so read */ count = 1; debug(F101,"netbufr count 1","",count); #ifdef TCPSOCKET if (ttnet == NET_TCPB) { /* This is for nonblocking reads */ if ((count = net_read(ttyfd,ttxbuf,count,0)) < 1) { if (count == -1 && errno == e$caller_must_wait) { debug(F100,"netbufr finds nothing","",0); return(0); } else if (count == 0) { debug(F100,"netbufr socket eof","",0); return(-2); /* this is really a disconnect */ } else { debug(F101,"netbufr net_read error","",errno); return(-2); /* say it's disconnected */ } } } #endif /* TCPSOCKET */ #ifdef STRATUSX25 if (ttnet == NET_VX25) { count = x25xin(sizeof(ttxbuf), ttxbuf); if (count <= 0) return count; } #endif /* STRATUSX25 */ ttxbp = 0; /* Reset buffer pointer. */ ttxbn = count; #ifdef DEBUG debug(F101,"netbufr count 2","",count); /* Got some bytes. */ if (count > 0) ttxbuf[count] = '\0'; debug(F111,"netbufr ttxbuf",ttxbuf,ttxbp); #endif /* DEBUG */ return(ttxbn); /* Return buffer count. */ } /* C-Kermit network open/close functions for OS TCP/IP & X.25/X.29 Networking. */ /* N E T O P E N -- Open a network connection. */ /* Returns 0 on success, -1 on failure. */ #ifdef TCPSOCKET #define TELNET_PORT 23 /* Should do lookup, but it won't change */ /* This symbol is not known to, e.g., Ultrix 2.0 */ #ifndef TELOPT_TTYPE #define TELOPT_TTYPE 24 #endif /* TELOPT_TTYPE */ #endif /* TCPSOCKET */ /* N E T O P E N -- Open a network connection */ /* Calling conventions same as ttopen(), except third argument is network type rather than modem type. Designed to be called from within ttopen. */ int netopen(name, lcl, nett) char *name; int *lcl, nett; { char *p; #ifdef SO_OOBINLINE int on = 1; #endif /* SO_OOBINLINE */ int i, x; #ifdef TCPSOCKET struct servent *service, servrec; struct hostent *host; struct sockaddr_in saddr; #endif /* TCPSOCKET */ #ifdef STRATUSX25 CV(66) destination; CV(15) address; CV(63) facilities; CV(128) call_data; long x25_timeout; short vcid; short status; struct { short version; short max_delay; short send_size; short recv_size; } x25_options; short sts_extension; #endif /* STRATUSX25 */ debug(F101,"netopen nett","",nett); *ipaddr = '\0'; /* Initialize IP address string */ #ifdef STRATUSX25 if (nett == NET_VX25) { /* If network type is X.25 */ netclos(); /* Close any previous net connection */ ttnproto = NP_NONE; /* No protocol selected yet */ sts_extension = -1; strncpy (namecopy, name, NAMECPYL); p = strchr (namecopy, ':'); if (NULL == p) { /* no address given */ strcpy (&destination, namecopy); strcpy (&address, ""); } else { /* address in part of name */ *p++ = '\0'; strcpy (&destination, namecopy); strcpy (&address, p); } if (namecopy[0] == '%') { /* STS with %system#module */ /* remove % from destination if no module name given */ if (NULL == strchr (namecopy, '#')) /* no module name */ strcpy (&destination, &namecopy[1]); sts_extension = (*p) ? atoi (p) : 255; strcpy (&call_data, ""); /* STS includes protocol ID */ } else memcpy (&call_data, "\x00\x04\x01\x00\x00\x00", 6); /* KLUDGE! */ /* Set call user data if specified */ if (cudata) strcat(&call_data,udata); strcpy (&facilities, ""); /* Set reverse charge call and closed user group if requested */ if (revcall) { /* reverse charge requested */ strcat (&facilities, "\x01\x01"); } /* closed user group not supported */ if (closgr > -1) { debug(F101,"x25 closed user group not supported","",closgr); } x25_options.version = 1; x25_options.max_delay = 0; x25_options.send_size = 1024; x25_options.recv_size = 1024; x25_timeout = 30 * 1024; /* lost timeout coming to netopen() */ /* Open X.25 virtual circuit */ if (sts_extension > -1) { s$vc_call_full_sts (&destination, &sts_extension, &facilities, &call_data, &x25_options, &x25_timeout, &vcid, &vcid_event, &status); debug(F101,"netopen s$vc_call_full_sts status","",status); } else { s$vc_call_full ((CV(32) *)&destination, &address, &facilities, &call_data, &x25_options, &x25_timeout, &vcid, &vcid_event, &status); debug(F101,"netopen s$vc_call_full status","",status); } if (status) { return (-1); } lcn = vcid; ttyfd = vcid; linkid = sts_extension; ttnet = nett; /* Stratus X.25 network */ ttnproto = NP_X3; /* PAD X.3, X.28, X.29 protocol */ if (*lcl < 0) *lcl = 1; /* Local mode */ return(0); } #endif /* STRATUSX25 */ #ifdef TCPSOCKET if (nett == NET_TCPB) { netclos(); /* Close any previous connection. */ strncpy(namecopy, name, NAMECPYL); /* Copy the hostname. */ ttnproto = NP_NONE; /* No protocol selected yet. */ debug(F110,"netopen namecopy",namecopy,0); p = namecopy; /* Was a service requested? */ while (*p != '\0' && *p != ':') p++; /* Look for colon */ if (*p == ':') { /* Have a colon */ *p++ = '\0'; /* Get service name or number */ } else { /* Otherwise use telnet */ p = "telnet"; } debug(F110,"netopen service requested",p,0); if (isdigit(*p)) { /* Use socket number without lookup */ service = &servrec; service->s_port = htons((unsigned short)atoi(p)); } else { /* Otherwise lookup the service name */ service = getservbyname(p, "tcp"); } if (!service) { fprintf(stderr, "Cannot find port for service %s\n", p); debug(F101,"netopen can't get service","",errno); errno = 0; /* rather than mislead */ return(-1); } /* Set up socket structure and get host address */ bzero((char *)&saddr, sizeof(saddr)); if ((host = gethostbyname(namecopy)) != NULL) { saddr.sin_family = host->h_addrtype; bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length); } else { #ifdef INADDRX /* inet_addr() is of type struct in_addr */ struct in_addr ina; unsigned long uu; ina = inet_addr(namecopy); uu = *(unsigned long *)&ina; #else /* Not INADDRX */ /* inet_addr() is unsigned long */ unsigned long uu; uu = inet_addr(namecopy); #endif /* INADDRX */ if ((saddr.sin_addr.s_addr = uu) != ((unsigned long)-1)) saddr.sin_family = AF_INET; else { fprintf(stderr, "Can't get address for %s\n", namecopy); debug(F101,"netopen can't get address","",errno); errno = 0; /* rather than mislead */ return(-1); } } /* Get a file descriptor for the connection. */ saddr.sin_port = service->s_port; sprintf(ipaddr,"%s", inet_ntoa(saddr.sin_addr)); if (!quiet && *ipaddr) printf(" Trying %s...\n", ipaddr); if ((ttyfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("TCP socket error"); debug(F101,"netopen socket error","",errno); return (-1); } errno = 0; /* Now connect to the socket on the other end. */ if (connect(ttyfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { i = errno; /* save error code */ close(ttyfd); ttyfd = -1; errno = i; /* and report this error */ debug(F101,"netopen connect errno","",errno); return(-1); } #ifdef SO_OOBINLINE /* The symbol SO_OOBINLINE is not known to Ultrix 2.0. It means "leave out of band data inline". The normal value is 0x0100, but don't try this on systems where the symbol is undefined. */ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #endif /* SO_OOBINLINE */ /* See if the service is TELNET. */ if ((x = ntohs((unsigned short)service->s_port)) == TELNET_PORT) ttnproto = NP_TELNET; /* Yes, set global flag. */ debug(F101,"netopen service","",x); ttnet = nett; /* TCP/IP (sockets) network */ tn_init = 0; /* Telnet protocol not init'd yet */ if (*lcl < 0) *lcl = 1; /* Local mode. */ return(0); /* Done. */ } #endif /* TCPSOCKET */ /* Add support for other networks here. */ return(-1); /* Unsupported Network type */ } /* N E T C L O S -- Close current network connection. */ int netclos() { int x = 0; short vcid; short status; short diag; if (ttyfd < 0) /* Was open? */ return(0); /* Wasn't. */ #ifdef STRATUSX25 if (ttnet == NET_VX25) { if (ttyfd > -1) { vcid = ttyfd; diag = 0; s$vc_clear (&vcid, &diag, &status); debug(F101,"s$vc_clear status","",status); ttyfd = -1; x = (status == 0) ? 0 : -1; } } #endif /* STRATUSX25 */ #ifdef TCPSOCKET if (ttnet == NET_TCPB) { if (ttyfd > -1) /* Was. */ x = net_close(ttyfd); } #endif /* TCPSOCKET */ ttyfd = -1; /* Mark it as closed. */ tn_init = 0; /* Remember about telnet protocol... */ *ipaddr = '\0'; /* Zero the IP address string */ return(x); } /* N E T T C H K -- Check if network up, and how many bytes can be read */ /* Returns number of bytes waiting, or -1 if connection has been dropped. */ int /* Check how many bytes are ready */ nettchk() { /* for reading from network */ unsigned int count = 0; int x, y; char c; x = y = 0; #ifdef COMMENT debug(F101,"nettchk entry ttxbn","",ttxbn); debug(F101,"nettchk entry ttxbp","",ttxbp); #endif errno = 0; /* for X.25, just return count of buffered characters */ #ifdef TCPSOCKET if (ttnet == NET_TCPB) { if (net_ioctl(ttyfd,FIONREAD,(char *)&count) < 0) { debug(F101,"nettchk net_ioctl error","",errno); if (ttxbn < 1) return(-1); else return(ttxbn); } debug(F101,"nettchk count","",count); } #endif /* TCPSOCKET */ /* count what's in network and what we have buffered */ debug(F101,"nettchk returns","",count+ttxbn); return(count + ttxbn); } /* N E T I N C -- Input character from network */ int netinc(timo) int timo; { debug(F101,"WARNING: netinc called, timo","",timo); return(-1); } /* N E T T O L -- Output a string of bytes to the network */ /* Call with s = pointer to string, n = length. Returns number of bytes actually written on success, or -1 on i/o error, -2 if called improperly. */ int nettol(s,n) char *s; int n; { int count = 0; short buflen = n; #ifdef TCPSOCKET if (ttnet == NET_TCPB) { if ((count = net_write(ttyfd,s,n,0)) < 1) { debug(F101,"nettol net_write error","",errno); return(-1); } debug(F111,"nettol net_write",s,count); return(count); } #endif /* TCPSOCKET */ #ifdef STRATUSX25 if (ttnet == NET_VX25) { short mbit = 0; short vcid = ttyfd; short status; if (0 == x25qbit && padparms[PAD_DATA_FORWARD_TIMEOUT]) mbit = 2; /* stream data, may combine packets */ if (buflen > 1024) /* only try to send 1K */ buflen = 1024; s$vc_send_packet (&vcid, &buflen, s, &x25qbit, &mbit, &status); s[n] = '\0'; debug(F111,"nettol s$vc_send_packet status",s,status); if (status) return (-1); count = buflen; return (count); } #endif /* STRATUSX25 */ return(-2); } /* N E T T O C -- Output character to network */ /* Call with character to be transmitted. Returns 0 if transmission was successful, or -1 upon i/o error, or -2 if called improperly. */ int #ifdef CK_ANSIC nettoc(char c) #else nettoc(c) char c; #endif /* CK_ANSIC */ /* nettoc */ { unsigned char cc; cc = c; #ifdef TCPSOCKET if (ttnet == NET_TCPB) { debug(F101,"nettoc cc","",cc); if (net_write(ttyfd,&cc,1,0) < 1) { debug(F101,"nettoc net_write error","",errno); return(-1); } debug(F101,"nettoc net_write","", cc); return(0); } #endif /* TCPSOCKET */ #ifdef STRATUSX25 if (ttnet == NET_VX25) { short mbit = 2; /* may be joined with other packets before send */ short vcid = ttyfd; short buflen = 1; short status; s$vc_send_packet (&vcid, &buflen, &cc, &x25qbit, &mbit, &status); if (status) { debug(F101,"nettoc s$vc_send_packet status",0,status); } return (status ? -2 : 0); } #endif /* STRATUSX25 */ return(-2); } /* N E T F L U I -- Flush network input buffer */ int netflui() { int n; ttxbuf[ttxbp+1] = '\0'; debug(F111,"netflui 1",ttxbuf,ttxbn); ttxbn = ttxbp = 0; /* Flush internal buffer *FIRST* */ if ((n = nettchk()) > 0) { /* Now see what's waiting on the net */ if (n > TTXBUFL) n = TTXBUFL; /* and sponge it up */ debug(F101,"netflui 2","",n); /* ... */ #ifdef TCPSOCKET if (ttnet == NET_TCPB) n = net_read(ttyfd,ttxbuf,n,0) ; /* into our buffer */ else n = 0; #endif /* TCPSOCKET */ #ifdef STRATUSX25 if (ttnet == NET_VX25) n = 0; /* x25 don't read to flush */ #endif /* STRATUSX25 */ if (n >= 0) ttxbuf[n] = '\0'; debug(F111,"netflui 3",ttxbuf,n); ttxbuf[0] = '\0'; } return(0); } /* N E T W A I T -- Set a network connection back and forth between wait mode and nowait mode, used by contti during network connections. Assumes the caller logs the statuses. */ int #ifdef CK_ANSIC netwait(int no_wait, int sd, long *evid, long *evcnt) #else netwait(no_wait, sd, evid, evcnt) int no_wait; int sd; long *evid; long *evcnt; #endif /* netwait(int no_wait, int sd, long *evid, long *evcnt) */ { short status; int y; int x; short event_stat; status = 0; debug(F101,"netwait no_wait","",no_wait); debug(F101," sd","",sd); netnowait = no_wait; #ifdef STRATUSX25 /* X.25 we don't actually change the mode. We stay in wait mode for everything except receiving packets. */ if (ttnet == NET_VX25) { if (no_wait) { /* pass back the saved event information */ *evid = vcid_event; s$read_event (evid, evcnt, &event_stat, &status); } } #endif /* STRATUSX25 */ #ifdef TCPSOCKET if (ttnet == NET_TCPB) { if (no_wait) { if (0 > get_socket_event (sd, evid, evcnt)) { status = errno; return status; } y = 0; /* Turn on nonblocking reads */ x = net_ioctl(ttyfd,FIONBIO,(char*)&y); debug(F101,"netwait FIONBIO","",x); if (x < 0) status = errno; } else { y = 1; /* Turn on blocking reads */ x = net_ioctl(ttyfd,FIONBIO,(char*)&y); debug(F101,"netwait FIONBIO","",x); if (x < 0) status = errno; } } #endif /* TCPSOCKET */ return (status); } #ifdef TCPSOCKET #ifdef TNCODE /* Compile in telnet support code */ /* TCP/IP TELNET protocol negotiation support code */ static int sgaflg = 0; /* SUPRRESS GO-AHEAD state */ static int dosga = 0; /* 1 if I sent DO SGA from tn_ini() */ static int wttflg = 0; /* ditto for WILL TERMINAL TYPE */ #ifndef TELCMDS char *telcmds[] = { "SE", "NOP", "DMARK", "BRK", "IP", "AO", "AYT", "EC", "EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC", }; int ntelcmds = sizeof(telcmds) / sizeof(char *); #endif /* TELCMDS */ #ifndef TELOPTS char *telopts[] = { "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME", "STATUS", "TIMING MARK", "RCTE", "NAOL", "NAOP", "NAOCRD", "NAOHTS", "NAOHTD", "NAOFFD", "NAOVTS", "NAOVTD", "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO", "DATA ENTRY TERMINAL", "SUPDUP", "SUPDUP OUTPUT", "SEND LOCATION", "TERMINAL TYPE", "END OF RECORD" #ifdef TELOPT_TUID ,"TACACS UID" #ifdef TELOPT_OUTMRK ,"OUTPUT MARKING" #ifdef TELOPT_TTYLOC ,"TTYLOC" #ifdef TELOPT_3270REGIME ,"3270 REGIME" #ifdef TELOPT_X3PAD ,"X.3 PAD" #ifdef TELOPT_NAWS ,"NAWS" #ifdef TELOPT_TSPEED ,"TSPEED" #ifdef TELOPT_LFLOW ,"LFLOW" #ifdef TELOPT_LINEMODE ,"LINEMODE" #endif #endif #endif #endif #endif #endif #endif #endif #endif }; #endif /* TELOPTS */ int ntelopts = sizeof(telopts) / sizeof(char *); #endif /* TNCODE */ #endif /* TCPSOCKET */ /* Send network BREAK */ /* Returns -1 on error, 0 if nothing happens, 1 if BREAK sent successfully. */ int netbreak() { CHAR buf[3]; #ifdef TCPSOCKET if (ttnet == NET_TCPB) { if (ttnproto == NP_TELNET) { #ifdef TNCODE buf[0] = (CHAR) IAC; buf[1] = (CHAR) BREAK; if (ttol(buf,2) < 2) return(-1); debug(F101,"telnet BREAK ok","",BREAK); return(1); #else debug(F100,"netbreak no TNCODE","",0); return(0); #endif /* TNCODE */ } /* Insert other TCP/IP protocols here */ } #endif /* TCPSOCKET */ #ifdef STRATUSX25 if (ttnet == NET_VX25) { breakact (); } #endif /* STRATUSX25 */ /* Insert other networks here */ return(0); } /* Send a telnet option, avoid loops. */ /* Returns 1 if command was sent, 0 if not, -1 on error */ #ifdef TCPSOCKET int tn_sopt(cmd,opt) int cmd, opt; { /* TELNET SEND OPTION */ CHAR buf[4]; int n; if (ttnet != NET_TCPB) return(0); /* Must be TCP/IP */ if (ttnproto != NP_TELNET) return(0); /* Must be telnet protocol */ n = cmd - SE; if (n < 0 || n > ntelcmds) return(0); #ifdef TNCODE buf[0] = (CHAR) IAC; buf[1] = (CHAR) cmd & 0xff; buf[2] = (CHAR) opt & 0xff; if (ttol(buf,3) < 3) return(-1); debug(F111,"telnet cmd >",telcmds[n],cmd); debug(F111,"telnet opt >", (opt < ntelopts) ? telopts[opt] : "UNKNOWN", opt ); if (debses && cmd != SB) printf("[%s %s]",telcmds[n], (opt < ntelopts) ? telopts[opt] : "UNKNOWN"); return(1); #else debug(F100,"tn_sopt no TNCODE","",0); return(0); #endif /* TNCODE */ } /* Initialize a telnet connection. */ /* Returns -1 on error, 0 if nothing happens, 1 if init msgs sent ok */ int tn_ini() { #ifndef TNCODE debug(F100,"tn_ini no TNCODE","",0); return(0); #else /* TELNET protocol support */ debug(F101,"tn_ini ttnproto","",ttnproto); debug(F101,"tn_ini tn_init","",tn_init); if (ttnet != NET_TCPB) /* Make sure connection is TCP/IP. */ return(0); if (tn_init) /* Have we done this already? */ return(0); /* Don't do it again. */ debug(F101,"tn_ini tn_duplex","",tn_duplex); duplex = tn_duplex; /* Assume local echo. */ sgaflg = 0; /* Assume Go-Ahead suppressed. */ wttflg = 0; /* Did not send WILL TERM TYPE yet. */ dosga = 0; /* Did not send DO SGA yet. */ if (ttnproto == NP_NONE) { /* If not talking to a telnet port, */ ttnproto = NP_TELNET; /* pretend it's telnet anyway, */ tn_init = 1; /* but don't send initial options. */ debug(F100,"tn_ini skipping telnet negotiations","",0); return(0); } /* Talking to telnet port, so send WILL TERMINAL TYPE and DO SGA */ if (tn_sopt(WILL,TELOPT_TTYPE) < 0) /* Will send terminal type. */ return(-1); wttflg = 1; /* Remember I said I would. */ if (tn_sopt(DO,TELOPT_SGA) < 0) /* Please suppress go-ahead. */ return(-1); dosga = 1; /* Remember I send DO SGA */ tn_init = 1; /* Set telnet-initialized flag. */ /* Don't send anthing else! */ debug(F101,"tn_ini duplex","",duplex); return(1); #endif /* TNCODE */ } /* Process in-band Telnet negotiation characters from the remote host. Call with the telnet IAC character and the current duplex setting (0 = remote echo, 1 = local echo). Returns: 3 if server has sent us a quoted IAC 2 if local echo must be changed to remote 1 if remote echo must be changed to local 0 if nothing happens or no action necessary -1 on failure (= internal or i/o error) */ #define TSBUFSIZ 41 char sb[TSBUFSIZ]; /* Buffer for subnegotiations */ int #ifdef CK_ANSIC /* TELNET DO OPTION */ tn_doop( CHAR z, int echo, int (*fn)(int) ) #else tn_doop(z, echo, fn) CHAR z; int echo; int (*fn)(); #endif /* CK_ANSIC */ /* tn_doop */ { int c, x, y, n, m, flag; #ifndef TNCODE debug(F100,"tn_doop no TNCODE","",0); return(0); #else if (z != (CHAR) IAC) { debug(F101,"tn_doop bad call","",z); return(-1); } if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); /* Check protocol */ /* Have IAC, read command character. */ c = (*fn)(0) & 0xff; /* Read command character */ m = c - SE; /* Check validity */ if (m < 0 || m > ntelcmds) { debug(F101,"tn_doop bad cmd","",c); return(0); } if (seslog && sessft) { /* Copy to session log, if any. */ if (zchout(ZSFILE, (char) z) < 0) seslog = 0; /* Log IAC. */ else if (zchout(ZSFILE, (char) c) < 0) seslog = 0; /* Log command */ } debug(F111,"telnet cmd <",telcmds[m],c); /* Debug log. */ if (c == (CHAR) IAC) return(3); /* Quoted IAC */ if (c < SB) return(0); /* Other command with no arguments. */ /* SB, WILL, WONT, DO, or DONT need more bytes... */ if ((x = (*fn)(0)) < 0) return(-1); /* Get the option. */ x &= 0xff; /* Trim to 8 bits. */ debug(F111,"telnet opt <", (x < ntelopts) ? telopts[x] : "UNKNOWN", x ); if (seslog && sessft) /* Session log */ if (zchout(ZSFILE, (char) x) < 0) seslog = 0; /* Now handle the command */ if (debses && c != SB) /* Debug to screen. */ printf("<%s %s>",telcmds[m], (x < ntelopts) ? telopts[x] : "UNKNOWN" ); switch (x) { case TELOPT_ECHO: /* ECHO negotiation. */ switch (c) { /* Command */ case WILL: /* Host says it will echo. */ if (echo) /* Only reply if change required. */ return((tn_sopt(DO,x) < 0) ? -1 : 2); /* Please do. */ else return(0); /* Otherwise no change. */ case WONT: /* Host says it won't echo. */ if (!echo) /* If I'm full duplex */ return ((tn_sopt(DONT,x) < 0) ? -1 : 1); /* Switch to half */ else return(0); /* Otherwise, no change. */ case DO: /* Host wants me to echo */ if (tn_sopt(WONT,x) < 0) /* but the client never echoes */ return(-1); /* back to the server. */ default: /* Don't reply to anything else */ return(0); } case TELOPT_SGA: /* Suppress Go-Ahead */ switch (c) { /* Command... */ case WONT: /* Host says it won't. */ if (!sgaflg) { sgaflg = 1; /* Remember. */ if (tn_sopt(DONT,x) < 0) /* acknowledge, */ return(-1); } return(echo ? 0 : 1); /* Switch to half duplex */ case WILL: /* Server says it will SGA */ if (sgaflg || !dosga) { /* ACK only if necessary */ if (tn_sopt(DO,x) < 0) return(-1); sgaflg = 0; /* Remember new SGA state. */ } return(0); /* But don't change echo state. */ } #ifdef TELOPT_TTYPE case TELOPT_TTYPE: /* Terminal Type */ switch (c) { case DO: /* DO terminal type. */ if (wttflg == 0) { /* If I haven't said so before, */ if (tn_sopt((CHAR)WILL,x) < 0) /* say I'll send it if asked. */ return(-1); wttflg++; } return(0); case SB: debug(F100,"TELNET subnegotiation:","",0); n = flag = 0; /* Flag for when done reading SB */ while (n < TSBUFSIZ) { /* Loop looking for IAC SE */ if ((y = (*fn)(0)) < 0) /* Read a byte */ return(-1); y &= 0xff; /* Make sure it's just 8 bits. */ sb[n++] = y; /* Deposit in buffer. */ if (seslog && sessft) /* Take care of session log */ if (zchout(ZSFILE, (char) y) < 0) seslog = 0; if (y == IAC) { /* If this is an IAC */ if (flag) { /* If previous char was IAC */ n--; /* it's quoted, keep one IAC */ flag = 0; /* and turn off the flag. */ } else flag = 1; /* Otherwise set the flag. */ } else if (flag) { /* Something else following IAC */ if (y != SE) /* If not SE, it's a protocol error */ flag = 0; break; } } if (!flag) { /* Make sure we got a valid SB */ debug(F100, "TELNET Subnegotian prematurely broken", "",0); return(-1); } if (debses) { /* Debug to screen. */ int i; printf(""); } debug(F101,"TELNET suboption<","",sb[0]); if (sb[0] == 1) { /* SEND terminal type? */ if (tn_sttyp() < 0) /* Yes, so send it. */ return(-1); } default: /* Others, ignore */ return(0); } #endif /* TELOPT_TTYPE */ default: /* All others: refuse */ switch(c) { case WILL: /* You will? */ if (tn_sopt(DONT,x) < 0) /* Please don't. */ return(-1); /* (Could this cause a loop?) */ break; case DO: /* You want me to? */ if (tn_sopt(WONT,x) < 0) /* I won't. */ return(-1); break; case DONT: /* You don't want me to? */ if (tn_sopt(WONT,x) < 0) /* I won't. */ return(-1); /* (Could this cause a loop?) */ case WONT: /* You won't? */ break; /* I didn't want you to. */ } /* Anything else, treat as user data */ return(0); } #endif /* TNCODE */ } /* Telnet send terminal type */ /* Returns -1 on error, 0 if nothing happens, 1 if type sent successfully */ int tn_sttyp() { /* Send telnet terminal type. */ #ifndef TNCODE debug(F100,"tn_sttyp no TNCODE","",0); return(0); #else char *ttn; int ttl, i; /* Name & length of terminal type. */ if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); ttn = NULL; if (tn_term) { /* Terminal type override? */ debug(F110,"tn_sttyp",tn_term,0); if (*tn_term) ttn = tn_term; } else debug(F100,"tn_sttyp no term override","",0); if (!ttn) /* If no override, */ ttn = getenv("TERM"); /* get it from the "environment". */ if ((ttn == ((char *)0)) || ((ttl = (int)strlen(ttn)) >= TSBUFSIZ)) { ttn = "UNKNOWN"; ttl = 7; } sb[0] = IAC; /* I Am a Command */ sb[1] = SB; /* Subnegotiation */ sb[2] = TELOPT_TTYPE; /* Terminal Type */ sb[3] = (CHAR) 0; /* Is... */ for (i = 4; *ttn; ttn++,i++) /* Copy and uppercase it */ sb[i] = (islower(*ttn)) ? toupper(*ttn) : *ttn; ttn = sb; /* Point back to beginning */ sb[i++] = IAC; /* End of Subnegotiation */ sb[i++] = SE; /* marked by IAC SE */ if (ttol((CHAR *)sb,i) < 0) /* Send it. */ return(-1); #ifdef DEBUG sb[i-2] = '\0'; /* For debugging */ debug(F111,"telnet SB sent ttype",sb+4,ttl); #endif /* DEBUG */ if (debses) /* Debug to screen. */ printf("[SB TERMINAL TYPE 00 %s IAC SE]",sb+4); return(1); #endif /* TNCODE */ } #endif /* TCPSOCKET */ #ifdef STRATUSX25 /* PAD X.3, X.28 and X.29 support */ static CHAR x29err [MAXPADPARMS+3] = { X29_ERROR, INVALID_PAD_PARM, '\0' }; /* Initialize PAD */ extern CHAR padparms[MAXPADPARMS+1]; static long ccittspeeds[] = { 110, 134, 300, 1200, 600, 75, 150, 1800, 200, 100, 50, 1200, 2400, 4800, 9600, 19200, 48000, 56000, 64000 }; VOID initpad() { int i; long speed; int match; /* may have to take closest match */ long diff; padparms[PAD_BREAK_CHARACTER] = 3; /* Break character */ padparms[PAD_ESCAPE] = 1; /* Escape permitted */ padparms[PAD_ECHO] = 1; /* Kermit PAD does echo */ padparms[PAD_DATA_FORWARD_CHAR] = 2; /* forward character CR */ padparms[PAD_DATA_FORWARD_TIMEOUT] = 0; /* no timeout forward condition */ padparms[PAD_FLOW_CONTROL_BY_PAD] = 0; /* not used */ padparms[PAD_SUPPRESSION_OF_SIGNALS] = 1; /* allow PAD service signals */ padparms[PAD_BREAK_ACTION] = 21; /* brk action: INT pk + brk ind*/ padparms[PAD_SUPPRESSION_OF_DATA] = 0; /* no supression of user data */ padparms[PAD_PADDING_AFTER_CR] = 0; /* no padding after CR */ padparms[PAD_LINE_FOLDING] = 0; /* no line fold */ padparms[PAD_LINE_SPEED] = 0; /* line speed - don't care */ padparms[PAD_FLOW_CONTROL_BY_USER] = 0; /* flow cont of PAD - not used */ padparms[PAD_LF_AFTER_CR] = 0; /* no LF insertion after CR */ padparms[PAD_PADDING_AFTER_LF] = 0; /* no padding after LF */ padparms[PAD_EDITING] = 1; /* can edit */ padparms[PAD_CHAR_DELETE_CHAR] = 8; /* character delete character */ padparms[PAD_BUFFER_DELETE_CHAR] = 21; /* buffer delete character */ padparms[PAD_BUFFER_DISPLAY_CHAR] = 18; /* buffer display character */ speed = congspd(); match = 0; for (i = 0; i < (sizeof ccittspeeds) / (sizeof ccittspeeds[0]); i++) { diff = speed - ccittspeeds[i]; if (diff == 0) { match = i; break; } if (diff > 0 && diff < (speed - ccittspeeds[match])) match = i; } padparms[PAD_LINE_SPEED] = match; /* line speed (as good as we can get) */ } /* Set PAD parameters */ VOID setpad(s,n) CHAR *s; int n; { int i; CHAR *ps = s; char buff[30]; if (n) { for (i = 0; i < n; i++) { if(deblog) { sprintf(buff," set pad parm %d=%d",*ps,*(ps+1)); debug(F100,buff,"",0); } /* out of range or read only */ if (*ps > MAXPADPARMS || *ps == PAD_LINE_SPEED) x29err[i+2] = *ps; else padparms[*ps] = *(ps+1); ps += 2; } } else { debug(F100," setpad calling initpad","",0); initpad(); } } /* Read PAD parameters */ VOID readpad(s,n,r) CHAR *s; int n; CHAR *r; { int i; CHAR *ps = s; CHAR *pr = r; char buff[30]; *pr++ = X29_PARAMETER_INDICATION; if (n == 0) { /* All supported parameters */ for (i = 1; i <= MAXPADPARMS; i++, ps++) { *pr++ = i; *pr++ = padparms[i]; if(deblog) { sprintf(buff," read pad parm %d=%d",i,padparms[i]); debug(F100,buff,"",0); } } } else { /* list of specific parameters */ for (i = 0; i < n; i++, ps++) { if(deblog) { sprintf(buff," read pad parm %d=%d", *ps, (*ps <= MAXPADPARMS) ? padparms[i] : -1); debug(F100,buff,"",0); } if (*ps > MAXPADPARMS) { x29err[i+2] = *ps++; } else { *pr++ = *ps; *pr++ = padparms[*ps++]; } } } } int qbitpkt(s,n) CHAR *s; int n; { CHAR *ps = s; int x29cmd = *ps; CHAR *psa = s+1; CHAR x29resp[(MAXPADPARMS*2)+1]; debug (F101,"qbitpkt, len","",n); switch (x29cmd) { case X29_SET_PARMS: debug(F100,"X29 SET PAD","",0); setpad (psa,n/2); if ((int)strlen(x29err) > 2) { ttol (x29err,(int)strlen(x29err)); x29err[2] = '\0'; } break; case X29_READ_PARMS: debug(F100,"X29 READ PAD","",0); readpad (psa,n/2,x29resp); setqbit (); ttol (x29resp, (n > 1) ? n : (MAXPADPARMS * 2 + 1)); if ((int)strlen(x29err) > 2) { ttol (x29err,(int)strlen(x29err)); x29err[2] = '\0'; } resetqbit(); break; case X29_SET_AND_READ_PARMS: debug(F100,"X29 SET AND READ PAD","",0); setpad (psa,n/2); readpad (psa,n/2,x29resp); setqbit(); ttol (x29resp, (n > 1) ? n : (MAXPADPARMS * 2 + 1)); if ((int)strlen(x29err) > 2) { ttol (x29err,(int)strlen(x29err)); x29err [2] = '\0'; } resetqbit(); break; case X29_INVITATION_TO_CLEAR: debug(F100,"X29 INVITATION TO CLEAR","",0); (VOID) x25clear(); return (-2) ; /* hang it up */ case X29_INDICATION_OF_BREAK: debug(F100,"X29 INDICATION OF BREAK","",0); break; } return (0); } /* PAD break action processor */ VOID breakact() { extern char x25obuf[]; extern int obufl; extern int active; extern unsigned char tosend; static CHAR indbrk[3] = { X29_INDICATION_OF_BREAK, PAD_SUPPRESSION_OF_DATA, 1 }; CHAR intudat, cause, diag; if (x25stat() < 0) return; /* Ignore if no virtual call established */ if (padparms[PAD_BREAK_ACTION] != 0) /* Forward condition */ if (ttol(x25obuf,obufl) < 0) { perror ("\r\nCan't send characters"); active = 0; } else { /* bzero (x25obuf,sizeof(x25obuf)); */ /**/ obufl = 0; tosend = 0; }; switch (padparms[PAD_BREAK_ACTION]) { case 0 : break; /* do nothing */ case 1 : /* send interrupt packet with interrupt user data field = 1 */ intudat = 1; x25intr (intudat); break; case 2 : /* send reset packet with cause and diag = 0 */ cause = diag = 0; x25reset (cause,diag); break; case 5 : /* send interrupt packet with interrupt user data field = 0 */ intudat = 0; x25intr (intudat) ; setqbit (); /* send indication of break without a parameter field */ ttoc(X29_INDICATION_OF_BREAK); resetqbit (); break; case 8 : active = 0; /* leave data transfer */ conol ("\r\n"); break; case 21: /* send interrupt packet with interrupt user data field = 0 */ intudat = 0; x25intr (intudat); setpad (indbrk+1,1); /* set pad to discard input */ setqbit (); /* send indication of break with parameter field */ ttol (indbrk,sizeof(indbrk)); resetqbit (); break; } } /* X.25 support functions */ /* Convert a null-terminated string representing an X.121 address to a packed BCD form. */ #ifdef SUNX25 /* not used for Stratus X.25 */ int pkx121(str,bcd) char *str; CHAR *bcd; { int i, j; u_char c; i = j = 0; while (str[i]) { if ( i >= 15 || str [i] < '0' || str [i] > '9' ) return (-1); c = str [i] - '0'; if ( i & 1 ) bcd [j++] |= c; else bcd [j] = c << 4; i++; } return (i); } #endif /* SUNX25 */ /* Reads and prints X.25 diagnostic */ int x25diag () { int i; #ifdef SUNX25 bzero ((char *)&diag,sizeof(diag)); if (ioctl(ttyfd,X25_RD_CAUSE_DIAG,&diag)) { perror ("Reading X.25 diagnostic"); return(-1); } if (diag.datalen > 0) { printf ("X.25 Diagnostic :"); for (i = 0; i < diag.datalen; i++) printf (" %02x",diag.data[i]); printf ("\r\n"); } #endif /* SUNX25 */ return(0); } /* X.25 Out-of-Band Signal Handler */ #ifdef SUNX25 VOID x25oobh() { int oobtype; u_char oobdata; (VOID) signal(SIGURG,x25oobh); do { if (ioctl(ttyfd,X25_OOB_TYPE,&oobtype)) { perror ("Getting signal type"); return; } switch (oobtype) { case INT_DATA: if (recv(ttyfd,oobdata,1,MSG_OOB) < 0) { perror ("Receiving X.25 interrupt data"); return; } printf ("\r\nInterrupt received, data = %d\r\n", oobdata); break; case VC_RESET: printf ("\r\nVirtual circuit reset\r\n"); x25diag (); break; case N_RESETS: printf ("\r\nReset timeout\r\n"); break; case N_CLEARS: printf ("\r\nClear timeout\r\n"); break; case MSG_TOO_LONG: printf ("\r\nMessage discarded, too long\r\n"); break; default: if (oobtype) printf("\r\nUnknown oob type %d\r\n",oobtype); break; } } while (oobtype); } #endif /* SUNX25 */ /* Send a X.25 interrupt packet */ int #ifdef CK_ANSIC x25intr(char intr) #else x25intr(intr) char intr; #endif /* CK_ANSIC */ /* x25intr */ { short vcid = ttyfd; short status; short diag; debug(F101,"X.25 intr, char","",(long) intr); s$vc_send_interrupt (&vcid, &intr, &status); debug(F101,"s$vc_send_interrupt status",0,status); s$vc_status (&vcid, &diag, &status); debug(F101,"s$vc_status status",0,status); return(0); } /* Reset X.25 virtual circuit */ int #ifdef CK_ANSIC x25reset(char cause, char diagn) #else x25reset(cause, diagn) char cause; char diagn; #endif /* CK_ANSIC */ /* x25reset */ { short vcid = ttyfd; short status; short vc_diag; char buff[50]; sprintf (buff, "cause=%d[IGNORED], diag=%d", cause, diagn); vc_diag = (CHAR) diagn; s$vc_reset (&vcid, &vc_diag, &status); debug(F111,"X.25 reset, status",buff,status); return(0); } /* Clear X.25 virtual circuit */ int x25clear() { short vcid = ttyfd; short diag = 0; short status; s$vc_clear (&vcid, &diag, &status); debug(F101,"X.25 clear status","",status); return(ttclos(0)); /* Close socket */ } /* X.25 status */ int x25stat() { if (ttyfd < 0) return (-1); return(0); } /* Set Q_BIT on */ VOID setqbit() { x25qbit = 1; } /* Set Q_BIT off */ VOID resetqbit() { x25qbit = 0; } /* Read n characters from X.25 circuit into buf */ int x25xin(n,buf) int n; CHAR *buf; { register int x, c; int qpkt; short vcid = ttyfd; short buffsize = n; short packsize; short mbit; short qbit; char intchar; short diag; short status; do { if (netnowait) /* do this in no_wait mode */ s$vc_set_no_wait_mode (); s$vc_recv_packet (&vcid, &buffsize, buf, &packsize, &qbit, &mbit, &status); debug(F101,"s$vc_recv_packet status","",status); if (netnowait) /* change it back for everything else */ s$vc_set_wait_mode (); x = (status)? (-1) : packsize; switch (status) { case 0: break; case e$vc_interrupt_pending: s$vc_recv_interrupt (&vcid, &intchar, &status); debug(F101,"s$vc_recv_interrupt status","",status); break; case e$vc_reset_pending: s$vc_reset (&vcid, &diag, &status); debug(F101,"s$vc_reset status","",status); break; case e$caller_must_wait: x = 0; break; default: return (-2); /* I dunno, but it's broke */ } if (x > 0 && qbit) { /* If Q_BIT packet, process it */ if ((c=qbitpkt(buf,x)) < 0) return(c); qpkt = 1; } else qpkt = 0; } while (qpkt); if (x >= 0) buf[x] = '\0'; else x = -1; debug(F101,"x25xin x","",x); return(x); } #endif /* STRATUSX25 */ #endif /* NETCONN */