📄 nsplitd.c
字号:
/* * nsplitd.c * --------- * * $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ * * Copyright (c) 1995, University of Cambridge Computer Laboratory, * Copyright (c) 1995, Richard Black, All Rights Reserved. * * * A complete re-implementation of DME's nsplitd for use from inetd * *//* The basic stream comes in (via inetd) and we then conenct to * somewhere else providing a loop-through service, except we offer * two other ports for connection - one of which gets a second channel * using the top bit to distinguish, and the other is a master control * port (normally used for gdb) which gets complete exclusive access * for its duration. * * Originally designed for multiplexing a xwcons/telnet with a gdb * post-mortem debugging session. * * Here is a picture: * * port0 (from inetd) * 8-bit connection / * made by us <----> nsplitd <-----gdbport (default port0+2) * to host:port/tcp |\ * | port1 (default port0+1) * \ * control (default port0+3) * * If port1 is explicitly disabled (through a command-line option) then * port0 becomes 8-bit clean. *//* * N.B.: We do NOT support 8 bit stdin/stdout usage on a * /dev/... because to do that right involves much messing with ioctl * and TIOC... etc. If you want to do that sort of thing then the * right way to do it is to chain this onto wconsd (which does know * about and understand all the ioctl and TIOC grief). */#include <sys/types.h>#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <assert.h>#include <errno.h>#include <unistd.h>#include <ctype.h>#include <netdb.h>#include <string.h>#include <sys/time.h>#include <sys/signal.h>#include <sys/socket.h>#include <netinet/in.h>#include <netinet/tcp.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <syslog.h>#ifndef FALSE#define FALSE 0#endif#ifndef TRUE#define TRUE 1#endif#ifndef LOG_DAEMON#define LOG_DAEMON 0#endif#define DB(x) /* ((x), fflush(stderr)) */extern char *optarg;extern int optind, opterr, optopt;static char *prog_name;static void usage(void){ fprintf(stderr, "This program (%s) should be run via inetd (tcp)\n\n", prog_name); fprintf(stderr, "usage: %s [-h<highport>][-g<gdbport>]" "[-c<ctlport>][-8] host:service\n", prog_name); exit(1);}static void fault(char *format, ...){ va_list ap; char logbuf[1024]; va_start(ap, format); fprintf(stderr, "%s: ", prog_name); vfprintf(stderr, format, ap); fflush(stderr); va_end(ap); /* XXX This is a bit dubious, but there is no vsyslog */ va_start(ap, format); vsprintf(logbuf, format, ap); syslog(LOG_ERR, logbuf); va_end(ap); exit(1);}static int getservice(char *name, unsigned short *port){ struct servent *se; if (!name) return -1; if (isdigit(name[0])) *port = atoi(name); else { if (!(se = getservbyname(name, "tcp"))) return -1; *port = ntohs(se->s_port); } return 0;}/* * connect_host: connect to ("name", "port") */static int connect_host (char *name, unsigned int port){ int fd; struct hostent *hostent; struct sockaddr_in sin; int on; if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) fault("socket"); if (!(hostent = gethostbyname(name))) fault("gethostbyname: %s: %s\n", name, strerror(errno)); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons (port); memcpy(&sin.sin_addr.s_addr, hostent->h_addr, sizeof(struct in_addr)); if (connect(fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) fault("connect: %s:%u: %s\n", name, port, strerror(errno)); on = 1; if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0) syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); on = 1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); return fd;}/* * open a tcp socket and start listening for connections on it */static int startlistening(unsigned short port){ int fd, on; struct sockaddr_in sin; if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) fault("socket"); on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) syslog(LOG_WARNING, "setsockopt (SO_REUSEADDR): %m"); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons (port); sin.sin_addr.s_addr = INADDR_ANY; if (bind(fd, &sin, sizeof(sin)) < 0) fault("bind: %u: %s\n", port, strerror(errno)); if (listen(fd, 1) < 0) fault("listen: %s\n", strerror(errno)); return fd;}static void noblock(int fd){ int on=1; if (ioctl(fd, FIONBIO, &on) < 0) fault("ioctl: FIONBIO: %s\n", strerror(errno));}/* You might not believe this, but fd_sets don't have to be a 32-bit * integer. In particular, in glibc2 it is an array of unsigned * longs. Hence, this hacked up FD_SET_rjb() that works out if it * would have been a nop. */#define FD_SET_rjb(fd, setp) \do { \ if ((fd) != 32) \ FD_SET((fd), (setp)); \} while(0)#define FD_ISSET_rjb(fd, setp) (((fd) != 32)? FD_ISSET((fd), (setp)) : 0)#define MAXSIZE 256/* ----------------------------------------------------------------- * The main bit of the algorithm. Note we use 32 to mean not connected * because this gives us 1<<32 == 0. We could have done this one * character at a time, but that would have been very inefficient and * not the unix way. */static int debug;static void doit(int actl, int acto, int lish, int lisg, int lisc){ int acth, actg, actc; int gdbmode = FALSE; char gibuf[MAXSIZE], oibuf[MAXSIZE]; char libuf[MAXSIZE], lobuf[MAXSIZE]; char hibuf[MAXSIZE], hobuf[MAXSIZE]; char ctlbuf[MAXSIZE]; fd_set rdfs, wrfs, exfs; int gicc, oicc, licc, locc, hicc, hocc, ctlcc; char *giptr, *oiptr, *liptr, *loptr, *hiptr, *hoptr; int rc, fromlen; struct sockaddr_in from; gicc = oicc = licc = locc = hicc = hocc = ctlcc = 0; acth = actg = actc = 32; /* XXX yummy */ noblock(actl); noblock(acto); for(;;) { FD_ZERO(&rdfs); FD_ZERO(&wrfs); FD_ZERO(&exfs); /* always take input from the control port (if it's connected) */ FD_SET_rjb(actc, &rdfs); if (gdbmode) { if (oicc) FD_SET_rjb(actg, &wrfs); else FD_SET_rjb(acto, &rdfs); if (gicc) FD_SET_rjb(acto, &wrfs); else FD_SET_rjb(actg, &rdfs); } else { /* There is no such thing as oibuf because its been split into * lobuf and hobuf */ if (locc || hocc) { if (locc) FD_SET_rjb(actl, &wrfs); if (hocc) FD_SET_rjb(acth, &wrfs); } else FD_SET_rjb(acto, &rdfs); if (licc) FD_SET_rjb(acto, &wrfs); else FD_SET_rjb(actl, &rdfs); if (hicc) FD_SET_rjb(acto, &wrfs); else FD_SET_rjb(acth, &rdfs); } if (acth == 32 && lish>=0) FD_SET_rjb(lish, &rdfs); if (actg == 32) FD_SET_rjb(lisg, &rdfs); if (actc == 32) FD_SET_rjb(lisc, &rdfs); /* now make exfs the union of the read and write fd sets, plus * "actl" */ { int i; exfs = rdfs; for(i=0; i<32; i++) /* XXX we only copy fd numbers up to 31 */ if (FD_ISSET(i, &wrfs)) FD_SET_rjb(i, &exfs); FD_SET_rjb(actl, &exfs); } /* XXX AND: can't print something of type fd_set as %x - it * might be an array */ DB(fprintf(stderr, "%s: before select: %08x %08x %08x\n", prog_name, rdfs, wrfs, exfs)); if (select(32, &rdfs, &wrfs, &exfs, NULL) < 0) fault("select: %s\n", strerror(errno)); DB(fprintf(stderr, "%s: after select: %08x %08x %08x\n", prog_name, rdfs, wrfs, exfs)); /* XXX it appears that a non-blocking socket may not show up * correctly in exfs but instead goes readable with no data in * it. Thus we check for zero and goto the appropriate close * method. */ /* Deal with exceptions */ if (FD_ISSET_rjb(actg, &exfs)) { exfs_actg: close(actg); gdbmode = FALSE; oicc = 0; oiptr = oibuf; actg = 32; continue; /* because assumptions changed */ } if (FD_ISSET_rjb(acth, &exfs)) { exfs_acth: close(acth); hicc = hocc = 0; hiptr = hibuf; hoptr = hibuf; acth = 32; continue; /* because assumptions changed */ } if (FD_ISSET_rjb(actl, &exfs) ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -