]> mj.ucw.cz Git - sock.git/blob - sock.c
Initial revision
[sock.git] / sock.c
1 /*
2  *      Command-Line Socket Interface
3  *
4  *      (c) 1998 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 /*
11  * FIXME: --version
12  */
13
14 #include "config.h"
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <getopt.h>
22 #include <netinet/in.h>
23 #include <sys/un.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <netdb.h>
27 #include <sys/signal.h>
28 #include <sys/wait.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31
32 static char *opts = "vtuxb:lde";
33
34 static enum { ip, ux } addr = ip;
35 static enum { deflt, tcp, udp, uxp } proto = deflt;
36 static int listen_p, verbose, daemon_p, both_eof_p;
37 static char *cmd, *my_name;
38
39 static struct sockaddr sa_bind, sa_conn;
40 static char *na_bind, *na_conn;
41 static int sa_bind_p;
42
43 #ifdef __GNUC__
44 #define NORET __attribute__((noreturn))
45 #else
46 #define NORET
47 #endif
48
49 static void usage(void) NORET;
50 static void mdie(char *, ...) NORET;
51 static void die(char *, ...) NORET;
52
53 static void
54 usage(void)
55 {
56   fprintf(stderr, "\
57 Usage: %s [-vtuxde] ([-b <local>] <remote> | -l <local>) [<command>]\n\
58 \n\
59 -v\tBe verbose\n\
60 -t\tUse TCP socket (default)\n\
61 -u\tUse UDP socket\n\
62 -x\tUse UNIX-domain socket\n\
63 -b\tBind local end to specified address\n\
64 -l\tListening mode\n\
65 -d\tDaemon mode (listen only) -- process multiple connections\n\
66 -e\tTerminate only if EOF seen in both directions\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 char *
97 parse_addr(struct sockaddr *sa, char *a, int need_port, int need_addr)
98 {
99   char *o = a;
100
101   switch (addr)
102     {
103     case ip:
104       {
105         struct sockaddr_in *s = (struct sockaddr_in *) sa;
106         char *p = strchr(a, ':');
107
108         if (p)
109           *p++ = 0;
110         else if (need_port)
111           die("%s: port number required", a);
112         s->sin_family = AF_INET;
113         if (*a)
114           {
115             struct hostent *e = gethostbyname(a);
116             if (!e)
117               mdie(a);
118             if (e->h_addrtype != AF_INET && e->h_length != sizeof(struct in_addr))
119               die("%s: invalid address type", a);
120             memcpy(&s->sin_addr, e->h_addr, sizeof(struct in_addr));
121             o = e->h_name;
122           }
123         else if (need_addr)
124           die("Host name required");
125         if (p)
126           {
127             struct servent *e = NULL;
128
129             if (proto != udp)
130               {
131                 e = getservbyname(p, "tcp");
132                 if (e)
133                   proto = tcp;
134               }
135             else if (!e && proto != tcp)
136               {
137                 e = getservbyname(p, "udp");
138                 if (e)
139                   proto = udp;
140               }
141             if (!e)
142               {
143                 char *z;
144                 long int i = strtol(p, &z, 10);
145                 if (i <= 0 || i > 0xffff || (z && *z))
146                   die("%s: invalid port", p);
147                 s->sin_port = htons(i);
148               }
149             else
150               s->sin_port = e->s_port;
151             p[-1] = ':';
152           }
153         break;
154       }
155     case ux:
156       {
157         struct sockaddr_un *s = (struct sockaddr_un *) sa;
158         if (strlen(a) >= sizeof(s->sun_path))
159           die("%s: address too long", a);
160         s->sun_family = AF_UNIX;
161         strcpy(s->sun_path, a);
162         break;
163       }
164     }
165   return o;
166 }
167
168 static char *
169 name_addr(struct sockaddr *sa)
170 {
171   switch (addr)
172     {
173     case ip:
174       {
175         struct sockaddr_in *a = (struct sockaddr_in *) sa;
176         struct hostent *h = gethostbyaddr((char *) &a->sin_addr.s_addr, sizeof(struct in_addr), AF_INET);
177         if (!h)
178           mdie("xxx");
179         return h->h_name;
180       }
181     case ux:
182       {
183         struct sockaddr_un *a = (struct sockaddr_un *) sa;
184         return a->sun_path;
185       }
186     default:
187       return "?";
188     }
189 }
190
191 static RETSIGTYPE
192 sigchld_handler(int sig)
193 {
194   while (wait3(NULL, WNOHANG, NULL) > 0)
195     ;
196 }
197
198 static void
199 setup_sigchld(void)
200 {
201   struct sigaction sa;
202
203   bzero(&sa, sizeof(sa));
204   sa.sa_handler = sigchld_handler;
205   sa.sa_flags = SA_RESTART;
206   if (sigaction(SIGCHLD, &sa, NULL) < 0)
207     mdie("sigaction");
208 }
209
210 static void
211 gw(int sk)
212 {
213   if (cmd)
214     {
215       char *sh = getenv("SHELL");
216       if (!sh)
217         sh = "/bin/sh";
218       close(0);
219       close(1);
220       if (dup(sk) != 0 || dup(sk) != 1)
221         mdie("dup");
222       close(sk);
223       execl(sh, sh, "-c", cmd, NULL);
224       mdie("exec");
225     }
226   else
227     {
228       char ib[4096], ob[4096];
229       char *ibr = ib;
230       char *ibw = ib;
231       char *ibe = ib + sizeof(ib);
232       char *obr = ob;
233       char *obw = ob;
234       char *obe = ob + sizeof(ob);
235       int ieof = 0;
236       int oeof = 0;
237       int n;
238       fd_set in, out;
239
240       if (fcntl(0, F_SETFL, O_NDELAY) < 0 ||
241           fcntl(1, F_SETFL, O_NDELAY) < 0 ||
242           fcntl(sk, F_SETFL, O_NDELAY) < 0)
243         mdie("fcntl");
244       FD_ZERO(&in);
245       FD_ZERO(&out);
246       for(;;)
247         {
248           if (ibr < ibe && !ieof)
249             FD_SET(0, &in);
250           if (ibw < ibr)
251             FD_SET(sk, &out);
252           if (obr < obe && !oeof)
253             FD_SET(sk, &in);
254           if (obw < obr)
255             FD_SET(1, &out);
256           if (ibr == ib && ieof == 1)
257             {
258               shutdown(sk, 1);
259               ieof = 2;
260             }
261           if (ibr == ib && obr == ob &&
262               (both_eof_p ? (ieof && oeof) : (ieof || oeof)))
263             break;
264           if (select(sk+1, &in, &out, NULL, NULL) < 0)
265             mdie("select");
266           if (FD_ISSET(sk, &out))
267             {
268               FD_CLR(sk, &out);
269               n = write(sk, ibw, ibr - ibw);
270               if (n < 0 && errno != EAGAIN && errno != EINTR)
271                 mdie("socket write");
272               ibw += n;
273               if (ibr == ibw)
274                 ibr = ibw = ib;
275             }
276           if (FD_ISSET(1, &out))
277             {
278               FD_CLR(1, &out);
279               n = write(1, obw, obr - obw);
280               if (n < 0 && errno != EAGAIN && errno != EINTR)
281                 mdie("stdio write");
282               obw += n;
283               if (obr == obw)
284                 obr = obw = ob;
285             }
286           if (FD_ISSET(0, &in))
287             {
288               FD_CLR(0, &in);
289               n = read(0, ibr, ibe - ibr);
290               if (n < 0 && errno != EAGAIN && errno != EINTR)
291                 mdie("stdio read");
292               if (n)
293                 ibr += n;
294               else
295                 ieof = 1;
296             }
297           if (FD_ISSET(sk, &in))
298             {
299               FD_CLR(sk, &in);
300               n = read(sk, obr, obe - obr);
301               if (n < 0 && errno != EAGAIN && errno != EINTR)
302                 mdie("socket read");
303               if (n)
304                 obr += n;
305               else
306                 oeof = 1;
307             }
308         }
309     }
310 }
311
312 int
313 main(int argc, char **argv)
314 {
315   int c, sk;
316
317   my_name = argv[0];
318   while ((c = getopt(argc, argv, opts)) > 0)
319     switch (c)
320       {
321       case 'v':
322         verbose++;
323         break;
324       case 'd':
325         daemon_p++;
326         break;
327       case 't':
328         proto = tcp;
329         break;
330       case 'u':
331         proto = udp;
332         break;
333       case 'x':
334         addr = ux;
335         proto = uxp;
336         break;
337       case 'l':
338         listen_p++;
339         break;
340       case 'b':
341         if (sa_bind_p++)
342           usage();
343         na_bind = parse_addr(&sa_bind, optarg, 0, 0);
344         break;
345       case 'e':
346         both_eof_p++;
347         break;
348       default:
349         usage();
350       }
351
352   if (argc == optind + 2)
353     cmd = argv[optind+1];
354   else if (argc != optind + 1)
355     usage();
356
357   if (listen_p)
358     {
359       if (sa_bind_p)
360         usage();
361       na_bind = parse_addr(&sa_bind, argv[optind], 1, 0);
362       sa_bind_p = 1;
363     }
364   else
365     na_conn = parse_addr(&sa_conn, argv[optind], 1, 1);
366
367   switch (proto)
368     {
369     case deflt:
370     case tcp:
371       sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
372       break;
373     case udp:
374       sk = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
375       break;
376     case uxp:
377       sk = socket(PF_UNIX, SOCK_STREAM, 0);
378       break;
379     default:
380       sk = -1;
381     }
382   if (sk < 0)
383     mdie("socket");
384   if (sa_bind_p)
385     {
386       int one = 1;
387       if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
388         mdie("setsockopt(SO_REUSEADDR)");
389       if (bind(sk, &sa_bind, sizeof(sa_bind)) < 0)
390         mdie("bind");
391     }
392
393   if (listen_p)
394     {
395       int l = sizeof(sa_conn);
396       if (verbose)
397         fprintf(stderr, "Listening on %s\n", na_bind);
398       if (listen(sk, (daemon_p ? 10 : 1)) < 0)
399         mdie("listen");
400       if (cmd && daemon_p)
401         setup_sigchld();
402       for(;;)
403         {
404           int ns = accept(sk, &sa_conn, &l);
405           if (ns < 0)
406             mdie("accept");
407           if (verbose)
408             fprintf(stderr, "Got connection from %s\n", name_addr(&sa_conn));
409           if (!daemon_p)
410             {
411               close(sk);
412               gw(ns);
413               return 0;
414             }
415           if (cmd)
416             {
417               pid_t p = fork();
418               if (p < 0)
419                 mdie("fork");
420               if (!p)
421                 {
422                   close(sk);
423                   gw(ns);
424                   exit(0);
425                 }
426             }
427           else
428             gw(ns);
429           close(ns);
430         }
431     }
432   else
433     {
434       if (verbose)
435         fprintf(stderr, "Connecting to %s\n", na_conn);
436       if (connect(sk, &sa_conn, sizeof(sa_conn)) < 0)
437         mdie("connect");
438       gw(sk);
439     }
440
441   return 0;
442 }