git ssb

0+

cel / sslh



Tree: c02e2d7aeeba25cb53d8a81acad55318ce63a4c4

Files: c02e2d7aeeba25cb53d8a81acad55318ce63a4c4 / sslh-main.c

15505 bytesRaw
1/*
2# main: processing of config file, command line options and start the main
3# loop.
4#
5# Copyright (C) 2007-2012 Yves Rutschle
6#
7# This program is free software; you can redistribute it
8# and/or modify it under the terms of the GNU General Public
9# License as published by the Free Software Foundation; either
10# version 2 of the License, or (at your option) any later
11# version.
12#
13# This program is distributed in the hope that it will be
14# useful, but WITHOUT ANY WARRANTY; without even the implied
15# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16# PURPOSE. See the GNU General Public License for more
17# details.
18#
19# The full text for the General Public License is here:
20# http://www.gnu.org/licenses/gpl.html
21
22*/
23
24#define _GNU_SOURCE
25#ifdef LIBCONFIG
26#include <libconfig.h>
27#endif
28#include <regex.h>
29
30#include "common.h"
31#include "probe.h"
32
33const char* USAGE_STRING =
34"sslh " VERSION "\n" \
35"usage:\n" \
36"\tsslh [-v] [-i] [-V] [-f] [-n] [-F <file>]\n"
37"\t[-t <timeout>] [-P <pidfile>] -u <username> -p <add> [-p <addr> ...] \n" \
38"%s\n\n" /* Dynamically built list of builtin protocols */ \
39"\t[--on-timeout <addr>]\n" \
40"-v: verbose\n" \
41"-V: version\n" \
42"-f: foreground\n" \
43"-n: numeric output\n" \
44"-F: use configuration file\n" \
45"--on-timeout: connect to specified address upon timeout (default: ssh address)\n" \
46"-t: seconds to wait before connecting to --on-timeout address.\n" \
47"-p: address and port to listen on.\n Can be used several times to bind to several addresses.\n" \
48"--[ssh,ssl,...]: where to connect connections from corresponding protocol.\n" \
49"-F: specify a configuration file\n" \
50"-P: PID file.\n" \
51"-i: Run as a inetd service.\n" \
52"";
53
54/* Constants for options that have no one-character shorthand */
55#define OPT_ONTIMEOUT 257
56
57static struct option const_options[] = {
58 { "inetd", no_argument, &inetd, 1 },
59 { "foreground", no_argument, &foreground, 1 },
60 { "background", no_argument, &background, 1 },
61 { "transparent", no_argument, &transparent, 1 },
62 { "numeric", no_argument, &numeric, 1 },
63 { "verbose", no_argument, &verbose, 1 },
64 { "user", required_argument, 0, 'u' },
65 { "config", required_argument, 0, 'F' },
66 { "pidfile", required_argument, 0, 'P' },
67 { "timeout", required_argument, 0, 't' },
68 { "on-timeout", required_argument, 0, OPT_ONTIMEOUT },
69 { "listen", required_argument, 0, 'p' },
70 {}
71};
72static struct option* all_options;
73static struct proto* builtins;
74static const char *optstr = "vt:T:p:VP:F:";
75
76
77
78static void print_usage(void)
79{
80 struct proto *p;
81 int i;
82 char *prots = "";
83
84 p = get_builtins();
85 for (i = 0; i < get_num_builtins(); i++)
86 asprintf(&prots, "%s\t[--%s <addr>]\n", prots, p[i].description);
87
88 fprintf(stderr, USAGE_STRING, prots);
89}
90
91static void printsettings(void)
92{
93 char buf[NI_MAXHOST];
94 struct addrinfo *a;
95 struct proto *p;
96
97 for (p = get_first_protocol(); p; p = p->next) {
98 fprintf(stderr,
99 "%s addr: %s. libwrap service: %s family %d %d\n",
100 p->description,
101 sprintaddr(buf, sizeof(buf), p->saddr),
102 p->service,
103 p->saddr->ai_family,
104 p->saddr->ai_addr->sa_family);
105 }
106 fprintf(stderr, "listening on:\n");
107 for (a = addr_listen; a; a = a->ai_next) {
108 fprintf(stderr, "\t%s\n", sprintaddr(buf, sizeof(buf), a));
109 }
110 fprintf(stderr, "timeout: %d\non-timeout: %s\n", probing_timeout,
111 timeout_protocol()->description);
112}
113
114
115/* Extract configuration on addresses and ports on which to listen.
116 * out: newly allocated list of addrinfo to listen to
117 */
118#ifdef LIBCONFIG
119static int config_listen(config_t *config, struct addrinfo **listen)
120{
121 config_setting_t *setting, *addr;
122 int len, i;
123 const char *hostname, *port;
124
125 setting = config_lookup(config, "listen");
126 if (setting) {
127 len = config_setting_length(setting);
128 for (i = 0; i < len; i++) {
129 addr = config_setting_get_elem(setting, i);
130 if (! (config_setting_lookup_string(addr, "host", &hostname) &&
131 config_setting_lookup_string(addr, "port", &port))) {
132 fprintf(stderr,
133 "line %d:Incomplete specification (hostname and port required)\n",
134 config_setting_source_line(addr));
135 return -1;
136 }
137
138 resolve_split_name(listen, hostname, port);
139
140 /* getaddrinfo returned a list of addresses corresponding to the
141 * specification; move the pointer to the end of that list before
142 * processing the next specification */
143 for (; *listen; listen = &((*listen)->ai_next));
144 }
145 }
146
147 return 0;
148}
149#endif
150
151
152
153#ifdef LIBCONFIG
154static void setup_regex_probe(struct proto *p, config_setting_t* probes)
155{
156 int num_probes, errsize, i, res;
157 char *err;
158 const char * expr;
159 regex_t** probe_list;
160
161 num_probes = config_setting_length(probes);
162 if (!num_probes) {
163 fprintf(stderr, "%s: no probes specified\n", p->description);
164 exit(1);
165 }
166
167 p->probe = get_probe("regex");
168 probe_list = calloc(num_probes + 1, sizeof(*probe_list));
169 p->data = (void*)probe_list;
170
171 for (i = 0; i < num_probes; i++) {
172 probe_list[i] = malloc(sizeof(*(probe_list[i])));
173 expr = config_setting_get_string_elem(probes, i);
174 res = regcomp(probe_list[i], expr, 0);
175 if (res) {
176 err = malloc(errsize = regerror(res, probe_list[i], NULL, 0));
177 regerror(res, probe_list[i], err, errsize);
178 fprintf(stderr, "%s:%s\n", expr, err);
179 free(err);
180 exit(1);
181 }
182 }
183}
184#endif
185
186/* Extract configuration for protocols to connect to.
187 * out: newly-allocated list of protocols
188 */
189#ifdef LIBCONFIG
190static int config_protocols(config_t *config, struct proto **prots)
191{
192 config_setting_t *setting, *prot, *probes;
193 const char *hostname, *port, *name;
194 int i, num_prots;
195 struct proto *p, *prev = NULL;
196
197 setting = config_lookup(config, "protocols");
198 if (setting) {
199 num_prots = config_setting_length(setting);
200 for (i = 0; i < num_prots; i++) {
201 p = calloc(1, sizeof(*p));
202 if (i == 0) *prots = p;
203 if (prev) prev->next = p;
204 prev = p;
205
206 prot = config_setting_get_elem(setting, i);
207 if ((config_setting_lookup_string(prot, "name", &name) &&
208 config_setting_lookup_string(prot, "host", &hostname) &&
209 config_setting_lookup_string(prot, "port", &port)
210 )) {
211 p->description = name;
212 config_setting_lookup_string(prot, "service", &(p->service));
213
214 resolve_split_name(&(p->saddr), hostname, port);
215
216
217 probes = config_setting_get_member(prot, "probe");
218 if (probes) {
219 if (config_setting_is_array(probes)) {
220 /* If 'probe' is an array, setup a regex probe using the
221 * array of strings as pattern */
222
223 setup_regex_probe(p, probes);
224
225 } else {
226 /* if 'probe' is 'builtin', set the probe to the
227 * appropriate builtin protocol */
228 if (!strcmp(config_setting_get_string(probes), "builtin")) {
229 p->probe = get_probe(name);
230 if (!p->probe) {
231 fprintf(stderr, "%s: no builtin probe for this protocol\n", name);
232 exit(1);
233 }
234 } else {
235 fprintf(stderr, "%s: illegal probe name\n", name);
236 exit(1);
237 }
238 }
239 }
240 }
241 }
242 }
243
244 return 0;
245}
246#endif
247
248/* Parses a config file
249 * in: *filename
250 * out: *listen, a newly-allocated linked list of listen addrinfo
251 * *prots, a newly-allocated linked list of protocols
252 */
253#ifdef LIBCONFIG
254static int config_parse(char *filename, struct addrinfo **listen, struct proto **prots)
255{
256 config_t config;
257 long int timeout;
258 const char* str;
259
260 config_init(&config);
261 if (config_read_file(&config, filename) == CONFIG_FALSE) {
262 fprintf(stderr, "%s:%d:%s\n",
263 filename,
264 config_error_line(&config),
265 config_error_text(&config));
266 exit(1);
267 }
268
269 config_lookup_bool(&config, "verbose", &verbose);
270 config_lookup_bool(&config, "inetd", &inetd);
271 config_lookup_bool(&config, "foreground", &foreground);
272 config_lookup_bool(&config, "numeric", &numeric);
273
274 if (config_lookup_int(&config, "timeout", &timeout) == CONFIG_TRUE) {
275 probing_timeout = timeout;
276 }
277
278 if (config_lookup_string(&config, "on-timeout", &str)) {
279 set_ontimeout(str);
280 }
281
282 config_lookup_string(&config, "user", &user_name);
283 config_lookup_string(&config, "pidfile", &pid_file);
284
285 config_listen(&config, listen);
286 config_protocols(&config, prots);
287
288 return 0;
289}
290#endif
291
292/* Adds protocols to the list of options, so command-line parsing uses the
293 * protocol definition array
294 * options: array of options to add to; must be big enough
295 * n_opts: number of options in *options before calling (i.e. where to append)
296 * prot: array of protocols
297 * n_prots: number of protocols in *prot
298 * */
299static void append_protocols(struct option *options, int n_opts, struct proto *prot , int n_prots)
300{
301 int o, p;
302
303 for (o = n_opts, p = 0; p < n_prots; o++, p++) {
304 options[o].name = prot[p].description;
305 options[o].has_arg = required_argument;
306 options[o].flag = 0;
307 options[o].val = p + PROT_SHIFT;
308 }
309}
310
311static void make_alloptions(void)
312{
313 builtins = get_builtins();
314
315 /* Create all_options, composed of const_options followed by one option per
316 * known protocol */
317 all_options = calloc(ARRAY_SIZE(const_options) + get_num_builtins(), sizeof(struct option));
318 memcpy(all_options, const_options, sizeof(const_options));
319 append_protocols(all_options, ARRAY_SIZE(const_options) - 1, builtins, get_num_builtins());
320}
321
322/* Performs a first scan of command line options to see if a configuration file
323 * is specified. If there is one, parse it now before all other options (so
324 * configuration file settings can be overridden from the command line).
325 *
326 * prots: newly-allocated list of configured protocols, if any.
327 */
328static void cmdline_config(int argc, char* argv[], struct proto** prots)
329{
330#ifdef LIBCONFIG
331 int c, res;
332 char *config_filename;
333#endif
334
335 make_alloptions();
336
337#ifdef LIBCONFIG
338 optind = 1;
339 opterr = 0; /* we're missing protocol options at this stage so don't output errors */
340 while ((c = getopt_long_only(argc, argv, optstr, all_options, NULL)) != -1) {
341 if (c == 'F') {
342 config_filename = optarg;
343 /* find the end of the listen list */
344 res = config_parse(config_filename, &addr_listen, prots);
345 if (res)
346 exit(4);
347 break;
348 }
349 }
350#endif
351}
352
353
354/* Parse command-line options. prots points to a list of configured protocols,
355 * potentially non-allocated */
356static void parse_cmdline(int argc, char* argv[], struct proto* prots)
357{
358 int c;
359 struct addrinfo **a;
360 struct proto *p;
361
362 optind = 1;
363 opterr = 1;
364next_arg:
365 while ((c = getopt_long_only(argc, argv, optstr, all_options, NULL)) != -1) {
366 if (c == 0) continue;
367
368 if (c >= PROT_SHIFT) {
369 if (prots)
370 for (p = prots; p && p->next; p = p->next) {
371 /* override if protocol was already defined by config file
372 * (note it only overrides address and use builtin probe) */
373 if (!strcmp(p->description, builtins[c-PROT_SHIFT].description)) {
374 resolve_name(&(p->saddr), optarg);
375 p->probe = builtins[c-PROT_SHIFT].probe;
376 goto next_arg;
377 }
378 }
379 /* At this stage, it's a new protocol: add it to the end of the
380 * list */
381 if (!prots) {
382 /* No protocols yet -- create the list */
383 p = prots = calloc(1, sizeof(*p));
384 } else {
385 p->next = calloc(1, sizeof(*p));
386 p = p->next;
387 }
388 memcpy(p, &builtins[c-PROT_SHIFT], sizeof(*p));
389 resolve_name(&(p->saddr), optarg);
390 continue;
391 }
392
393 switch (c) {
394
395 case 'F':
396 /* Legal option, but do nothing, it was already processed in
397 * cmdline_config() */
398#ifndef LIBCONFIG
399 fprintf(stderr, "Built without libconfig support: configuration file not available.\n");
400 exit(1);
401#endif
402 break;
403
404 case 't':
405 probing_timeout = atoi(optarg);
406 break;
407
408 case OPT_ONTIMEOUT:
409 set_ontimeout(optarg);
410 break;
411
412 case 'p':
413 /* find the end of the listen list */
414 for (a = &addr_listen; *a; a = &((*a)->ai_next));
415 /* append the specified addresses */
416 resolve_name(a, optarg);
417
418 break;
419
420 case 'V':
421 printf("%s %s\n", server_type, VERSION);
422 exit(0);
423
424 case 'u':
425 user_name = optarg;
426 break;
427
428 case 'P':
429 pid_file = optarg;
430 break;
431
432 case 'v':
433 verbose++;
434 break;
435
436 default:
437 print_usage();
438 exit(2);
439 }
440 }
441
442 if (!prots) {
443 fprintf(stderr, "At least one target protocol must be specified.\n");
444 exit(2);
445 }
446
447 set_protocol_list(prots);
448
449 if (!addr_listen) {
450 fprintf(stderr, "No listening address specified; use at least one -p option\n");
451 exit(1);
452 }
453
454 /* Did command-line override foreground setting? */
455 if (background)
456 foreground = 0;
457
458}
459
460int main(int argc, char *argv[])
461{
462
463 extern char *optarg;
464 extern int optind;
465 int res, num_addr_listen;
466 struct proto* protocols = NULL;
467
468 int *listen_sockets;
469
470 /* Init defaults */
471 pid_file = NULL;
472 user_name = NULL;
473
474 cmdline_config(argc, argv, &protocols);
475 parse_cmdline(argc, argv, protocols);
476
477 if (inetd)
478 {
479 verbose = 0;
480 start_shoveler(0);
481 exit(0);
482 }
483
484 if (verbose)
485 printsettings();
486
487 num_addr_listen = start_listen_sockets(&listen_sockets, addr_listen);
488
489 if (!foreground) {
490 if (fork() > 0) exit(0); /* Detach */
491
492 /* New session -- become group leader */
493 if (getuid() == 0) {
494 res = setsid();
495 CHECK_RES_DIE(res, "setsid: already process leader");
496 }
497 }
498
499 setup_signals();
500
501 if (pid_file)
502 write_pid_file(pid_file);
503
504 if (user_name)
505 drop_privileges(user_name);
506
507 /* Open syslog connection */
508 setup_syslog(argv[0]);
509
510 main_loop(listen_sockets, num_addr_listen);
511
512 return 0;
513}
514

Built with git-ssb-web