]> mj.ucw.cz Git - sock.git/blob - sock.c
Debian: Squeeze needs debian/compat
[sock.git] / sock.c
1 /*
2  *      Command-Line Socket Interface
3  *
4  *      (c) 1998--2001 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
5  *
6  *      This program can be freely distributed and used according
7  *      to the terms of the GNU General Public Licence.
8  */
9
10 #include "config.h"
11
12 #include <sys/types.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <stdarg.h>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
21 #include <sys/un.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <netdb.h>
25 #include <signal.h>
26 #include <sys/wait.h>
27 #include <sys/time.h>
28
29 #define SOCK_VERSION "1.1"
30
31 static char *opts = "vtuxb:lden";
32
33 static enum { ip, ux } addr = ip;
34 static enum { deflt, tcp, udp, uxp } proto = deflt;
35 static int listen_p, verbose, daemon_p, single_eof_p, avoid_dns_p;
36 static char *cmd, *my_name;
37
38 static struct sockaddr *sa_bind, *sa_conn;
39 static char *na_bind, *na_conn;
40 static int sock_addr_length;
41
42 #ifdef __GNUC__
43 #define NORET __attribute__((noreturn))
44 #else
45 #define NORET
46 #endif
47
48 static void usage(void) NORET;
49 static void mdie(char *, ...) NORET;
50 static void die(char *, ...) NORET;
51
52 static void
53 usage(void)
54 {
55   fprintf(stderr, "\
56 Usage: %s [-vtuxde] ([-b <local>] <remote> | -l <local>) [<command>]\n\
57 \n\
58 -v\tBe verbose\n\
59 -t\tUse TCP socket (default)\n\
60 -u\tUse UDP socket\n\
61 -x\tUse UNIX-domain socket\n\
62 -b\tBind local end to specified address\n\
63 -l\tListening mode\n\
64 -d\tDaemon mode (listen only) -- process multiple connections\n\
65 -e\tTerminate as soon as EOF is seen in any direction\n\
66 -n\tAvoid reverse DNS lookups\n\
67 ", my_name);
68   exit(1);
69 }
70
71 static void
72 mdie(char *x, ...)
73 {
74   va_list args;
75   char *e = strerror(errno);
76
77   va_start(args, x);
78   fprintf(stderr, "%s: ", my_name);
79   vfprintf(stderr, x, args);
80   fprintf(stderr, ": %s\n", e);
81   exit(1);
82 }
83
84 static void
85 die(char *x, ...)
86 {
87   va_list args;
88
89   va_start(args, x);
90   fprintf(stderr, "%s: ", my_name);
91   vfprintf(stderr, x, args);
92   fputc('\n', stderr);
93   exit(1);
94 }
95
96 static void *
97 xmalloc(int size)
98 {
99   void *x = malloc(size);
100   if (!x)
101     die("Out of memory");
102   return x;
103 }
104
105 static struct sockaddr *
106 alloc_sockaddr(void)
107 {
108   struct sockaddr *sa;
109   sock_addr_length = (addr == ux) ? sizeof(struct sockaddr_un) : sizeof(struct sockaddr_in);
110   sa = xmalloc(sock_addr_length);
111   memset(sa, 0, sizeof(*sa));
112   sa->sa_family = (addr == ux) ? AF_UNIX : AF_INET;
113   return sa;
114 }
115
116 static char *
117 parse_addr(struct sockaddr **sap, char *a, int need_port, int need_addr)
118 {
119   char *o = a;
120   struct sockaddr *sa = alloc_sockaddr();
121
122   switch (addr)
123     {
124     case ip:
125       {
126         struct sockaddr_in *s = (struct sockaddr_in *) sa;
127         char *p = strchr(a, ':');
128
129         if (p)
130           *p++ = 0;
131         else if (need_port)
132           die("%s: port number required", a);
133         s->sin_family = AF_INET;
134         if (!*a && need_addr)
135           a = "localhost";
136         if (*a)
137           {
138             struct hostent *e = gethostbyname(a);
139             if (!e)
140               mdie(a);
141             if (e->h_addrtype != AF_INET && e->h_length != sizeof(struct in_addr))
142               die("%s: invalid address type", a);
143             memcpy(&s->sin_addr, e->h_addr, sizeof(struct in_addr));
144             o = e->h_name;
145           }
146         if (p)
147           {
148             struct servent *e = NULL;
149
150             if (proto != udp)
151               {
152                 e = getservbyname(p, "tcp");
153                 if (e)
154                   proto = tcp;
155               }
156             else if (!e && proto != tcp)
157               {
158                 e = getservbyname(p, "udp");
159                 if (e)
160                   proto = udp;
161               }
162             if (!e)
163               {
164                 char *z;
165                 long int i = strtol(p, &z, 10);
166                 if (i <= 0 || i > 0xffff || (z && *z))
167                   die("%s: invalid port", p);
168                 s->sin_port = htons(i);
169               }
170             else
171               s->sin_port = e->s_port;
172             p[-1] = ':';
173           }
174         break;
175       }
176     case ux:
177       {
178         struct sockaddr_un *s = (struct sockaddr_un *) sa;
179         if (strlen(a) >= sizeof(s->sun_path))
180           die("%s: address too long", a);
181         s->sun_family = AF_UNIX;
182         strcpy(s->sun_path, a);
183         break;
184       }
185     }
186   *sap = sa;
187   return o;
188 }
189
190 static char *
191 name_addr(struct sockaddr *sa)
192 {
193   switch (addr)
194     {
195     case ip:
196       {
197         struct sockaddr_in *a = (struct sockaddr_in *) sa;
198         struct hostent *h = avoid_dns_p ? NULL : gethostbyaddr((char *) &a->sin_addr.s_addr, sizeof(struct in_addr), AF_INET);
199         if (h)
200           return h->h_name;
201         else
202           return inet_ntoa(a->sin_addr);
203       }
204     case ux:
205       {
206         struct sockaddr_un *a = (struct sockaddr_un *) sa;
207         return a->sun_path;
208       }
209     default:
210       return "?";
211     }
212 }
213
214 static RETSIGTYPE
215 sigchld_handler(int sig)
216 {
217   while (waitpid(-1, NULL, WNOHANG) > 0)
218     ;
219 }
220
221 static void
222 setup_sigchld(void)
223 {
224   struct sigaction sa;
225
226   memset(&sa, 0, sizeof(sa));
227   sa.sa_handler = sigchld_handler;
228   sa.sa_flags = SA_RESTART;
229   if (sigaction(SIGCHLD, &sa, NULL) < 0)
230     mdie("sigaction");
231 }
232
233 static void
234 gw(int sk)
235 {
236   if (cmd)
237     {
238       char *sh = getenv("SHELL");
239       if (!sh)
240         sh = "/bin/sh";
241       close(0);
242       close(1);
243       if (dup(sk) != 0 || dup(sk) != 1)
244         mdie("dup");
245       close(sk);
246       execl(sh, sh, "-c", cmd, NULL);
247       mdie("exec");
248     }
249   else
250     {
251       char ib[4096], ob[4096];
252       char *ibr = ib;
253       char *ibw = ib;
254       char *ibe = ib + sizeof(ib);
255       char *obr = ob;
256       char *obw = ob;
257       char *obe = ob + sizeof(ob);
258       int ieof = 0;
259       int oeof = 0;
260       int n;
261       fd_set in, out;
262
263       if (proto == udp)
264         {
265           if (listen_p)
266             ieof = 1;
267           else
268             oeof = 1;
269         }
270       if (!ieof && fcntl(0, F_SETFL, O_NDELAY) < 0 ||
271           !oeof && fcntl(1, F_SETFL, O_NDELAY) < 0 ||
272           fcntl(sk, F_SETFL, O_NDELAY) < 0)
273         mdie("fcntl");
274       FD_ZERO(&in);
275       FD_ZERO(&out);
276       for(;;)
277         {
278           if (ibr < ibe && !ieof)
279             FD_SET(0, &in);
280           if (ibw < ibr)
281             FD_SET(sk, &out);
282           if (obr < obe && !oeof)
283             FD_SET(sk, &in);
284           if (obw < obr)
285             FD_SET(1, &out);
286           if (ibr == ib && ieof == 1)
287             {
288               shutdown(sk, 1);
289               ieof = 2;
290             }
291           if (ibr == ib && obr == ob &&
292               (single_eof_p ? (ieof || oeof) : (ieof && oeof)))
293             break;
294           if (select(sk+1, &in, &out, NULL, NULL) < 0)
295             mdie("select");
296           if (FD_ISSET(sk, &out))
297             {
298               FD_CLR(sk, &out);
299               n = write(sk, ibw, ibr - ibw);
300               if (n < 0 && errno != EAGAIN && errno != EINTR)
301                 mdie("socket write");
302               ibw += n;
303               if (ibr == ibw)
304                 ibr = ibw = ib;
305             }
306           if (FD_ISSET(1, &out))
307             {
308               FD_CLR(1, &out);
309               n = write(1, obw, obr - obw);
310               if (n < 0 && errno != EAGAIN && errno != EINTR)
311                 mdie("stdio write");
312               obw += n;
313               if (obr == obw)
314                 obr = obw = ob;
315             }
316           if (FD_ISSET(0, &in))
317             {
318               FD_CLR(0, &in);
319               n = read(0, ibr, ibe - ibr);
320               if (n < 0 && errno != EAGAIN && errno != EINTR)
321                 mdie("stdio read");
322               if (n)
323                 ibr += n;
324               else
325                 ieof = 1;
326             }
327           if (FD_ISSET(sk, &in))
328             {
329               FD_CLR(sk, &in);
330               n = read(sk, obr, obe - obr);
331               if (n < 0 && errno != EAGAIN && errno != EINTR)
332                 mdie("socket read");
333               if (n)
334                 obr += n;
335               else
336                 oeof = 1;
337             }
338         }
339     }
340 }
341
342 int
343 main(int argc, char **argv)
344 {
345   int c, sk;
346
347   if (argc == 2 && !strcmp(argv[1], "--version"))
348     {
349       puts("This is sock " SOCK_VERSION ". Be happy.");
350       return 0;
351     }
352
353   my_name = argv[0];
354   while ((c = getopt(argc, argv, opts)) > 0)
355     switch (c)
356       {
357       case 'v':
358         verbose++;
359         break;
360       case 'd':
361         daemon_p++;
362         break;
363       case 't':
364         if (proto != deflt)
365           usage();
366         proto = tcp;
367         break;
368       case 'u':
369         if (proto != deflt)
370           usage();
371         proto = udp;
372         break;
373       case 'x':
374         if (proto != deflt)
375           usage();
376         proto = uxp;
377         break;
378       case 'l':
379         listen_p++;
380         break;
381       case 'b':
382         if (na_bind)
383           usage();
384         na_bind = optarg;
385         break;
386       case 'e':
387         single_eof_p++;
388         break;
389       case 'n':
390         avoid_dns_p++;
391         break;
392       default:
393         usage();
394       }
395
396   if (argc == optind + 2)
397     cmd = argv[optind+1];
398   else if (argc != optind + 1)
399     usage();
400
401   if (proto == uxp)
402     addr = ux;
403
404   if (listen_p)
405     {
406       if (na_bind)
407         usage();
408       na_bind = parse_addr(&sa_bind, argv[optind], 1, 0);
409     }
410   else
411     {
412       if (na_bind)
413         na_bind = parse_addr(&sa_bind, na_bind, 0, 0);
414       na_conn = parse_addr(&sa_conn, argv[optind], 1, 1);
415     }
416
417   switch (proto)
418     {
419     case deflt:
420     case tcp:
421       sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
422       break;
423     case udp:
424       sk = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
425       break;
426     case uxp:
427       sk = socket(PF_UNIX, SOCK_STREAM, 0);
428       break;
429     default:
430       sk = -1;
431     }
432   if (sk < 0)
433     mdie("socket");
434   if (sa_bind)
435     {
436       int one = 1;
437       if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, (void *) &one, sizeof(one)) < 0)
438         mdie("setsockopt(SO_REUSEADDR)");
439       if (bind(sk, sa_bind, sock_addr_length) < 0)
440         mdie("bind");
441     }
442
443   if (listen_p)
444     {
445       struct sockaddr *sa_incoming;
446       if (verbose)
447         fprintf(stderr, "Listening on %s\n", na_bind);
448       if (proto == udp)
449         {
450           gw(sk);
451           return 0;
452         }
453       if (listen(sk, (daemon_p ? 10 : 1)) < 0)
454         mdie("listen");
455       if (cmd && daemon_p)
456         setup_sigchld();
457       sa_incoming = alloc_sockaddr();
458       for(;;)
459         {
460           socklen_t l = sock_addr_length;
461           int ns = accept(sk, sa_incoming, &l);
462           if (ns < 0)
463             mdie("accept");
464           if (verbose)
465             fprintf(stderr, "Got connection from %s\n", name_addr(sa_incoming));
466           if (!daemon_p)
467             {
468               close(sk);
469               gw(ns);
470               return 0;
471             }
472           if (cmd)
473             {
474               pid_t p = fork();
475               if (p < 0)
476                 mdie("fork");
477               if (!p)
478                 {
479                   close(sk);
480                   gw(ns);
481                   exit(0);
482                 }
483             }
484           else
485             gw(ns);
486           close(ns);
487         }
488     }
489   else
490     {
491       if (verbose)
492         fprintf(stderr, "Connecting to %s\n", na_conn);
493       if (connect(sk, sa_conn, sock_addr_length) < 0)
494         mdie("connect");
495       gw(sk);
496     }
497
498   return 0;
499 }