1 #include <math.h>
2 #include <signal.h>
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 
8 #include "asprintf.h"
9 #include "daemonize.h"
10 #include "events.h"
11 #include "getopt.h"
12 #include "graceful_shutdown.h"
13 #include "parsenum.h"
14 #include "setuidgid.h"
15 #include "sock.h"
16 #include "warnp.h"
17 
18 #include "dispatch.h"
19 #include "proto_crypt.h"
20 
21 static void
usage(void)22 usage(void)
23 {
24 
25 	fprintf(stderr,
26 	    "usage: spiped {-e | -d} -s <source socket> "
27 	    "-t <target socket> -k <key file>\n"
28 	    "    [-DFj] [-f | -g] [-n <max # connections>] "
29 	    "[-o <connection timeout>]\n"
30 	    "    [-p <pidfile>] [-r <rtime> | -R] [--syslog]\n"
31 	    "    [-u {<username> | <:groupname> | <username:groupname>}]\n"
32 	    "       spiped -v\n");
33 	exit(1);
34 }
35 
36 static int
callback_graceful_shutdown(void * dispatch_cookie)37 callback_graceful_shutdown(void * dispatch_cookie)
38 {
39 
40 	dispatch_request_shutdown(dispatch_cookie);
41 
42 	/* Success! */
43 	return (0);
44 }
45 
46 /*
47  * Signal handler for SIGINT to perform a hard shutdown.
48  */
49 static void
diediedie_handler(int signo)50 diediedie_handler(int signo)
51 {
52 
53 	(void)signo; /* UNUSED */
54 	_exit(0);
55 }
56 
57 /* Simplify error-handling in command-line parse loop. */
58 #define OPT_EPARSE(opt, arg) do {					\
59 	warnp("Error parsing argument: %s %s", opt, arg);		\
60 	exit(1);							\
61 } while (0)
62 
63 int
main(int argc,char * argv[])64 main(int argc, char * argv[])
65 {
66 	/* Command-line parameters. */
67 	int opt_d = 0;
68 	int opt_D = 0;
69 	int opt_e = 0;
70 	int opt_f = 0;
71 	int opt_g = 0;
72 	int opt_F = 0;
73 	int opt_j = 0;
74 	const char * opt_k = NULL;
75 	int opt_n_set = 0;
76 	size_t opt_n = 0;
77 	int opt_o_set = 0;
78 	double opt_o = 0.0;
79 	const char * opt_p = NULL;
80 	int opt_r_set = 0;
81 	double opt_r = 0.0;
82 	int opt_R = 0;
83 	int opt_syslog = 0;
84 	const char * opt_s = NULL;
85 	const char * opt_t = NULL;
86 	const char * opt_u = NULL;
87 
88 	/* Working variables. */
89 	struct sock_addr ** sas_s;
90 	struct sock_addr ** sas_t;
91 	struct proto_secret * K;
92 	const char * ch;
93 	char * pidfilename = NULL;
94 	int s;
95 	void * dispatch_cookie = NULL;
96 	int conndone = 0;
97 
98 	WARNP_INIT;
99 
100 	/* Parse the command line. */
101 	while ((ch = GETOPT(argc, argv)) != NULL) {
102 		GETOPT_SWITCH(ch) {
103 		GETOPT_OPT("-d"):
104 			if (opt_d || opt_e)
105 				usage();
106 			opt_d = 1;
107 			break;
108 		GETOPT_OPT("-D"):
109 			if (opt_D)
110 				usage();
111 			opt_D = 1;
112 			break;
113 		GETOPT_OPT("-e"):
114 			if (opt_d || opt_e)
115 				usage();
116 			opt_e = 1;
117 			break;
118 		GETOPT_OPT("-f"):
119 			if (opt_f)
120 				usage();
121 			opt_f = 1;
122 			break;
123 		GETOPT_OPT("-F"):
124 			if (opt_F)
125 				usage();
126 			opt_F = 1;
127 			break;
128 		GETOPT_OPT("-g"):
129 			if (opt_g)
130 				usage();
131 			opt_g = 1;
132 			break;
133 		GETOPT_OPT("-j"):
134 			if (opt_j)
135 				usage();
136 			opt_j = 1;
137 			break;
138 		GETOPT_OPTARG("-k"):
139 			if (opt_k)
140 				usage();
141 			opt_k = optarg;
142 			break;
143 		GETOPT_OPTARG("-n"):
144 			if (opt_n_set)
145 				usage();
146 			opt_n_set = 1;
147 			if (PARSENUM(&opt_n, optarg))
148 				OPT_EPARSE(ch, optarg);
149 			if (opt_n == 0)
150 				opt_n = SIZE_MAX;
151 			break;
152 		GETOPT_OPTARG("-o"):
153 			if (opt_o_set)
154 				usage();
155 			opt_o_set = 1;
156 			if (PARSENUM(&opt_o, optarg, 0, INFINITY))
157 				OPT_EPARSE(ch, optarg);
158 			break;
159 		GETOPT_OPTARG("-p"):
160 			if (opt_p)
161 				usage();
162 			opt_p = optarg;
163 			break;
164 		GETOPT_OPTARG("-r"):
165 			if (opt_r_set)
166 				usage();
167 			opt_r_set = 1;
168 			if (PARSENUM(&opt_r, optarg, 0, INFINITY))
169 				OPT_EPARSE(ch, optarg);
170 			break;
171 		GETOPT_OPT("-R"):
172 			if (opt_R)
173 				usage();
174 			opt_R = 1;
175 			break;
176 		GETOPT_OPTARG("-s"):
177 			if (opt_s)
178 				usage();
179 			opt_s = optarg;
180 			break;
181 		GETOPT_OPT("--syslog"):
182 			if (opt_syslog)
183 				usage();
184 			opt_syslog = 1;
185 			break;
186 		GETOPT_OPTARG("-t"):
187 			if (opt_t)
188 				usage();
189 			opt_t = optarg;
190 			break;
191 		GETOPT_OPTARG("-u"):
192 			if (opt_u != NULL)
193 				usage();
194 			opt_u = optarg;
195 			break;
196 		GETOPT_OPT("-v"):
197 			fprintf(stderr, "spiped 1.6.2\n");
198 			exit(0);
199 		GETOPT_MISSING_ARG:
200 			warn0("Missing argument to %s", ch);
201 			usage();
202 		GETOPT_DEFAULT:
203 			warn0("illegal option -- %s", ch);
204 			usage();
205 		}
206 	}
207 	argc -= optind;
208 	argv += optind;
209 
210 	/* We should have processed all the arguments. */
211 	if (argc != 0)
212 		usage();
213 	(void)argv; /* argv is not used beyond this point. */
214 
215 	/* Set defaults. */
216 	if (!opt_n_set)
217 		opt_n = 100;
218 	if (opt_o == 0.0)
219 		opt_o = 5.0;
220 	if (opt_r == 0.0)
221 		opt_r = 60.0;
222 
223 	/* Sanity-check options. */
224 	if (!opt_d && !opt_e)
225 		usage();
226 	if (opt_f && opt_g)
227 		usage();
228 	if (opt_k == NULL)
229 		usage();
230 	if (!(opt_o > 0.0))
231 		usage();
232 	if ((opt_r != 60.0) && opt_R)
233 		usage();
234 	if (opt_s == NULL)
235 		usage();
236 	if (opt_t == NULL)
237 		usage();
238 
239 	/*
240 	 * A limit of SIZE_MAX connections is equivalent to any larger limit;
241 	 * we'll be unable to allocate memory for socket bookkeeping before we
242 	 * reach either.
243 	 */
244 	if (opt_n > SIZE_MAX)
245 		opt_n = SIZE_MAX;
246 
247 	/* Figure out where our pid should be written. */
248 	if (asprintf(&pidfilename, (opt_p != NULL) ? "%s" : "%s.pid",
249 	    (opt_p != NULL) ? opt_p : opt_s) == -1) {
250 		warnp("asprintf");
251 		goto err0;
252 	}
253 
254 	/* Check whether we are running as init (e.g., inside a container). */
255 	if (getpid() == 1) {
256 		/* https://github.com/docker/docker/issues/7086 */
257 		warn0("WARNING: Applying workaround for Docker signal-handling bug");
258 
259 		/* Bind an explicit signal handler for SIGINT. */
260 		if (signal(SIGINT, diediedie_handler) == SIG_ERR) {
261 			warnp("Failed to bind SIGINT signal handler");
262 		}
263 	}
264 
265 	/* Daemonize early if we're going to wait for DNS to be ready. */
266 	if (opt_D && !opt_F) {
267 		if (daemonize(pidfilename)) {
268 			warnp("Failed to daemonize");
269 			goto err1;
270 		}
271 
272 		/* Send to syslog (if applicable). */
273 		if (opt_syslog)
274 			warnp_syslog(1);
275 	}
276 
277 	/* Resolve source address. */
278 	while ((sas_s = sock_resolve(opt_s)) == NULL) {
279 		if (!opt_D) {
280 			warnp("Error resolving socket address: %s", opt_s);
281 			goto err1;
282 		}
283 		sleep(1);
284 	}
285 	if (sas_s[0] == NULL) {
286 		warn0("No addresses found for %s", opt_s);
287 		goto err2;
288 	}
289 
290 	/* Resolve target address. */
291 	while ((sas_t = sock_resolve(opt_t)) == NULL) {
292 		if (!opt_D) {
293 			warnp("Error resolving socket address: %s", opt_t);
294 			goto err2;
295 		}
296 		sleep(1);
297 	}
298 	if (sas_t[0] == NULL) {
299 		warn0("No addresses found for %s", opt_t);
300 		goto err3;
301 	}
302 
303 	/* Load the keying data. */
304 	if ((K = proto_crypt_secret(opt_k)) == NULL) {
305 		warnp("Error reading shared secret");
306 		goto err3;
307 	}
308 
309 	/* Create and bind a socket, and mark it as listening. */
310 	if (sas_s[1] != NULL)
311 		warn0("Listening on first of multiple addresses found for %s",
312 		    opt_s);
313 	if ((s = sock_listener(sas_s[0])) == -1)
314 		goto err4;
315 
316 	/* Daemonize and write pid. */
317 	if (!opt_D && !opt_F) {
318 		if (daemonize(pidfilename)) {
319 			warnp("Failed to daemonize");
320 			goto err5;
321 		}
322 		/* Send to syslog (if applicable). */
323 		if (opt_syslog)
324 			warnp_syslog(1);
325 	}
326 
327 	/* Drop privileges (if applicable). */
328 	if (opt_u && setuidgid(opt_u, SETUIDGID_SGROUP_LEAVE_WARN)) {
329 		warnp("Failed to drop privileges");
330 		goto err5;
331 	}
332 
333 	/* Start accepting connections. */
334 	if ((dispatch_cookie = dispatch_accept(s, opt_t, opt_R ? 0.0 : opt_r,
335 	    sas_t, opt_d, opt_f, opt_g, opt_j, K, opt_n, opt_o,
336 	    &conndone)) == NULL) {
337 		warnp("Failed to initialize connection acceptor");
338 		goto err5;
339 	}
340 
341 	/* dispatch is now maintaining sas_t and s. */
342 	sas_t = NULL;
343 	s = -1;
344 
345 	/* Register a handler for SIGTERM. */
346 	if (graceful_shutdown_initialize(&callback_graceful_shutdown,
347 	    dispatch_cookie)) {
348 		warn0("Failed to start graceful_shutdown timer");
349 		goto err6;
350 	}
351 
352 	/*
353 	 * Loop until an error occurs, or a connection closes if the
354 	 * command-line argument -1 was given.
355 	 */
356 	if (events_spin(&conndone)) {
357 		warnp("Error running event loop");
358 		goto err6;
359 	}
360 
361 	/* Stop accepting connections and shut down the dispatcher. */
362 	dispatch_shutdown(dispatch_cookie);
363 
364 	/* Free the protocol secret structure. */
365 	proto_crypt_secret_free(K);
366 
367 	/* Free arrays of resolved addresses. */
368 	sock_addr_freelist(sas_s);
369 
370 	/* Free pid filename. */
371 	free(pidfilename);
372 
373 	/* Success! */
374 	exit(0);
375 
376 err6:
377 	dispatch_shutdown(dispatch_cookie);
378 err5:
379 	if (s != -1)
380 		close(s);
381 err4:
382 	proto_crypt_secret_free(K);
383 err3:
384 	sock_addr_freelist(sas_t);
385 err2:
386 	sock_addr_freelist(sas_s);
387 err1:
388 	free(pidfilename);
389 err0:
390 	/* Failure! */
391 	exit(1);
392 }
393