chiark / gitweb /
Use new mdup(3mLib) function.
[tripe] / server / tun-slip.c
1 /* -*-c-*-
2  *
3  * Tunnel packets via SLIP
4  *
5  * (c) 2005 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Trivial IP Encryption (TrIPE).
11  *
12  * TrIPE is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * TrIPE is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with TrIPE; if not, write to the Free Software Foundation,
24  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #define TUN_INTERNALS
30
31 #include "tripe.h"
32
33 /*----- Data structures ---------------------------------------------------*/
34
35 typedef struct slipif {
36   struct slipif *next;                  /* Next one in the list */
37   int ifd, ofd;                         /* File descriptors to talk on */
38   char *name;                           /* Interface name */
39   pid_t kid;                            /* Child process id */
40   unsigned f;                           /* Various flags */
41 #   define F_INUSE 1u                   /*   Interface is in use */
42 #   define F_DYNAMIC 2u                 /*   Interface found dynamically */
43 } slipif;
44
45 struct tunnel {
46   const tunnel_ops *ops;                /* Pointer to operations */
47   slipif *sl;                           /* My interface record */
48   sel_file f;                           /* Selector for SLIP tty */
49   struct peer *p;                       /* Pointer to my peer */
50   unsigned st;                          /* Current parser state */
51 #   define ST_ESC 1u                    /*   Last saw an escape character */
52 #   define ST_BAD 2u                    /*   This packet is malformed */
53 #   define ST_EOF 4u                    /*   File descriptor reported EOF */
54   size_t n;                             /* Number of bytes used in buffer */
55   octet buf[PKBUFSZ];                   /* Buffer for incoming data */
56 };
57
58 /*----- Static variables --------------------------------------------------*/
59
60 static slipif *slipifs;                 /* List of available interfaces */
61 static const char *slipcmd;             /* Script to make new interfaces */
62
63 /*----- Main code ---------------------------------------------------------*/
64
65 /* --- @t_read@ --- *
66  *
67  * Arguments:   @int fd@ = file descriptor to read
68  *              @unsigned mode@ = what's happened
69  *              @void *v@ = pointer to tunnel block
70  *
71  * Returns:     ---
72  *
73  * Use:         Reads data from the tunnel.
74  */
75
76 static void t_read(int fd, unsigned mode, void *v)
77 {
78   tunnel *t = v;
79   ssize_t n;
80   const octet *p, *l, *ll;
81   octet *q;
82   unsigned st;
83   octet o;
84   buf b;
85
86   /* --- Read the input data --- */
87
88   n = read(fd, buf_t, sizeof(buf_t));
89   if (n < 0) {
90     if (errno == EINTR ||
91 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
92         errno == EWOULDBLOCK ||
93 #endif
94         errno == EAGAIN)
95       return;
96     a_warn("TUN", "%s", p_ifname(t->p), "read-error", "?ERRNO", A_END);
97     return;
98   }
99   if (!n) {
100     a_warn("TUN", "%s", p_ifname(t->p), "slip", "eof", A_END);
101     t->st = ST_EOF;
102     sel_rmfile(&t->f);
103     return;
104   }
105   IF_TRACING(T_TUNNEL, {
106     trace_block(T_PACKET, "tun-slip: SLIP-encapsulated data",
107                 buf_t, n);
108   })
109
110   /* --- Decapsulate the packet --- */
111
112   for (p = buf_t, l = p + n, st = t->st,
113        q = t->buf + t->n, ll = t->buf + sizeof(t->buf);
114        p < l;
115        p++) {
116     o = *p;
117     switch (o) {
118       case SL_END:
119         if (st & ST_BAD)
120           ;
121         else if (st & ST_ESC)
122           a_warn("TUN", "%s", p_ifname(t->p), "slip", "escape-end", A_END);
123         else if (q == t->buf) {
124           T( trace(T_TUNNEL, "tun-slip: empty packet"); )
125         } else {
126           IF_TRACING(T_TUNNEL, {
127             trace(T_TUNNEL, "tun-slip: packet arrived");
128             trace_block(T_PACKET, "tun-slip: packet contents",
129                         t->buf, q - t->buf);
130           })
131           buf_init(&b, t->buf, q - t->buf);
132           p_tun(t->p, &b);
133         }
134         q = t->buf;
135         st &= ~(ST_ESC | ST_BAD);
136         break;
137       case SL_ESC:
138         if ((st & ST_ESC) && !(st & ST_BAD)) {
139           a_warn("TUN", "%s", p_ifname(t->p), "slip", "bad-escape", A_END);
140           st |= ST_BAD;
141         } else
142           st |= ST_ESC;
143         break;
144       case SL_ESCEND:
145         if (st & ST_ESC)
146           o = SL_END;
147         goto emit;
148       case SL_ESCESC:
149         if (st & ST_ESC)
150           o = SL_ESC;
151         goto emit;
152       default:
153         if ((st & ST_ESC) && !(st & ST_BAD)) {
154           a_warn("TUN", "%s", p_ifname(t->p), "slip", "bad-escape", A_END);
155           st |= ST_BAD;
156         }
157       emit:
158         if (!(st & ST_BAD)) {
159           if (q < ll)
160             *q++ = o;
161           else {
162             a_warn("TUN", "%s", p_ifname(t->p), "slip", "overflow", A_END);
163             st |= ST_BAD;
164           }
165         }
166         st &= ~ST_ESC;
167         break;
168     }
169   }
170
171   t->n = q - t->buf;
172   t->st = st;
173 }
174
175 /* --- @t_init@ --- *
176  *
177  * Arguments:   ---
178  *
179  * Returns:     ---
180  *
181  * Use:         Initializes the tunneling system.  Maybe this will require
182  *              opening file descriptors or something.
183  */
184
185 static void t_init(void)
186 {
187   char *p, *q;
188   dstr d = DSTR_INIT;
189   slipif *sl, **tail = &slipifs;
190   unsigned long uli, ulo;
191   size_t n;
192
193   if ((p = getenv("TRIPE_SLIPIF")) == 0)
194     return;
195
196   /* --- Build the list of available interfaces --- */
197
198   dstr_puts(&d, p);
199
200   p = d.buf;
201   for (;;) {
202     if (*p == '/' || *p == '.') {
203       slipcmd = p;
204       T( trace(T_TUNNEL, "tun-slip: declared slip command `%s'", slipcmd); )
205       break;
206     }
207     uli = strtoul(p, &q, 0);
208     if (uli > INT_MAX || q == p)
209       goto whine;
210     if (*q != ',')
211       ulo = uli;
212     else {
213       p = q + 1;
214       ulo = strtoul(p, &q, 0);
215       if (ulo > INT_MAX || q == p)
216         goto whine;
217     }
218     if (*q != '=' || (n = strcspn(q + 1, ":")) == 0)
219       goto whine;
220     sl = CREATE(slipif);
221     sl->next = 0;
222     sl->ifd = uli;
223     fdflags(sl->ifd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
224     fdflags(sl->ofd, O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
225     sl->ofd = ulo;
226     sl->name = xmalloc(n + 1);
227     sl->kid = -1;
228     sl->f = 0;
229     memcpy(sl->name, q + 1, n);
230     sl->name[n] = 0;
231     *tail = sl;
232     tail = &sl->next;
233     T( trace(T_TUNNEL, "tun-slip: declared slipif %d,%d=%s",
234              sl->ifd, sl->ofd, sl->name); )
235     p = q + n + 1;
236     if (!*p)
237       break;
238     p++;
239   }
240   return;
241
242 whine:
243   moan("bad slip interface list");
244 }
245
246 /* --- @t_create@ --- *
247  *
248  * Arguments:   @peer *p@ = pointer to peer block
249  *              @int fd@ = file descriptor of tunnel device (unused)
250  *              @char **ifn@ = where to put the interface name
251  *
252  * Returns:     A tunnel block if it worked, or null on failure.
253  *
254  * Use:         Initializes a new tunnel.
255  */
256
257 static tunnel *t_create(peer *p, int fd, char **ifn)
258 {
259   slipif *sl = 0;
260   int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
261   mdup_fd md[2];
262   pid_t kid = -1;
263   dstr d = DSTR_INIT;
264   unsigned char ch;
265   tunnel *t;
266   static const char end[] = { SL_END, SL_END };
267
268   /* --- Try to find a spare static interface --- */
269
270   for (sl = slipifs; sl; sl = sl->next) {
271     if (!(sl->f & F_INUSE)) {
272       T( trace(T_TUNNEL, "tun-slip: %s using static slipif %s",
273                p_name(p), sl->name); )
274       goto found;
275     }
276   }
277
278   /* --- If no dynamic interfaces are available, give up --- */
279
280   if (!slipcmd) {
281     a_warn("TUN", "-", "slip", "no-slip-interfaces", A_END);
282     goto fail;
283   }
284
285   /* --- Fork off a child process to create a dynamic SLIP interface --- */
286
287   if (pipe(pin) || pipe(pout)) {
288     a_warn("TUN", "-", "slip", "pipe-error", "?ERRNO", A_END);
289     goto fail;
290   }
291   if ((kid = fork()) < 0) {
292     a_warn("TUN", "-", "slip", "fork-error", "?ERRNO", A_END);
293     goto fail;
294   }
295   if (!kid) {
296     close(pin[1]); close(pout[0]);
297     md[0].cur = pin[0];  md[0].want = STDIN_FILENO;
298     md[1].cur = pout[0]; md[1].want = STDOUT_FILENO;
299     mdup(md, 2);
300     execlp(slipcmd, slipcmd, p_name(p), (char *)0);
301     _exit(127);
302   }
303
304   /* --- Read the interface name --- */
305
306   sl = CREATE(slipif);
307   close(pin[0]); pin[0] = -1;
308   close(pout[1]); pout[1] = -1;
309   for (;;) {
310     errno = EIO;
311     if (read(pout[0], &ch, 1) != 1 || ch == SL_END) {
312       a_warn("TUN", "-", "slip", "read-ifname-failed", "?ERRNO", A_END);
313       goto fail;
314     }
315     if (ch == '\n')
316       break;
317     DPUTC(&d, (char)ch);
318   }
319   DPUTZ(&d);
320   sl->name = xstrdup(d.buf);
321   sl->ifd = pout[0];
322   sl->ofd = pin[1];
323   sl->kid = kid;
324   sl->next = 0;
325   sl->f = F_DYNAMIC;
326   T( trace(T_TUNNEL, "tun-slip: %s using dynamic slipif %s",
327            p_name(p), sl->name); )
328   fdflags(pout[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
329   fdflags(pin[1], O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
330
331   /* --- Set up the new tunnel --- */
332
333 found:
334   t = CREATE(tunnel);
335   t->ops = &tun_slip;
336   t->p = p;
337   t->sl = sl;
338   t->st = 0;
339   t->n = 0;
340   sl->f |= F_INUSE;
341   sel_initfile(&sel, &t->f, sl->ifd, SEL_READ, t_read, t);
342   sel_addfile(&t->f);
343   write(sl->ofd, end, sizeof(end));
344   *ifn = xstrdup(sl->name);
345   dstr_destroy(&d);
346   return (t);
347
348   /* --- Tidy up after a failure --- */
349
350 fail:
351 #define CLOSE(fd) do if (fd != -1) close(fd); while (0)
352   CLOSE(pin[0]); CLOSE(pout[0]);
353   CLOSE(pin[1]); CLOSE(pout[1]);
354 #undef CLOSE
355   if (kid != -1) kill(kid, SIGTERM);
356   if (sl && (sl->f & F_DYNAMIC)) DESTROY(sl);
357   dstr_destroy(&d);
358   return (0);
359 }
360
361 /* --- @t_setifname@ --- *
362  *
363  * Arguments:   @tunnel *t@ = pointer to tunnel block
364  *              @const char *ifn@ = new interface name
365  *
366  * Returns:     ---
367  *
368  * Use:         Updates the interface name of a slip interface.
369  */
370
371 static void t_setifname(tunnel *t, const char *ifn)
372   { xfree(t->sl->name); t->sl->name = xstrdup(ifn); }
373
374 /* --- @t_inject@ --- *
375  *
376  * Arguments:   @tunnel *t@ = pointer to tunnel block
377  *              @buf *b@ = buffer to send
378  *
379  * Returns:     ---
380  *
381  * Use:         Injects a packet into the local network stack.
382  */
383
384 static void t_inject(tunnel *t, buf *b)
385 {
386   octet buf[PKBUFSZ * 2 + 2];
387   const octet *p, *l;
388   octet *q;
389
390   IF_TRACING(T_TUNNEL, {
391     trace(T_TUNNEL, "tun-slip: inject decrypted packet");
392     trace_block(T_PACKET, "tun-slip: packet contents", BBASE(b), BLEN(b));
393   })
394
395   q = buf;
396   *q++ = SL_END;
397   for (p = BBASE(b), l = BCUR(b); p < l; p++) {
398     switch (*p) {
399       case SL_END: *q++ = SL_ESC; *q++ = SL_ESCEND; break;
400       case SL_ESC: *q++ = SL_ESC; *q++ = SL_ESCESC; break;
401       default: *q++ = *p; break;
402     }
403   }
404   *q++ = SL_END;
405   IF_TRACING(T_TUNNEL, {
406     trace_block(T_PACKET, "tun-slip: SLIP-encapsulated contents",
407                 buf, q - buf);
408   })
409   write(t->sl->ofd, buf, q - buf);
410 }
411
412 /* --- @t_destroy@ --- *
413  *
414  * Arguments:   @tunnel *t@ = pointer to tunnel block
415  *
416  * Returns:     ---
417  *
418  * Use:         Destroys a tunnel.
419  */
420
421 static void t_destroy(tunnel *t)
422 {
423   slipif *sl = t->sl;
424
425   /* --- If it reported EOF, leave it out-of-action --- */
426
427   if (!(t->st & ST_EOF)) {
428     sel_rmfile(&t->f);
429     sl->f &= ~F_INUSE;
430   }
431   if (sl && (sl->f & F_DYNAMIC)) {
432     T( trace(T_TUNNEL, "tun-slip: releasing dynamic slipif %s", sl->name); )
433     close(sl->ofd);
434     close(sl->ifd);
435     kill(sl->kid, SIGTERM);
436     xfree(sl->name);
437     DESTROY(sl);
438   }
439   DESTROY(t);
440 }
441
442 const tunnel_ops tun_slip = {
443   "slip",
444   0,
445   t_init,
446   t_create,
447   t_setifname,
448   t_inject,
449   t_destroy
450 };
451
452 /*----- That's all, folks -------------------------------------------------*/