chiark / gitweb /
tripe.h: Make job index be unsigned int, not unsigned short.
[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 #define SL_END 0xc0
66 #define SL_ESC 0xdb
67 #define SL_ESCEND 0xdc
68 #define SL_ESCESC 0xdd
69
70 /* --- @t_read@ --- *
71  *
72  * Arguments:   @int fd@ = file descriptor to read
73  *              @unsigned mode@ = what's happened
74  *              @void *v@ = pointer to tunnel block
75  *
76  * Returns:     ---
77  *
78  * Use:         Reads data from the tunnel.
79  */
80
81 static void t_read(int fd, unsigned mode, void *v)
82 {
83   tunnel *t = v;
84   ssize_t n;
85   const octet *p, *l, *ll;
86   octet *q;
87   unsigned st;
88   octet o;
89   buf b;
90
91   /* --- Read the input data --- */
92
93   n = read(fd, buf_t, sizeof(buf_t));
94   if (n < 0) {
95     if (errno == EINTR ||
96 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
97         errno == EWOULDBLOCK ||
98 #endif
99         errno == EAGAIN)
100       return;
101     a_warn("TUN", "%s", p_ifname(t->p), "read-error", "?ERRNO", A_END);
102     return;
103   }
104   if (!n) {
105     a_warn("TUN", "%s", p_ifname(t->p), "slip", "eof", A_END);
106     t->st = ST_EOF;
107     sel_rmfile(&t->f);
108     return;
109   }
110   IF_TRACING(T_TUNNEL, {
111     trace_block(T_PACKET, "tun-slip: SLIP-encapsulated data",
112                 buf_t, n);
113   })
114
115   /* --- Decapsulate the packet --- */
116
117   for (p = buf_t, l = p + n, st = t->st,
118        q = t->buf + t->n, ll = t->buf + sizeof(t->buf);
119        p < l;
120        p++) {
121     o = *p;
122     switch (o) {
123       case SL_END:
124         if (st & ST_BAD)
125           ;
126         else if (st & ST_ESC)
127           a_warn("TUN", "%s", p_ifname(t->p), "slip", "escape-end", A_END);
128         else if (q == t->buf) {
129           T( trace(T_TUNNEL, "tun-slip: empty packet"); )
130         } else {
131           IF_TRACING(T_TUNNEL, {
132             trace(T_TUNNEL, "tun-slip: packet arrived");
133             trace_block(T_PACKET, "tun-slip: packet contents",
134                         t->buf, q - t->buf);
135           })
136           buf_init(&b, t->buf, q - t->buf);
137           p_tun(t->p, &b);
138         }
139         q = t->buf;
140         st &= ~(ST_ESC | ST_BAD);
141         break;
142       case SL_ESC:
143         if ((st & ST_ESC) && !(st & ST_BAD)) {
144           a_warn("TUN", "%s", p_ifname(t->p), "slip", "bad-escape", A_END);
145           st |= ST_BAD;
146         } else
147           st |= ST_ESC;
148         break;
149       case SL_ESCEND:
150         if (st & ST_ESC)
151           o = SL_END;
152         goto emit;
153       case SL_ESCESC:
154         if (st & ST_ESC)
155           o = SL_ESC;
156         goto emit;
157       default:
158         if ((st & ST_ESC) && !(st & ST_BAD)) {
159           a_warn("TUN", "%s", p_ifname(t->p), "slip", "bad-escape", A_END);
160           st |= ST_BAD;
161         }
162       emit:
163         if (!(st & ST_BAD)) {
164           if (q < ll)
165             *q++ = o;
166           else {
167             a_warn("TUN", "%s", p_ifname(t->p), "slip", "overflow", A_END);
168             st |= ST_BAD;
169           }
170         }
171         st &= ~ST_ESC;
172         break;
173     }
174   }
175
176   t->n = q - t->buf;
177   t->st = st;
178 }
179
180 /* --- @t_init@ --- *
181  *
182  * Arguments:   ---
183  *
184  * Returns:     ---
185  *
186  * Use:         Initializes the tunneling system.  Maybe this will require
187  *              opening file descriptors or something.
188  */
189
190 static void t_init(void)
191 {
192   char *p, *q;
193   dstr d = DSTR_INIT;
194   slipif *sl, **tail = &slipifs;
195   unsigned long uli, ulo;
196   size_t n;
197
198   if ((p = getenv("TRIPE_SLIPIF")) == 0)
199     return;
200
201   /* --- Build the list of available interfaces --- */
202
203   dstr_puts(&d, p);
204
205   p = d.buf;
206   for (;;) {
207     if (*p == '/' || *p == '.') {
208       slipcmd = p;
209       T( trace(T_TUNNEL, "tun-slip: declared slip command `%s'", slipcmd); )
210       break;
211     }
212     uli = strtoul(p, &q, 0);
213     if (uli > INT_MAX || q == p)
214       goto whine;
215     if (*q != ',')
216       ulo = uli;
217     else {
218       p = q + 1;
219       ulo = strtoul(p, &q, 0);
220       if (ulo > INT_MAX || q == p)
221         goto whine;
222     }
223     if (*q != '=' || (n = strcspn(q + 1, ":")) == 0)
224       goto whine;
225     sl = CREATE(slipif);
226     sl->next = 0;
227     sl->ifd = uli;
228     fdflags(sl->ifd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
229     fdflags(sl->ofd, O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
230     sl->ofd = ulo;
231     sl->name = xmalloc(n + 1);
232     sl->kid = -1;
233     sl->f = 0;
234     memcpy(sl->name, q + 1, n);
235     sl->name[n] = 0;
236     *tail = sl;
237     tail = &sl->next;
238     T( trace(T_TUNNEL, "tun-slip: declared slipif %d,%d=%s",
239              sl->ifd, sl->ofd, sl->name); )
240     p = q + n + 1;
241     if (!*p)
242       break;
243     p++;
244   }
245   return;
246
247 whine:
248   moan("bad slip interface list");
249 }
250
251 /* --- @t_create@ --- *
252  *
253  * Arguments:   @peer *p@ = pointer to peer block
254  *              @char **ifn@ = where to put the interface name
255  *
256  * Returns:     A tunnel block if it worked, or null on failure.
257  *
258  * Use:         Initializes a new tunnel.
259  */
260
261 static tunnel *t_create(peer *p, char **ifn)
262 {
263   slipif *sl = 0;
264   int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
265   pid_t kid = -1;
266   dstr d = DSTR_INIT;
267   unsigned char ch;
268   tunnel *t;
269   static const char end[] = { SL_END, SL_END };
270
271   /* --- Try to find a spare static interface --- */
272
273   for (sl = slipifs; sl; sl = sl->next) {
274     if (!(sl->f & F_INUSE)) {
275       T( trace(T_TUNNEL, "tun-slip: %s using static slipif %s",
276                p_name(p), sl->name); )
277       goto found;
278     }
279   }
280
281   /* --- If no dynamic interfaces are available, give up --- */
282
283   if (!slipcmd) {
284     a_warn("TUN", "-", "slip", "no-slip-interfaces", A_END);
285     goto fail;
286   }
287
288   /* --- Fork off a child process to create a dynamic SLIP interface --- */
289
290   if (pipe(pin) || pipe(pout)) {
291     a_warn("TUN", "-", "slip", "pipe-error", "?ERRNO", A_END);
292     goto fail;
293   }
294   if ((kid = fork()) < 0) {
295     a_warn("TUN", "-", "slip", "fork-error", "?ERRNO", A_END);
296     goto fail;
297   }
298   if (!kid) {
299     close(pin[1]);
300     close(pout[0]);
301     dup2(pin[0], STDIN_FILENO);
302     dup2(pout[1], STDOUT_FILENO);
303     execlp(slipcmd, slipcmd, p_name(p), (char *)0);
304     _exit(127);
305   }
306
307   /* --- Read the interface name --- */
308
309   sl = CREATE(slipif);
310   close(pin[0]); pin[0] = -1;
311   close(pout[1]); pout[1] = -1;
312   for (;;) {
313     errno = EIO;
314     if (read(pout[0], &ch, 1) != 1 || ch == SL_END) {
315       a_warn("TUN", "-", "slip", "read-ifname-failed", "?ERRNO", A_END);
316       goto fail;
317     }
318     if (ch == '\n')
319       break;
320     DPUTC(&d, (char)ch);
321   }
322   DPUTZ(&d);
323   sl->name = xstrdup(d.buf);
324   sl->ifd = pout[0];
325   sl->ofd = pin[1];
326   sl->kid = kid;
327   sl->next = 0;
328   sl->f = F_DYNAMIC;
329   T( trace(T_TUNNEL, "tun-slip: %s using dynamic slipif %s",
330            p_name(p), sl->name); )
331   fdflags(pout[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
332   fdflags(pin[1], O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
333
334   /* --- Set up the new tunnel --- */
335
336 found:
337   t = CREATE(tunnel);
338   t->ops = &tun_slip;
339   t->p = p;
340   t->sl = sl;
341   t->st = 0;
342   t->n = 0;
343   sl->f |= F_INUSE;
344   sel_initfile(&sel, &t->f, sl->ifd, SEL_READ, t_read, t);
345   sel_addfile(&t->f);
346   write(sl->ofd, end, sizeof(end));
347   *ifn = xstrdup(sl->name);
348   dstr_destroy(&d);
349   return (t);
350
351   /* --- Tidy up after a failure --- */
352
353 fail:
354 #define CLOSE(fd) do if (fd != -1) close(fd); while (0)
355   CLOSE(pin[0]); CLOSE(pout[0]);
356   CLOSE(pin[1]); CLOSE(pout[1]);
357 #undef CLOSE
358   if (kid != -1) kill(kid, SIGTERM);
359   if (sl && (sl->f & F_DYNAMIC)) DESTROY(sl);
360   dstr_destroy(&d);
361   return (0);
362 }
363
364 /* --- @t_setifname@ --- *
365  *
366  * Arguments:   @tunnel *t@ = pointer to tunnel block
367  *              @const char *ifn@ = new interface name
368  *
369  * Returns:     ---
370  *
371  * Use:         Updates the interface name of a slip interface.
372  */
373
374 static void t_setifname(tunnel *t, const char *ifn)
375   { xfree(t->sl->name); t->sl->name = xstrdup(ifn); }
376
377 /* --- @t_inject@ --- *
378  *
379  * Arguments:   @tunnel *t@ = pointer to tunnel block
380  *              @buf *b@ = buffer to send
381  *
382  * Returns:     ---
383  *
384  * Use:         Injects a packet into the local network stack.
385  */
386
387 static void t_inject(tunnel *t, buf *b)
388 {
389   octet buf[PKBUFSZ * 2 + 2];
390   const octet *p, *l;
391   octet *q;
392
393   IF_TRACING(T_TUNNEL, {
394     trace(T_TUNNEL, "tun-slip: inject decrypted packet");
395     trace_block(T_PACKET, "tun-slip: packet contents", BBASE(b), BLEN(b));
396   })
397
398   q = buf;
399   *q++ = SL_END;
400   for (p = BBASE(b), l = BCUR(b); p < l; p++) {
401     switch (*p) {
402       case SL_END: *q++ = SL_ESC; *q++ = SL_ESCEND; break;
403       case SL_ESC: *q++ = SL_ESC; *q++ = SL_ESCESC; break;
404       default: *q++ = *p; break;
405     }
406   }
407   *q++ = SL_END;
408   IF_TRACING(T_TUNNEL, {
409     trace_block(T_PACKET, "tun-slip: SLIP-encapsulated contents",
410                 buf, q - buf);
411   })
412   write(t->sl->ofd, buf, q - buf);
413 }
414
415 /* --- @t_destroy@ --- *
416  *
417  * Arguments:   @tunnel *t@ = pointer to tunnel block
418  *
419  * Returns:     ---
420  *
421  * Use:         Destroys a tunnel.
422  */
423
424 static void t_destroy(tunnel *t)
425 {
426   slipif *sl = t->sl;
427
428   /* --- If it reported EOF, leave it out-of-action --- */
429
430   if (!(t->st & ST_EOF)) {
431     sel_rmfile(&t->f);
432     sl->f &= ~F_INUSE;
433   }
434   if (sl && (sl->f & F_DYNAMIC)) {
435     T( trace(T_TUNNEL, "tun-slip: releasing dynamic slipif %s", sl->name); )
436     close(sl->ofd);
437     close(sl->ifd);
438     kill(sl->kid, SIGTERM);
439     xfree(sl->name);
440     DESTROY(sl);
441   }
442   DESTROY(t);
443 }
444
445 const tunnel_ops tun_slip = {
446   "slip",
447   t_init,
448   t_create,
449   t_setifname,
450   t_inject,
451   t_destroy
452 };
453
454 /*----- That's all, folks -------------------------------------------------*/