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