chiark / gitweb /
build: Distribute make-summary to fix distcheck.
[tripe] / tripe.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Main program
6  *
7  * (c) 2001 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Trivial IP Encryption (TrIPE).
13  *
14  * TrIPE is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * TrIPE is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with TrIPE; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Header files ------------------------------------------------------*/
30
31 #include "tripe.h"
32
33 /*----- Global variables --------------------------------------------------*/
34
35 sel_state sel;
36
37 /*----- Static variables --------------------------------------------------*/
38
39 static sel_timer it;
40 #define T_INTERVAL MIN(1)
41
42 /*----- Main code ---------------------------------------------------------*/
43
44 /* --- @interval@ --- *
45  *
46  * Arguments:   @struct timeval *tv@ = time when called
47  *              @void *v@ = boring pointer
48  *
49  * Returns:     ---
50  *
51  * Use:         Called periodically to do housekeeping tasks.
52  */
53
54 static void interval(struct timeval *tv, void *v)
55 {
56   struct timeval tvv;
57   T( trace(T_PEER, "peer: interval timer"); )
58   rand_seed(RAND_GLOBAL, MAXHASHSZ);
59   p_interval();
60   tvv = *tv;
61   tvv.tv_sec += T_INTERVAL;
62   sel_addtimer(&sel, &it, &tvv, interval, v);
63 }
64
65 /* --- @mystrieq@ --- *
66  *
67  * Arguments:   @const char *x, *y@ = two strings
68  *
69  * Returns:     True if @x@ and @y are equal, up to case.
70  */
71
72 int mystrieq(const char *x, const char *y)
73 {
74   for (;;) {
75     if (!*x && !*y) return (1);
76     if (tolower((unsigned char)*x) != tolower((unsigned char)*y))
77       return (0);
78     x++; y++;
79   }
80 }
81
82 /* --- @main@ --- *
83  *
84  * Arguments:   @int argc@ = number of command line arguments
85  *              @char *argv[]@ = vector of arguments
86  *
87  * Returns:     Zero if OK, nonzero on error.
88  *
89  * Use:         Main program.  Provides a simple VPN.
90  */
91
92 static void usage(FILE *fp)
93 {
94   pquis(fp, "Usage: $ [-D] [-d DIR] [-b ADDR] [-p PORT] [-n TUNNEL]\n\
95         [-U USER] [-G GROUP] [-a SOCKET] [-T TRACE-OPTS]\n\
96         [-k PRIV-KEYRING] [-K PUB-KEYRING] [-t KEY-TAG]\n");
97 }
98
99 static void version(FILE *fp)
100 {
101   pquis(fp, "$, version " VERSION "\n");
102 }
103
104 static void help(FILE *fp)
105 {
106   version(fp);
107   fputc('\n', fp);
108   usage(fp);
109   fputs("\n\
110 Options:\n\
111 \n\
112 -h, --help              Display this help text.\n\
113 -v, --version           Display version number.\n\
114 -u, --usage             Display pointless usage message.\n\
115     --tunnels           Display IP tunnel drivers and exit.\n\
116 \n\
117 -D, --daemon            Run in the background.\n\
118 -d, --directory=DIR     Switch to directory DIR [default " CONFIGDIR "].\n\
119 -b, --bind-address=ADDR Bind UDP socket to this IP ADDR.\n\
120 -p, --port=PORT         Select UDP port to listen to.\n\
121 -n, --tunnel=TUNNEL     Seelect default tunnel driver.\n\
122 -U, --setuid=USER       Set uid to USER after initialization.\n\
123 -G, --setgid=GROUP      Set gid to GROUP after initialization.\n\
124 -k, --priv-keyring=FILE Get private key from FILE.\n\
125 -K, --pub-keyring=FILE  Get public keys from FILE.\n\
126 -t, --tag=KEYTAG        Use private key labelled TAG.\n\
127 -a, --admin-socket=FILE Use FILE as the adminstration socket.\n\
128 " T( "\
129 -T, --trace=OPTIONS     Turn on tracing options.\n\
130 " ) "\
131 ", fp);
132 }
133
134 int main(int argc, char *argv[])
135 {
136   const char *kr_priv = "keyring", *kr_pub = "keyring.pub";
137   const char *tag_priv = "tripe-dh";
138   const char *csock = SOCKETDIR "/tripesock";
139   const char *dir = CONFIGDIR;
140   const char *p;
141   unsigned port = 0;
142   struct in_addr baddr = { INADDR_ANY };
143   unsigned f = 0;
144   int i;
145   int selerr = 0;
146   struct timeval tv;
147   uid_t u = -1;
148   gid_t g = -1;
149
150 #define f_bogus 1u
151 #define f_daemon 2u
152
153   ego(argv[0]);
154   T( trace_on(stderr, 0); )
155
156   if ((p = getenv("TRIPEDIR")) != 0)
157     dir = p;
158   tun_default = tunnels[0];
159
160   for (;;) {
161     static const struct option opts[] = {
162       { "help",         0,              0,      'h' },
163       { "version",      0,              0,      'v' },
164       { "usage",        0,              0,      'u' },
165       { "tunnels",      0,              0,      '0' },
166
167       { "daemon",       0,              0,      'D' },
168       { "uid",          OPTF_ARGREQ,    0,      'U' },
169       { "setuid",       OPTF_ARGREQ,    0,      'U' },
170       { "gid",          OPTF_ARGREQ,    0,      'G' },
171       { "setgid",       OPTF_ARGREQ,    0,      'G' },
172       { "bind-address", OPTF_ARGREQ,    0,      'b' },
173       { "tunnel",       OPTF_ARGREQ,    0,      'n' },
174       { "port",         OPTF_ARGREQ,    0,      'p' },
175       { "directory",    OPTF_ARGREQ,    0,      'd' },
176       { "priv-keyring", OPTF_ARGREQ,    0,      'k' },
177       { "pub-keyring",  OPTF_ARGREQ,    0,      'K' },
178       { "tag",          OPTF_ARGREQ,    0,      't' },
179       { "admin-socket", OPTF_ARGREQ,    0,      'a' },
180 #ifndef NTRACE
181       { "trace",        OPTF_ARGREQ,    0,      'T' },
182 #endif
183
184       { 0,              0,              0,      0 }
185     };
186
187     i = mdwopt(argc, argv, "hvuDU:G:b:p:d:k:K:t:a:" T("T:"),
188                opts, 0, 0, 0);
189     if (i < 0)
190       break;
191     switch (i) {
192       case 'h':
193         help(stdout);
194         exit(0);
195       case 'v':
196         version(stdout);
197         exit(0);
198       case 'u':
199         usage(stdout);
200         exit(0);
201
202       case 'D':
203         f |= f_daemon;
204         break;
205       case 'U': {
206         struct passwd *pw;
207         char *p;
208         unsigned long i = strtoul(optarg, &p, 0);
209         if (!*p)
210           pw = getpwuid(i);
211         else
212           pw = getpwnam(optarg);
213         if (!pw)
214           die(EXIT_FAILURE, "user `%s' not found", optarg);
215         u = pw->pw_uid;
216         if (g == -1)
217           g = pw->pw_gid;
218       } break;
219       case 'G': {
220         struct group *gr;
221         char *p;
222         unsigned long i = strtoul(optarg, &p, 0);
223         if (!*p)
224           gr = getgrgid(i);
225         else
226           gr = getgrnam(optarg);
227         if (!gr)
228           die(EXIT_FAILURE, "group `%s' not found", optarg);
229         g = gr->gr_gid;
230       } break;
231
232       case 'b': {
233         struct hostent *h = gethostbyname(optarg);
234         if (!h)
235           die(EXIT_FAILURE, "unknown host name `%s'", optarg);
236         memcpy(&baddr, h->h_addr, sizeof(struct in_addr));
237       } break;
238       case 'p': {
239         char *p;
240         unsigned long i = strtoul(optarg, &p, 0);
241         if (*p) {
242           struct servent *s = getservbyname(optarg, "udp");
243           if (!s)
244             die(EXIT_FAILURE, "unknown service name `%s'", optarg);
245           i = ntohs(s->s_port);
246         }
247         if (i == 0 || i >= 65536)
248           die(EXIT_FAILURE, "bad port number %lu", i);
249         port = i;
250       } break;
251       case 'n': {
252         int i;
253         for (i = 0;; i++) {
254           if (!tunnels[i])
255             die(EXIT_FAILURE, "unknown tunnel `%s'", optarg);
256           if (mystrieq(optarg, tunnels[i]->name))
257             break;
258         }
259         tun_default = tunnels[i];
260       } break;
261       case 'd':
262         dir = optarg;
263         break;
264       case 'k':
265         kr_priv = optarg;
266         break;
267       case 'K':
268         kr_pub = optarg;
269         break;
270       case 'a':
271         csock = optarg;
272         break;
273       case 't':
274         tag_priv = optarg;
275         break;
276 #ifndef NTRACE
277       case 'T':
278         tr_flags = traceopt(tr_opts, optarg, tr_flags, 0);
279         trace_level(tr_flags);
280         break;
281 #endif
282       case '0': {
283         int i;
284         for (i = 0; tunnels[i]; i++)
285           puts(tunnels[i]->name);
286         exit(0);
287       } break;
288       default:
289         f |= f_bogus;
290         break;
291     }
292   }
293
294   if (optind < argc || (f & f_bogus)) {
295     usage(stderr);
296     exit(EXIT_FAILURE);
297   }
298
299   if (chdir(dir)) {
300     die(EXIT_FAILURE, "can't set current directory to `%s': %s",
301         dir, strerror(errno));
302   }
303
304   sel_init(&sel);
305   sig_init(&sel);
306   rand_noisesrc(RAND_GLOBAL, &noise_source);
307   rand_seed(RAND_GLOBAL, MAXHASHSZ);
308   signal(SIGPIPE, SIG_IGN);
309   for (i = 0; tunnels[i]; i++)
310     tunnels[i]->init();
311   p_init(baddr, port);
312   if (!(f & f_daemon)) {
313 #ifndef NTRACE
314     a_create(STDIN_FILENO, STDOUT_FILENO, AF_TRACE | AF_WARN);
315 #else
316     a_create(STDIN_FILENO, STDOUT_FILENO, AF_WARN);
317 #endif
318   }
319   if (g != (gid_t)-1) {
320     if (setgid(g) || (getuid() == 0 && setgroups(1, &g))) {
321       die(EXIT_FAILURE, "couldn't setgid to %u: %s",
322           (unsigned)g, strerror(errno));
323     }
324   }
325   if (u != (uid_t)-1) {
326     if (setuid(u)) {
327       die(EXIT_FAILURE, "couldn't setuid to %u: %s",
328           (unsigned)u, strerror(errno));
329     }
330   }
331   km_init(kr_priv, kr_pub, tag_priv);
332   a_init(csock);
333   if (f & f_daemon) {
334     if (u_daemon())
335       die(EXIT_FAILURE, "couldn't become a daemon: %s", strerror(errno));
336     a_daemon();
337   }
338
339   tv.tv_sec = time(0) + T_INTERVAL;
340   tv.tv_usec = 0;
341   sel_addtimer(&sel, &it, &tv, interval, 0);
342
343   for (;;) {
344     if (!sel_select(&sel))
345       selerr = 0;
346     else if (errno != EINTR && errno != EAGAIN) {
347       a_warn("SERVER", "select-error", "?ERRNO", A_END);
348       selerr++;
349       if (selerr > 8) {
350         a_warn("ABORT", "repeated-select-errors", A_END);
351         abort();
352       }
353     }
354   }
355
356   return (0);
357 }
358
359 /*----- That's all, folks -------------------------------------------------*/