source: trunk/packages/xen-3.1/xen-3.1/tools/misc/nsplitd/nsplitd.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 16.3 KB
Line 
1/*
2 *      nsplitd.c
3 *      ---------
4 *
5 * $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $
6 *
7 * Copyright (c) 1995, University of Cambridge Computer Laboratory,
8 * Copyright (c) 1995, Richard Black, All Rights Reserved.
9 *
10 *
11 * A complete re-implementation of DME's nsplitd for use from inetd
12 *
13 */
14
15/* The basic stream comes in (via inetd) and we then conenct to
16 * somewhere else providing a loop-through service, except we offer
17 * two other ports for connection - one of which gets a second channel
18 * using the top bit to distinguish, and the other is a master control
19 * port (normally used for gdb) which gets complete exclusive access
20 * for its duration.
21 *
22 * Originally designed for multiplexing a xwcons/telnet with a gdb
23 * post-mortem debugging session.
24 *
25 * Here is a picture:
26 *
27 *                                          port0 (from inetd)
28 *      8-bit connection                   /
29 *         made by us      <----> nsplitd <-----gdbport (default port0+2)
30 *      to host:port/tcp                  |\
31 *                                        | port1 (default port0+1)
32 *                                         \
33 *                                          control (default port0+3)
34 *
35 * If port1 is explicitly disabled (through a command-line option) then
36 * port0 becomes 8-bit clean.
37 */
38
39/*
40 * N.B.: We do NOT support 8 bit stdin/stdout usage on a
41 * /dev/... because to do that right involves much messing with ioctl
42 * and TIOC... etc.  If you want to do that sort of thing then the
43 * right way to do it is to chain this onto wconsd (which does know
44 * about and understand all the ioctl and TIOC grief).
45 */
46
47#include <sys/types.h>
48#include <stdarg.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <assert.h>
52#include <errno.h>
53#include <unistd.h>
54#include <ctype.h>
55#include <netdb.h>
56#include <string.h>
57
58#include <sys/time.h>
59#include <sys/signal.h>
60#include <sys/socket.h>
61#include <netinet/in.h>
62#include <netinet/tcp.h>
63#include <arpa/inet.h>
64#include <sys/ioctl.h>
65#include <syslog.h>
66
67#ifndef FALSE
68#define FALSE 0
69#endif
70#ifndef TRUE
71#define TRUE 1
72#endif
73
74#ifndef LOG_DAEMON
75#define LOG_DAEMON 0
76#endif
77
78#define DB(x)  /* ((x), fflush(stderr)) */
79
80extern char *optarg;
81
82extern int optind, opterr, optopt;
83
84static char *prog_name;
85
86static void usage(void)
87{
88    fprintf(stderr, "This program (%s) should be run via inetd (tcp)\n\n",
89            prog_name);
90    fprintf(stderr, "usage: %s [-h<highport>][-g<gdbport>]"
91            "[-c<ctlport>][-8] host:service\n",
92            prog_name);
93    exit(1);
94}
95
96static void fault(char *format, ...)
97{
98    va_list             ap;
99    char                logbuf[1024];
100
101    va_start(ap, format);
102    fprintf(stderr, "%s: ", prog_name);
103    vfprintf(stderr, format, ap);
104    fflush(stderr);
105    va_end(ap);
106   
107    /* XXX This is a bit dubious, but there is no vsyslog */
108    va_start(ap, format);
109    vsprintf(logbuf, format, ap);
110    syslog(LOG_ERR, logbuf);
111    va_end(ap);
112    exit(1);
113}
114
115static int getservice(char *name, unsigned short *port)
116{
117    struct servent              *se;
118
119    if (!name) return -1;
120
121    if (isdigit(name[0]))
122        *port = atoi(name);
123    else
124    {
125        if (!(se = getservbyname(name, "tcp")))
126            return -1;
127        *port = ntohs(se->s_port);
128    }
129    return 0;
130}
131
132/*
133 *  connect_host: connect to ("name", "port")
134 */
135static int connect_host (char *name, unsigned int port)
136{
137    int                 fd;
138    struct hostent      *hostent;
139    struct sockaddr_in  sin;
140    int                 on;
141   
142    if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
143        fault("socket");
144   
145    if (!(hostent = gethostbyname(name)))
146        fault("gethostbyname: %s: %s\n", name, strerror(errno));
147   
148    memset(&sin, 0, sizeof(sin));
149    sin.sin_family = AF_INET;
150    sin.sin_port   = htons (port);
151    memcpy(&sin.sin_addr.s_addr, hostent->h_addr, sizeof(struct in_addr));
152   
153    if (connect(fd, (struct sockaddr *) &sin, sizeof (sin)) < 0)
154        fault("connect: %s:%u: %s\n", name, port, strerror(errno));
155   
156    on = 1;
157    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
158        syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
159
160    on = 1;
161    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
162        syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
163
164    return fd;
165}
166
167/*
168 * open a tcp socket and start listening for connections on it
169 */
170static int startlistening(unsigned short port)
171{
172    int                 fd, on;
173    struct sockaddr_in  sin;
174
175    if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
176        fault("socket");
177   
178    on = 1;
179    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
180      syslog(LOG_WARNING, "setsockopt (SO_REUSEADDR): %m");
181
182    memset(&sin, 0, sizeof(sin));
183    sin.sin_family      = AF_INET;
184    sin.sin_port        = htons (port);
185    sin.sin_addr.s_addr = INADDR_ANY;
186    if (bind(fd, &sin, sizeof(sin)) < 0)
187        fault("bind: %u: %s\n", port, strerror(errno));
188   
189    if (listen(fd, 1) < 0)
190        fault("listen: %s\n", strerror(errno));
191   
192    return fd;
193}
194
195static void noblock(int fd)
196{
197    int on=1;
198   
199    if (ioctl(fd, FIONBIO, &on) < 0)
200        fault("ioctl: FIONBIO: %s\n", strerror(errno));
201}
202
203
204/* You might not believe this, but fd_sets don't have to be a 32-bit
205 * integer.  In particular, in glibc2 it is an array of unsigned
206 * longs.  Hence, this hacked up FD_SET_rjb() that works out if it
207 * would have been a nop. */
208#define FD_SET_rjb(fd, setp) \
209do {                                            \
210    if ((fd) != 32)                             \
211        FD_SET((fd), (setp));                   \
212} while(0)
213
214#define FD_ISSET_rjb(fd, setp) (((fd) != 32)? FD_ISSET((fd), (setp)) : 0)
215
216#define MAXSIZE 256
217
218/* -----------------------------------------------------------------
219 * The main bit of the algorithm. Note we use 32 to mean not connected
220 * because this gives us 1<<32 == 0. We could have done this one
221 * character at a time, but that would have been very inefficient and
222 * not the unix way.  */
223static int debug;
224
225static void doit(int actl, int acto, int lish, int lisg, int lisc)
226{
227    int         acth, actg, actc;
228    int         gdbmode = FALSE;
229    char        gibuf[MAXSIZE], oibuf[MAXSIZE];
230    char        libuf[MAXSIZE], lobuf[MAXSIZE];
231    char        hibuf[MAXSIZE], hobuf[MAXSIZE];
232    char        ctlbuf[MAXSIZE];
233    fd_set      rdfs, wrfs, exfs;
234    int         gicc, oicc, licc, locc, hicc, hocc, ctlcc;
235    char        *giptr, *oiptr, *liptr, *loptr, *hiptr, *hoptr;
236    int         rc, fromlen;
237    struct sockaddr_in          from;
238   
239    gicc = oicc = licc = locc = hicc = hocc = ctlcc = 0;
240    acth = actg = actc = 32;                    /* XXX yummy */
241
242    noblock(actl);
243    noblock(acto);
244
245    for(;;)
246    {
247        FD_ZERO(&rdfs);
248        FD_ZERO(&wrfs);
249        FD_ZERO(&exfs);
250
251        /* always take input from the control port (if it's connected) */
252        FD_SET_rjb(actc, &rdfs);
253
254        if (gdbmode)
255        {
256            if (oicc)
257                FD_SET_rjb(actg, &wrfs);
258            else
259                FD_SET_rjb(acto, &rdfs);
260           
261            if (gicc)
262                FD_SET_rjb(acto, &wrfs);
263            else
264                FD_SET_rjb(actg, &rdfs);
265        }
266        else
267        {
268            /* There is no such thing as oibuf because its been split into
269             * lobuf and hobuf
270             */
271            if (locc || hocc)
272            {
273                if (locc)
274                    FD_SET_rjb(actl, &wrfs);
275                if (hocc)
276                    FD_SET_rjb(acth, &wrfs);
277            }
278            else
279                FD_SET_rjb(acto, &rdfs);
280           
281            if (licc)
282                FD_SET_rjb(acto, &wrfs);
283            else
284                FD_SET_rjb(actl, &rdfs);
285           
286            if (hicc)
287                FD_SET_rjb(acto, &wrfs);
288            else
289                FD_SET_rjb(acth, &rdfs);
290        }
291       
292        if (acth == 32 && lish>=0)      FD_SET_rjb(lish, &rdfs);
293        if (actg == 32)                 FD_SET_rjb(lisg, &rdfs);
294        if (actc == 32)                 FD_SET_rjb(lisc, &rdfs);
295
296        /* now make exfs the union of the read and write fd sets, plus
297         * "actl" */
298        {
299            int i;
300            exfs = rdfs;
301            for(i=0; i<32; i++)  /* XXX we only copy fd numbers up to 31 */
302                if (FD_ISSET(i, &wrfs))
303                    FD_SET_rjb(i, &exfs);
304            FD_SET_rjb(actl, &exfs);
305        }
306
307        /* XXX AND: can't print something of type fd_set as %x - it
308         * might be an array */
309        DB(fprintf(stderr, "%s: before select: %08x %08x %08x\n",
310                   prog_name, rdfs, wrfs, exfs));
311       
312        if (select(32, &rdfs, &wrfs, &exfs, NULL) < 0)
313            fault("select: %s\n", strerror(errno));
314       
315        DB(fprintf(stderr, "%s: after  select: %08x %08x %08x\n",
316                   prog_name, rdfs, wrfs, exfs));
317       
318        /* XXX it appears that a non-blocking socket may not show up
319         * correctly in exfs but instead goes readable with no data in
320         * it. Thus we check for zero and goto the appropriate close
321         * method.  */
322
323        /* Deal with exceptions */
324        if (FD_ISSET_rjb(actg, &exfs))
325        {
326        exfs_actg:
327            close(actg);
328            gdbmode = FALSE;
329            oicc = 0;
330            oiptr = oibuf;
331            actg = 32;
332            continue;           /* because assumptions changed */
333        }
334        if (FD_ISSET_rjb(acth, &exfs))
335        {
336        exfs_acth:
337            close(acth);
338            hicc = hocc = 0;
339            hiptr = hibuf;
340            hoptr = hibuf;
341            acth = 32;
342            continue;           /* because assumptions changed */
343        }
344        if (FD_ISSET_rjb(actl, &exfs) ||
345            FD_ISSET_rjb(acto, &exfs))
346        {
347        exfs_actl:
348        exfs_acto:
349            /* Thats all folks ... */
350            break;
351        }
352        if (FD_ISSET_rjb(actc, &exfs))
353        {
354        exfs_ctl:
355            close(actc);
356            actc = 32;
357            ctlcc = 0;
358            continue;
359        }
360
361        /* Deal with reading */
362        if (FD_ISSET_rjb(acto, &rdfs))
363        {
364            if ((oicc = read(acto, oiptr = oibuf, MAXSIZE)) < 0)
365                fault("read acto: %d: %s\n", oicc, strerror(errno));
366            if (!oicc) goto exfs_acto;
367           
368            if (!gdbmode)
369            {
370                int t;
371
372                assert((locc == 0) && (hocc == 0));
373                loptr = lobuf;
374                hoptr = hobuf;
375               
376                if (lish>=0) {
377                    for(t=0; t<oicc; t++)
378                        if (oibuf[t] & 0x80)
379                            hobuf[hocc++] = oibuf[t] & 0x7f;
380                        else
381                            lobuf[locc++] = oibuf[t];
382                } else {
383                    for (t=0; t<oicc; t++)
384                        lobuf[locc++] = oibuf[t];
385                }
386                /* If no high connection scratch that */
387                if (acth == 32)
388                    hocc=0;
389            }
390        }
391        if (FD_ISSET_rjb(actl, &rdfs))
392        {
393            if ((licc = read(actl, liptr = libuf, MAXSIZE)) < 0)
394                fault("read actl: %d: %s\n", licc, strerror(errno));
395            if (!licc) goto exfs_actl;
396        }
397        if (FD_ISSET_rjb(acth, &rdfs))
398        {
399            int t;
400           
401            if ((hicc = read(acth, hiptr = hibuf, MAXSIZE)) < 0)
402                fault("read acth: %d: %s\n", hicc, strerror(errno));
403            if (!hicc) goto exfs_acth;
404            for(t=0; t<hicc; t++)
405                hibuf[t] |= 0x80;
406        }
407        if (FD_ISSET_rjb(actg, &rdfs))
408        {
409            if ((gicc = read(actg, giptr = gibuf, MAXSIZE)) < 0)
410                fault("read actg: %d: %s\n", gicc, strerror(errno));
411            if (debug) write(1, giptr, gicc);           /* XXX */
412            if (!gicc) goto exfs_actg;
413        }
414        if (FD_ISSET_rjb(actc, &rdfs))
415        {
416            if ((ctlcc = read(actc, ctlbuf, MAXSIZE)) < 0)
417                fault("read actc: %d: %s\n", ctlcc, strerror(errno));
418            if (debug) write(1, ctlbuf, gicc);
419            if (!ctlcc) goto exfs_ctl;
420            if (ctlbuf[0] == 'r') /* reset command */
421            {
422                syslog(LOG_INFO, "reset command read, exiting");
423                if (debug) write(1, "reseting\n", sizeof("reseting\n"));
424                break;
425            }
426        }
427       
428        /* Deal with writing */
429        if (FD_ISSET_rjb(actg, &wrfs))
430        {
431            /* We must be in gdb mode so send oi buffer data */
432            assert(gdbmode);
433            if (debug) write(2, oiptr, oicc);           /* XXX */
434            if ((rc = write(actg, oiptr, oicc)) <= 0)
435                fault("write actg: %d: %s\n", rc, strerror(errno));
436            oiptr += rc;
437            oicc  -= rc;
438        }
439        if (FD_ISSET_rjb(actl, &wrfs))
440        {
441            if ((rc = write(actl, loptr, locc)) <= 0)
442                fault("write actl: %d: %s\n", rc, strerror(errno));
443            loptr += rc;
444            locc  -= rc;
445        }
446        if (FD_ISSET_rjb(acth, &wrfs))
447        {
448            if ((rc = write(acth, hoptr, hocc)) <= 0)
449                fault("write acth: %d: %s\n", rc, strerror(errno));
450            hoptr += rc;
451            hocc  -= rc;
452        }
453        if (FD_ISSET_rjb(acto, &wrfs))
454        {
455            /* If in gdb mode send gdb input, otherwise send low data
456               preferentially */
457            if (gdbmode)
458            {
459                assert(gicc);
460                if ((rc = write(acto, giptr, gicc)) <= 0)
461                    fault("write acto: %d: %s\n", rc, strerror(errno));
462                giptr += rc;
463                gicc  -= rc;
464            }
465            else
466            {
467                if (licc)
468                {
469                    if ((rc = write(acto, liptr, licc)) <= 0)
470                        fault("write acto: %d: %s\n", rc, strerror(errno));
471                    liptr += rc;
472                    licc  -= rc;
473                }
474                else
475                {
476                    assert(hicc);
477                    if ((rc = write(acto, hiptr, hicc)) <= 0)
478                        fault("write acto: %d: %s\n", rc, strerror(errno));
479                    hiptr += rc;
480                    hicc  -= rc;
481                }
482            }
483        }
484       
485        /* Deals with new connections */
486        if ((acth == 32) && lish>=0 && (FD_ISSET_rjb(lish, &rdfs)))
487        {
488            fromlen = sizeof(from);
489            if ((acth = accept(lish, &from, &fromlen)) < 0)
490            {
491                syslog(LOG_WARNING, "accept: %m");
492                acth = 32;
493            }
494            else
495            {
496                noblock(acth);
497                hicc = hocc = 0;
498                syslog(LOG_INFO, "highbit client peer is %s:%u\n",
499                       inet_ntoa(from.sin_addr), ntohs(from.sin_port));
500            }
501        }
502       
503        if ((actg == 32) && (FD_ISSET_rjb(lisg, &rdfs)))
504        {
505            fromlen = sizeof(from);
506            if ((actg = accept(lisg, &from, &fromlen)) < 0)
507            {
508                syslog(LOG_WARNING, "accept: %m");
509                actg = 32;
510            }
511            else
512            {
513                noblock(actg);
514                gicc = 0;
515                gdbmode = TRUE;
516                syslog(LOG_INFO, "gdb client peer is %s:%u\n",
517                       inet_ntoa(from.sin_addr), ntohs(from.sin_port));
518            }
519        }
520
521        if ((actc == 32) && (FD_ISSET_rjb(lisc, &rdfs)))
522        {
523            fromlen = sizeof(from);
524            if ((actc = accept(lisc, &from, &fromlen)) < 0)
525            {
526                syslog(LOG_WARNING, "accept (ctl): %m");
527                actc = 32;
528            }
529            else
530            {
531                noblock(actc);
532                syslog(LOG_INFO, "ctl client peer is %s:%u\n",
533                       inet_ntoa(from.sin_addr), ntohs(from.sin_port));
534            }
535        }
536           
537        /* Back to top of loop */
538    }
539   
540    /* We are bailing because one of the primary connections has gone
541     * away. We close these all explicitly here because that way the
542     * timeout on reusing the port numbers is smnaller. */
543   
544    close(acth);
545    close(actg);
546    /* XXX AND: why are we closing all these "character counts" ?? */
547    close(gicc);
548    close(oicc);
549    close(licc);
550    close(locc);
551    close(hicc);
552    close(hocc);
553}
554
555/*
556 * ------------------------------------------------------------
557 */
558int main(int argc, char **argv)
559{
560    /* In general, suffix "l" is low channel, "h" is high channel, "g"
561     * is gdb channel, "c" is control channel and "o" is output channel.
562     */
563    struct sockaddr_in          from;
564    int                         infd = 0, outfd;
565    unsigned short              portl, porth, portg, portc, porto;
566    int                         on = 1, c;
567    char                        *outname, *outservice;
568    int                         fromlen;
569    int                         lish, lisg, lisc;
570#if 0
571    FILE                        *newerr;
572#endif /* 0 */
573   
574    prog_name = argv[0];
575
576    if (isatty(infd))
577        usage();
578
579    /* Here, then not just a simple idiot. */
580
581    signal(SIGPIPE, SIG_IGN);
582
583    openlog(prog_name, LOG_PID, LOG_DAEMON);
584
585    fromlen = sizeof(from);
586    if (getsockname(infd, &from, &fromlen) < 0)
587        fault("getsockname: %s", strerror(errno));
588    if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET))
589        fault("not an inet socket (family=%d)\n", from.sin_family);
590   
591    portl = ntohs(from.sin_port);
592    porth = portl+1;
593    portg = porth+1;
594    portc = portg+1;
595
596    fromlen = sizeof(from);
597    if (getpeername(infd, &from, &fromlen) < 0)
598        fault("getpeername: %s", strerror(errno));
599    if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET))
600        fault("not an inet socket (family=%d)\n", from.sin_family);
601
602    syslog(LOG_INFO, "on port %u peer is %s:%u\n", portl,
603           inet_ntoa(from.sin_addr), ntohs(from.sin_port));
604   
605    if (setsockopt(infd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
606        syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
607
608    /* from here on, we map stderr to output on the connection so we can
609     * report errors to the remote user.
610     */
611#if 0
612    if (!(newerr = fdopen(infd, "w")))
613        syslog(LOG_WARNING, "fdopen: %m");
614    else
615        *stderr = *newerr;
616#endif
617       
618    while((c = getopt(argc, argv, "d8h:g:c:")) != EOF)
619    {
620        switch(c)
621        {
622        case 'd':
623            debug++;
624            break;
625           
626        case 'h':
627            /* high bit port */
628            if (getservice(optarg, &porth) < 0)
629                fault("getservice failed (high port '%s')\n", optarg);
630            break;
631           
632        case 'g':
633            /* gdb port */
634            if (getservice(optarg, &portg) < 0)
635                fault("getservice failed (gdb port '%s')\n", optarg);
636            break;
637
638        case 'c':
639            /* control port */
640            if (getservice(optarg, &portc) < 0)
641                fault("getservice failed (control port '%s')\n", optarg);
642            break;
643
644        case '8':
645            /* 8-bit clean; no high port */
646            porth=0;
647            break;
648
649        default:
650            fault("bad argument list!\n");
651        }
652    }
653   
654    if (argc != optind + 1)
655        fault("unparsed arguments (%d!=%d)\n", argc, optind+1);
656
657    outname = argv[optind];
658    if (!(outservice = strchr(outname, ':')))
659        fault("output arg '%s' doesn't contain ':'\n", outname);
660    *outservice++ = 0;
661    if (getservice(outservice, &porto) < 0)
662        fault("getservice failed (output port '%s')\n", outservice);
663   
664    /* Time to start the sockets */
665
666    if (porth) {
667        lish  = startlistening(porth);
668    } else {
669        lish  = -1;
670    }
671    lisg  = startlistening(portg);
672    lisc  = startlistening(portc);
673   
674    outfd = connect_host(outname, porto);
675   
676    doit(infd, outfd, lish, lisg, lisc);
677
678    syslog(LOG_INFO, "terminating normally\n");
679
680    fclose(stderr);
681
682    closelog();
683    exit(0); 
684}
685
686/* End $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ */
Note: See TracBrowser for help on using the repository browser.