/* Copyright (C) 1991, Marcus J. Ranum. All rights reserved. */ #ifndef lint static char RCSid[] = "$Header: /usr/users/mjr/hacks/umud/RCS/xmit.c,v 1.1 91/08/19 01:14:44 mjr Exp $"; #endif /* configure all options BEFORE including system stuff. */ #include "config.h" #include <stdio.h> #ifdef NOSYSTYPES_H #include <types.h> #else #include <sys/types.h> #endif #include <errno.h> extern int errno; #include <ctype.h> #include <varargs.h> #include <fcntl.h> #include <sys/time.h> #include <sys/file.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <signal.h> #include "mud.h" #include "xact.h" #include "sbuf.h" #ifndef FD_SET #define NBBY 8 #define NFDBITS (sizeof(long) * NBBY) #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) #define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) #endif /* #define XMITBSD_DEBUG */ /* don't bother being too big - this is a TCP */ #define XMBUFSIZ 1024 /* default timeout for I/O or connections */ #define DEFAULT_TIMEOUT 2 /* mud database entry */ typedef struct mudent { int flg; char *name; char *plyport; char *host; char *symhost; char *rempw; char *locpw; struct sockaddr_in addr; int timot; struct mudent *next; } Mudent; /* buffered connection */ typedef struct { int fd; char ibuf[XMBUFSIZ]; char *ibp; int ibc; char obuf[XMBUFSIZ]; char *obp; Mudent *curmp; } Cnxt; static Mudent *getmudent(); static Mudent *mud_list; static Cnxt xbuf; static Mudent *lastmud = (Mudent *)0; /* Look up the remote MUD and set the context up right */ set_remmud(nam) char *nam; { Mudent *mp; if((mp = getmudent(nam)) == (Mudent *)0) return(1); lastmud = xbuf.curmp = mp; return(0); } xc_open(mud) char *mud; { Mudent *mp; if((mp = getmudent(mud)) == (Mudent *)0) { logf("unknown mud ",mud,"\n",(char *)0); return(-1); } if((xbuf.fd = socket(AF_INET,SOCK_STREAM,0)) < 0) { perror("socket"); return(-1); } fcntl(xbuf.fd,F_SETFL,FNDELAY); /* this may EWOULDBLOCK, which is OK */ if(connect(xbuf.fd,&(mp->addr),sizeof(struct sockaddr_in)) < 0) { fd_set msk; sig_t sigsaved; struct timeval timo; #ifdef EINPROGRESS if(errno != EWOULDBLOCK && errno != EINPROGRESS) { #else if(errno != EWOULDBLOCK) { #endif (void)shutdown(xbuf.fd,2); (void)close(xbuf.fd); #ifdef XMITBSD_DEBUG perror("connect"); #endif logf("connection to ",mud," failed ",(char *)-1,"\n",(char *)0); return(-1); } FD_ZERO(&msk); FD_SET(xbuf.fd,&msk); timo.tv_usec = 0; timo.tv_sec = mp->timot; if(select(xbuf.fd + 1,0,&msk,0,&timo) != 1) { (void)shutdown(xbuf.fd,2); (void)close(xbuf.fd); #ifdef XMITBSD_DEBUG perror("connect failed (timeout?)"); #endif logf("connection to ",mud," timeout ",(char *)-1,"\n",(char *)0); return(-1); } /* Probe the fd to see if the connect() worked */ sigsaved = signal(SIGPIPE,SIG_IGN); if(write(xbuf.fd,"",0) < 0) { (void)signal(SIGPIPE,sigsaved); return(-1); } (void)signal(SIGPIPE,sigsaved); } #ifdef XMITBSD_DEBUG (void)printf("<<<connected>>>\n"); #endif logf("connection to ",mud,"\n",(char *)0); xbuf.ibp = xbuf.ibuf; xbuf.ibc = 0; xbuf.obp = xbuf.obuf; *xbuf.ibp = *xbuf.obp = '\0'; lastmud = xbuf.curmp = mp; return(0); } xc_initfd(fd) int fd; { xbuf.fd = fd; xbuf.ibp = xbuf.ibuf; xbuf.ibc = 0; xbuf.obp = xbuf.obuf; *xbuf.ibp = *xbuf.obp = '\0'; fcntl(xbuf.fd,F_SETFL,FNDELAY); return(0); } /* Send out an OIF Level 2 greet string to the current mud */ xmit_greet() { /* Firewall. This should NEVER happen. */ if(xbuf.curmp == (Mudent *)0) return(-1); if(xc_write("UnterMUD ",mud_getname()," ",version," " ,(xbuf.curmp)->locpw,"\n",(char *)0)) return(-1); if(xc_flush()) return(-1); return(0); } /* Return 0 if pwd is OK */ check_rempwd(pwd) char *pwd; { /* NEVER happen. */ if(xbuf.curmp == (Mudent *)0) return(1); return(strcmp(pwd,(xbuf.curmp)->rempw)); } /* These grub data out of the LAST Mud contacted. */ char * xc_getips() { extern char *inet_ntoa(); if(lastmud == (Mudent *)0) return(""); return( inet_ntoa(lastmud->addr.sin_addr) ); } char * xc_getmudname() { if(lastmud == (Mudent *)0){ return(""); } return(lastmud->name); } char * xc_gethostname() { if(lastmud == (Mudent *)0) return(""); return(lastmud->symhost); } char * xc_getport() { if(lastmud == (Mudent *)0) return(""); return(lastmud->plyport); } static Mudent * getmudent(mud) char *mud; { Mudent *mp = mud_list; while(mp != (Mudent *)0) { if(!strcmp(mud,mp->name)) return(mp); mp = mp->next; } return((Mudent *)0); } /* define a MUD entry. One semi-sensible thing done here is to pre-resolve the addresses and just stash those. this way we shouldn't have to hit our resolver a whole lot once the MUD is up, if at all. also accept "dot" notation. */ xmit_def_mudent(name,host,symhost,rempw,locpw,port,plyport,timot) char *name; char *host; char *symhost; char *rempw; char *locpw; char *port; char *plyport; char *timot; { Mudent *np; #ifndef NO_HUGE_RESOLVER_CODE struct hostent *hp; #endif struct sockaddr_in tmpa; char *p; p = host; while(*p != '\0' && (*p == '.' || isdigit(*p))) p++; if(*p != '\0') { #ifndef NO_HUGE_RESOLVER_CODE if((hp = gethostbyname(host)) == (struct hostent *)0) { logf("unknown host ",host,"\n",(char *)0); return(1); } (void)bcopy(hp->h_addr,(char *)&tmpa.sin_addr,hp->h_length); #else logf("must use 'dot' notation to define host ",host,"\n",(char *)0); return(1); #endif } else { unsigned long f; if((f = inet_addr(host)) == -1L) return(1); (void)bcopy((char *)&f,(char *)&tmpa.sin_addr,sizeof(f)); } if((np = (Mudent *)malloc(sizeof(Mudent))) == (Mudent *)0) { logf("cannot allocate remote mud map\n",(char *)0); return(1); } (void)bcopy((char *)&tmpa.sin_addr,&(np->addr.sin_addr),sizeof(tmpa.sin_addr)); np->addr.sin_port = htons(atoi(port)); np->addr.sin_family = AF_INET; if(timot != (char *)0) np->timot = atoi(timot); else np->timot = DEFAULT_TIMEOUT; np->name = (char *)malloc((unsigned)(strlen(name) + 1)); np->rempw = (char *)malloc((unsigned)(strlen(rempw) + 1)); np->locpw = (char *)malloc((unsigned)(strlen(locpw) + 1)); np->plyport = (char *)malloc((unsigned)(strlen(plyport) + 1)); np->host = (char *)malloc((unsigned)(strlen(host) + 1)); np->symhost = (char *)malloc((unsigned)(strlen(symhost) + 1)); if(np->name == (char *)0 || np->rempw == (char *)0 || np->locpw == (char *)0 || np->plyport == (char *)0 || np->host == (char *)0 || np->symhost == (char *)0) { logf("cannot allocate remote mud data. owie.\n",(char *)0); return(1); } (void)strcpy(np->name,name); (void)strcpy(np->rempw,rempw); (void)strcpy(np->locpw,locpw); (void)strcpy(np->plyport,plyport); (void)strcpy(np->host,host); (void)strcpy(np->symhost,symhost); np->flg = 0; /* link on */ np->next = mud_list; mud_list = np; return(0); } xc_status() { if(xbuf.fd == -1) return(1); return(0); } xc_close() { shutdown(xbuf.fd,2); close(xbuf.fd); xbuf.fd = -1; xbuf.curmp = (Mudent *)0; logf("hang up connection\n",(char *)0); #ifdef XMITBSD_DEBUG (void)printf("<<<dis-connected>>>\n"); #endif return(0); } char * xc_error() { return("no error"); } xc_sbread(sb) Sbuf *sb; { if(xbuf.fd == -1 || sb == (Sbuf *)0) return(0); sbuf_reset(sb); while(1) { /* buffer empty ? fill. */ if(xbuf.ibc <= 0) { fd_set msk; struct timeval timo; FD_ZERO(&msk); FD_SET(xbuf.fd,&msk); timo.tv_usec = 0; if(xbuf.curmp != (Mudent *)0) timo.tv_sec = xbuf.curmp->timot; else timo.tv_sec = DEFAULT_TIMEOUT; if(select(xbuf.fd + 1,&msk,0,0,&timo) != 1) { logf("timeout\n",(char *)0); return(0); } xbuf.ibc = read(xbuf.fd,xbuf.ibuf,sizeof(xbuf.ibuf)); if(xbuf.ibc <= 0) return(0); xbuf.ibp = xbuf.ibuf; } /* end of line? */ if(*xbuf.ibp == '\n') { sbuf_put('\0',sb); xbuf.ibp++; xbuf.ibc--; return(1); } /* put. */ sbuf_put(*xbuf.ibp,sb); xbuf.ibp++; xbuf.ibc--; } } xc_flush() { fd_set msk; struct timeval timo; int twrt; FD_ZERO(&msk); FD_SET(xbuf.fd,&msk); timo.tv_usec = 0; if(xbuf.curmp != (Mudent *)0) timo.tv_sec = xbuf.curmp->timot; else timo.tv_sec = DEFAULT_TIMEOUT; if(select(xbuf.fd + 1,0,&msk,0,&timo) != 1) { logf("write timeout\n",(char *)0); return(1); } twrt = xbuf.obp - xbuf.obuf; if(write(xbuf.fd,xbuf.obuf,twrt) != twrt) return(1); xbuf.obp = xbuf.obuf; return(0); } /* VARARGS */ xc_write(va_alist) va_dcl { char *s; va_list ap; va_start(ap); while((s = va_arg(ap,char *)) != (char *)0) { while(*s) { if((xbuf.obp - xbuf.obuf) >= sizeof(xbuf.obuf)) if(xc_flush()) return(1); *xbuf.obp++ = *s++; } } va_end(ap); return(0); } /* Maintain a list of MUD-names we've heard about. */ static Mudent *known_muds = 0; static int restrict_known = 0; /* Don't restrict to just known MUDs. */ static Mudent * isknownmud(mud) char *mud; { Mudent *temp, *pos; temp = getmudent(mud); if (temp != NULL) return temp; pos = known_muds; while (pos != NULL) { if (!strcmp(pos->name, mud)) return pos; pos = pos->next; } return (Mudent *) 0; } int xmit_def_knownmud(mud) char *mud; { Mudent *mp; restrict_known = 1; mp = isknownmud(mud); if (mp != 0) return 0; mp = (Mudent *) malloc(sizeof(Mudent)); if (mp == 0) { logf("couldn't alloc memory for knownmud mudent\n", (char *)0); return 1; } mp->name = (char *)malloc((unsigned)(strlen(mud) + 1)); if (mp->name == 0) { free(mp); logf("couldn't alloc memory for knownmud name\n", (char *)0); return 1; } (void)strcpy(mp->name, mud); mp->next = known_muds; known_muds = mp; return 0; } /* A given MUD name is okay if: - we don't have MUD security enabled (we have specified no defknownmuds) - it's a MUD we connect to - it's a MUD listed in a _mudconfig defknownmud or - it's us. */ int xmit_okay_remote(name) char *name; { if (!restrict_known) return 1; if (isknownmud(name) != 0) return 1; if (!strcmp(name, mud_getname())) return 1; return 0; }