ia64/xen-unstable

view tools/misc/nsplitd/nsplitd.c @ 17838:e5c9c8e6e726

tools: replace sprintf with snprintf where applicable

Signed-off-by: Christoph Egger <Christoph.Egger@amd.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jun 12 15:41:15 2008 +0100 (2008-06-12)
parents 0e23f01219c6
children
line source
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 */
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 */
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 */
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>
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>
67 #ifndef FALSE
68 #define FALSE 0
69 #endif
70 #ifndef TRUE
71 #define TRUE 1
72 #endif
74 #ifndef LOG_DAEMON
75 #define LOG_DAEMON 0
76 #endif
78 #define DB(x) /* ((x), fflush(stderr)) */
80 extern char *optarg;
82 extern int optind, opterr, optopt;
84 static char *prog_name;
86 static 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 }
96 static void fault(char *format, ...)
97 {
98 va_list ap;
99 char logbuf[1024];
101 va_start(ap, format);
102 fprintf(stderr, "%s: ", prog_name);
103 vfprintf(stderr, format, ap);
104 fflush(stderr);
105 va_end(ap);
107 /* XXX This is a bit dubious, but there is no vsyslog */
108 va_start(ap, format);
109 vsnprintf(logbuf, sizeof(logbuf), format, ap);
110 syslog(LOG_ERR, logbuf);
111 va_end(ap);
112 exit(1);
113 }
115 static int getservice(char *name, unsigned short *port)
116 {
117 struct servent *se;
119 if (!name) return -1;
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 }
132 /*
133 * connect_host: connect to ("name", "port")
134 */
135 static int connect_host (char *name, unsigned int port)
136 {
137 int fd;
138 struct hostent *hostent;
139 struct sockaddr_in sin;
140 int on;
142 if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
143 fault("socket");
145 if (!(hostent = gethostbyname(name)))
146 fault("gethostbyname: %s: %s\n", name, strerror(errno));
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));
153 if (connect(fd, (struct sockaddr *) &sin, sizeof (sin)) < 0)
154 fault("connect: %s:%u: %s\n", name, port, strerror(errno));
156 on = 1;
157 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
158 syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
160 on = 1;
161 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
162 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
164 return fd;
165 }
167 /*
168 * open a tcp socket and start listening for connections on it
169 */
170 static int startlistening(unsigned short port)
171 {
172 int fd, on;
173 struct sockaddr_in sin;
175 if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
176 fault("socket");
178 on = 1;
179 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
180 syslog(LOG_WARNING, "setsockopt (SO_REUSEADDR): %m");
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));
189 if (listen(fd, 1) < 0)
190 fault("listen: %s\n", strerror(errno));
192 return fd;
193 }
195 static void noblock(int fd)
196 {
197 int on=1;
199 if (ioctl(fd, FIONBIO, &on) < 0)
200 fault("ioctl: FIONBIO: %s\n", strerror(errno));
201 }
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) \
209 do { \
210 if ((fd) != 32) \
211 FD_SET((fd), (setp)); \
212 } while(0)
214 #define FD_ISSET_rjb(fd, setp) (((fd) != 32)? FD_ISSET((fd), (setp)) : 0)
216 #define MAXSIZE 256
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. */
223 static int debug;
225 static 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;
239 gicc = oicc = licc = locc = hicc = hocc = ctlcc = 0;
240 acth = actg = actc = 32; /* XXX yummy */
242 noblock(actl);
243 noblock(acto);
245 for(;;)
246 {
247 FD_ZERO(&rdfs);
248 FD_ZERO(&wrfs);
249 FD_ZERO(&exfs);
251 /* always take input from the control port (if it's connected) */
252 FD_SET_rjb(actc, &rdfs);
254 if (gdbmode)
255 {
256 if (oicc)
257 FD_SET_rjb(actg, &wrfs);
258 else
259 FD_SET_rjb(acto, &rdfs);
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);
281 if (licc)
282 FD_SET_rjb(acto, &wrfs);
283 else
284 FD_SET_rjb(actl, &rdfs);
286 if (hicc)
287 FD_SET_rjb(acto, &wrfs);
288 else
289 FD_SET_rjb(acth, &rdfs);
290 }
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);
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 }
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));
312 if (select(32, &rdfs, &wrfs, &exfs, NULL) < 0)
313 fault("select: %s\n", strerror(errno));
315 DB(fprintf(stderr, "%s: after select: %08x %08x %08x\n",
316 prog_name, rdfs, wrfs, exfs));
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. */
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 }
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;
368 if (!gdbmode)
369 {
370 int t;
372 assert((locc == 0) && (hocc == 0));
373 loptr = lobuf;
374 hoptr = hobuf;
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;
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 }
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 }
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 }
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 }
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 }
537 /* Back to top of loop */
538 }
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. */
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 }
555 /*
556 * ------------------------------------------------------------
557 */
558 int 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 */
574 prog_name = argv[0];
576 if (isatty(infd))
577 usage();
579 /* Here, then not just a simple idiot. */
581 signal(SIGPIPE, SIG_IGN);
583 openlog(prog_name, LOG_PID, LOG_DAEMON);
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);
591 portl = ntohs(from.sin_port);
592 porth = portl+1;
593 portg = porth+1;
594 portc = portg+1;
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);
602 syslog(LOG_INFO, "on port %u peer is %s:%u\n", portl,
603 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
605 if (setsockopt(infd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
606 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
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
618 while((c = getopt(argc, argv, "d8h:g:c:")) != EOF)
619 {
620 switch(c)
621 {
622 case 'd':
623 debug++;
624 break;
626 case 'h':
627 /* high bit port */
628 if (getservice(optarg, &porth) < 0)
629 fault("getservice failed (high port '%s')\n", optarg);
630 break;
632 case 'g':
633 /* gdb port */
634 if (getservice(optarg, &portg) < 0)
635 fault("getservice failed (gdb port '%s')\n", optarg);
636 break;
638 case 'c':
639 /* control port */
640 if (getservice(optarg, &portc) < 0)
641 fault("getservice failed (control port '%s')\n", optarg);
642 break;
644 case '8':
645 /* 8-bit clean; no high port */
646 porth=0;
647 break;
649 default:
650 fault("bad argument list!\n");
651 }
652 }
654 if (argc != optind + 1)
655 fault("unparsed arguments (%d!=%d)\n", argc, optind+1);
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);
664 /* Time to start the sockets */
666 if (porth) {
667 lish = startlistening(porth);
668 } else {
669 lish = -1;
670 }
671 lisg = startlistening(portg);
672 lisc = startlistening(portc);
674 outfd = connect_host(outname, porto);
676 doit(infd, outfd, lish, lisg, lisc);
678 syslog(LOG_INFO, "terminating normally\n");
680 fclose(stderr);
682 closelog();
683 exit(0);
684 }
686 /* End $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ */