|
|
main.c - geomyidae - A small C-based gopherd. |
|
|
 |
git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/geomyidae/ (git://bitreich.org) |
|
|
 |
Log |
|
|
 |
Files |
|
|
 |
Refs |
|
|
 |
Tags |
|
|
 |
README |
|
|
 |
LICENSE |
|
|
|
--- |
|
|
|
main.c (28309B) |
|
|
|
--- |
|
|
|
1 /* |
|
|
|
2 * Copy me if you can. |
|
|
|
3 * by 20h |
|
|
|
4 */ |
|
|
|
5 |
|
|
|
6 #include <limits.h> |
|
|
|
7 #include <unistd.h> |
|
|
|
8 #include <dirent.h> |
|
|
|
9 #include <memory.h> |
|
|
|
10 #include <netdb.h> |
|
|
|
11 #include <netinet/in.h> |
|
|
|
12 #include <fcntl.h> |
|
|
|
13 #include <stdio.h> |
|
|
|
14 #include <stdlib.h> |
|
|
|
15 #include <sys/socket.h> |
|
|
|
16 #include <sys/stat.h> |
|
|
|
17 #include <sys/wait.h> |
|
|
|
18 #include <sys/types.h> |
|
|
|
19 #include <netinet/tcp.h> |
|
|
|
20 #include <signal.h> |
|
|
|
21 #include <string.h> |
|
|
|
22 #include <strings.h> |
|
|
|
23 #include <time.h> |
|
|
|
24 #include <pwd.h> |
|
|
|
25 #include <grp.h> |
|
|
|
26 #include <errno.h> |
|
|
|
27 #include <arpa/inet.h> |
|
|
|
28 #include <sys/select.h> |
|
|
|
29 #include <sys/time.h> |
|
|
|
30 #include <syslog.h> |
|
|
|
31 |
|
|
|
32 #ifdef ENABLE_TLS |
|
|
|
33 #include <tls.h> |
|
|
|
34 #endif /* ENABLE_TLS */ |
|
|
|
35 |
|
|
|
36 #include "ind.h" |
|
|
|
37 #include "handlr.h" |
|
|
|
38 #include "arg.h" |
|
|
|
39 |
|
|
|
40 enum { |
|
|
|
41 NOLOG = 0, |
|
|
|
42 FILES = 1, |
|
|
|
43 DIRS = 2, |
|
|
|
44 HTTP = 4, |
|
|
|
45 ERRORS = 8, |
|
|
|
46 CONN = 16, |
|
|
|
47 GPLUS = 32 |
|
|
|
48 }; |
|
|
|
49 |
|
|
|
50 int glfd = -1; |
|
|
|
51 int dosyslog = 0; |
|
|
|
52 int logpriority = LOG_INFO|LOG_DAEMON; |
|
|
|
53 int loglvl = 47; |
|
|
|
54 int revlookup = 0; |
|
|
|
55 char *logfile = NULL; |
|
|
|
56 |
|
|
|
57 int *listfds = NULL; |
|
|
|
58 int nlistfds = 0; |
|
|
|
59 |
|
|
|
60 char *argv0; |
|
|
|
61 char stdbase[] = "/var/gopher"; |
|
|
|
62 char *stdport = "70"; |
|
|
|
63 char *indexf[] = {"index.gph", "index.cgi", "index.dcgi", "index.bob", "index.bin"}; |
|
|
|
64 |
|
|
|
65 char *nocgierr = "3Sorry, execution of the token '%s' was requested, but this " |
|
|
|
66 "is disabled in the server configuration.\tErr" |
|
|
|
67 "\tlocalhost\t70\r\n"; |
|
|
|
68 |
|
|
|
69 char *notfounderr = "3Sorry, but the requested token '%s' could not be found.\tErr" |
|
|
|
70 "\tlocalhost\t70\r\n"; |
|
|
|
71 |
|
|
|
72 char *toolongerr = "3Sorry, but the requested token '%s' is a too long path.\tErr" |
|
|
|
73 "\tlocalhost\t70\r\n"; |
|
|
|
74 |
|
|
|
75 char *tlserr = "3Sorry, but the requested token '%s' requires an encrypted connection.\tErr" |
|
|
|
76 "\tlocalhost\t70\r\n"; |
|
|
|
77 |
|
|
|
78 /* TODO: Transform gopherspace to not need this anymore. See sacc(1). */ |
|
|
|
79 char *htredir = "<!DOCTYPE html>\n" |
|
|
|
80 "<html><head><title>gopher redirect</title>\n" |
|
|
|
81 "<meta http-equiv=\"refresh\" content=\"1;url=%s\" />\n" |
|
|
|
82 "</head><body>\n" |
|
|
|
83 "Please consider using native gopher 'w' type.\n" |
|
|
|
84 "HTML is insecure and bloated.<br/>\n" |
|
|
|
85 "You will be redirected to: <a href=\"%s\">%s</a>.\n" |
|
|
|
86 "</body></html>\n"; |
|
|
|
87 |
|
|
|
88 char *htescape = "3Happy helping ☃ here: " |
|
|
|
89 "Sorry, your URI was not properly escaped." |
|
|
|
90 "\tErr\tlocalhost\t70\r\n.\r\n\r\n"; |
|
|
|
91 |
|
|
|
92 char *selinval = "3Happy helping ☃ here: " |
|
|
|
93 "Sorry, your selector does contains '..'. " |
|
|
|
94 "That's illegal here.\tErr\tlocalhost\t70\r\n.\r\n\r\n"; |
|
|
|
95 |
|
|
|
96 int |
|
|
|
97 dropprivileges(struct group *gr, struct passwd *pw) |
|
|
|
98 { |
|
|
|
99 if (gr != NULL) |
|
|
|
100 if (setgroups(1, &gr->gr_gid) != 0 || setgid(gr->gr_gid) != 0) |
|
|
|
101 return -1; |
|
|
|
102 if (pw != NULL) { |
|
|
|
103 if (gr == NULL) { |
|
|
|
104 if (setgroups(1, &pw->pw_gid) != 0 || |
|
|
|
105 setgid(pw->pw_gid) != 0) |
|
|
|
106 return -1; |
|
|
|
107 } |
|
|
|
108 if (setuid(pw->pw_uid) != 0) |
|
|
|
109 return -1; |
|
|
|
110 } |
|
|
|
111 |
|
|
|
112 return 0; |
|
|
|
113 } |
|
|
|
114 |
|
|
|
115 void |
|
|
|
116 logentry(char *host, char *port, char *qry, char *status) |
|
|
|
117 { |
|
|
|
118 time_t tim; |
|
|
|
119 struct tm *ptr; |
|
|
|
120 char timstr[128], *ahost; |
|
|
|
121 |
|
|
|
122 if (glfd >= 0 || dosyslog) { |
|
|
|
123 ahost = revlookup ? reverselookup(host) : host; |
|
|
|
124 if (dosyslog) { |
|
|
|
125 syslog(logpriority, "[%s|%s|%s] %s\n", ahost, port, |
|
|
|
126 status, qry); |
|
|
|
127 } else { |
|
|
|
128 tim = time(0); |
|
|
|
129 ptr = gmtime(&tim); |
|
|
|
130 strftime(timstr, sizeof(timstr), "%F %T %z", ptr); |
|
|
|
131 dprintf(glfd, "[%s|%s|%s|%s] %s\n", |
|
|
|
132 timstr, ahost, port, status, qry); |
|
|
|
133 } |
|
|
|
134 if (revlookup) |
|
|
|
135 free(ahost); |
|
|
|
136 } |
|
|
|
137 |
|
|
|
138 return; |
|
|
|
139 } |
|
|
|
140 |
|
|
|
141 void |
|
|
|
142 handlerequest(int sock, char *req, int rlen, char *base, char *ohost, |
|
|
|
143 char *port, char *clienth, char *clientp, char *serverh, |
|
|
|
144 char *serverp, int nocgi, int istls) |
|
|
|
145 { |
|
|
|
146 struct stat dir; |
|
|
|
147 char recvc[1025], recvb[1025], path[PATH_MAX+1], args[1025], |
|
|
|
148 argsc[1025], traverse[1025], traversec[1025], |
|
|
|
149 *sear, *sep, *recvbp, *c; |
|
|
|
150 int len = 0, fd, i, maxrecv, pathfallthrough = 0; |
|
|
|
151 filetype *type; |
|
|
|
152 |
|
|
|
153 if (!istls) { |
|
|
|
154 /* |
|
|
|
155 * If sticky bit is set on base dir and encryption is not |
|
|
|
156 * used, do not serve. |
|
|
|
157 */ |
|
|
|
158 if (stat(*base? base : "/", &dir) == -1) |
|
|
|
159 return; |
|
|
|
160 if (dir.st_mode & S_ISVTX) { |
|
|
|
161 dprintf(sock, tlserr, recvc); |
|
|
|
162 if (loglvl & ERRORS) { |
|
|
|
163 logentry(clienth, clientp, recvc, |
|
|
|
164 "encryption only"); |
|
|
|
165 } |
|
|
|
166 return; |
|
|
|
167 } |
|
|
|
168 } |
|
|
|
169 |
|
|
|
170 memset(&dir, 0, sizeof(dir)); |
|
|
|
171 memset(recvb, 0, sizeof(recvb)); |
|
|
|
172 memset(recvc, 0, sizeof(recvc)); |
|
|
|
173 memset(args, 0, sizeof(args)); |
|
|
|
174 memset(argsc, 0, sizeof(argsc)); |
|
|
|
175 memset(traverse, 0, sizeof(argsc)); |
|
|
|
176 |
|
|
|
177 maxrecv = sizeof(recvb) - 1; |
|
|
|
178 if (rlen > maxrecv || rlen < 0) |
|
|
|
179 return; |
|
|
|
180 memcpy(recvb, req, rlen); |
|
|
|
181 |
|
|
|
182 c = strchr(recvb, '\r'); |
|
|
|
183 if (c) |
|
|
|
184 c[0] = '\0'; |
|
|
|
185 c = strchr(recvb, '\n'); |
|
|
|
186 if (c) |
|
|
|
187 c[0] = '\0'; |
|
|
|
188 |
|
|
|
189 memmove(recvc, recvb, rlen+1); |
|
|
|
190 /* |
|
|
|
191 * Try to guess if we have some HTTP-like protocol compatibility |
|
|
|
192 * mode. |
|
|
|
193 */ |
|
|
|
194 if (!nocgi && recvb[0] != '/' && (c = strchr(recvb, ' '))) { |
|
|
|
195 *c = '\0'; |
|
|
|
196 if (strchr(recvb, '/')) |
|
|
|
197 goto dothegopher; |
|
|
|
198 if (snprintf(path, sizeof(path), "%s/%s", base, recvb) <= sizeof(path)) { |
|
|
|
199 if (stat(path, &dir) == 0) { |
|
|
|
200 if (loglvl & FILES) |
|
|
|
201 logentry(clienth, clientp, recvc, "compatibility serving"); |
|
|
|
202 |
|
|
|
203 handlecgi(sock, path, port, base, "", "", ohost, |
|
|
|
204 clienth, serverh, istls, req, ""); |
|
|
|
205 return; |
|
|
|
206 } |
|
|
|
207 } |
|
|
|
208 dothegopher: |
|
|
|
209 *c = ' '; |
|
|
|
210 } |
|
|
|
211 |
|
|
|
212 /* Do not allow requests including "..". */ |
|
|
|
213 if (strstr(recvb, "..")) { |
|
|
|
214 dprintf(sock, "%s", selinval); |
|
|
|
215 return; |
|
|
|
216 } |
|
|
|
217 |
|
|
|
218 sear = strchr(recvb, '\t'); |
|
|
|
219 if (sear != NULL) { |
|
|
|
220 *sear++ = '\0'; |
|
|
|
221 |
|
|
|
222 /* |
|
|
|
223 * This is a compatibility layer to geomyidae for users using |
|
|
|
224 * the original gopher(1) client. Gopher+ is by default |
|
|
|
225 * requesting the metadata. We are using a trick in the |
|
|
|
226 * gopher(1) parsing code to jump back to gopher compatibility |
|
|
|
227 * mode. DO NOT ADD ANY OTHER GOPHER+ SUPPORT. GOPHER+ IS |
|
|
|
228 * CRAP. |
|
|
|
229 */ |
|
|
|
230 if ((sear[0] == '+' && sear[1] == '\0') |
|
|
|
231 || (sear[0] == '$' && sear[1] == '\0') |
|
|
|
232 || (sear[0] == '!' && sear[1] == '\0') |
|
|
|
233 || sear[0] == '\0') { |
|
|
|
234 if (loglvl & GPLUS) |
|
|
|
235 logentry(clienth, clientp, recvb, "gopher+ redirect"); |
|
|
|
236 dprintf(sock, "+-2\r\n"); |
|
|
|
237 dprintf(sock, "+INFO: 1gopher+\t\t%s\t%s\r\n", |
|
|
|
238 ohost, port); |
|
|
|
239 dprintf(sock, "+ADMIN:\r\n Admin: Me\r\n"); |
|
|
|
240 return; |
|
|
|
241 } |
|
|
|
242 } |
|
|
|
243 |
|
|
|
244 memmove(recvc, recvb, rlen+1); |
|
|
|
245 |
|
|
|
246 /* Redirect to HTML redirecting to the specified URI. */ |
|
|
|
247 /* TODO: Fix gopherspace to not require this. */ |
|
|
|
248 if (!strncmp(recvb, "URL:", 4)) { |
|
|
|
249 for (i = 4; i < sizeof(recvb)-1; i++) { |
|
|
|
250 switch (recvb[i]) { |
|
|
|
251 case '\0': |
|
|
|
252 i = sizeof(recvb); |
|
|
|
253 break; |
|
|
|
254 case '"': |
|
|
|
255 case '&': |
|
|
|
256 case '>': |
|
|
|
257 case '<': |
|
|
|
258 case ' ': |
|
|
|
259 case '\'': |
|
|
|
260 case '\\': |
|
|
|
261 write(sock, htescape, strlen(htescape)); |
|
|
|
262 if (loglvl & ERRORS) |
|
|
|
263 logentry(clienth, clientp, recvc, "Unescaped HTTP redirect"); |
|
|
|
264 return; |
|
|
|
265 } |
|
|
|
266 } |
|
|
|
267 len = snprintf(path, sizeof(path), htredir, |
|
|
|
268 recvb + 4, recvb + 4, recvb + 4); |
|
|
|
269 if (len > sizeof(path)) |
|
|
|
270 len = sizeof(path); |
|
|
|
271 write(sock, path, len); |
|
|
|
272 if (loglvl & HTTP) |
|
|
|
273 logentry(clienth, clientp, recvc, "HTTP redirect"); |
|
|
|
274 return; |
|
|
|
275 } |
|
|
|
276 |
|
|
|
277 /* Strip off the arguments of req?args style. */ |
|
|
|
278 c = strchr(recvb, '?'); |
|
|
|
279 if (c != NULL) { |
|
|
|
280 *c++ = '\0'; |
|
|
|
281 snprintf(args, sizeof(args), "%s", c); |
|
|
|
282 } |
|
|
|
283 |
|
|
|
284 /* Strip '/' at the end of the request. */ |
|
|
|
285 for (c = recvb + strlen(recvb) - 1; c >= recvb && c[0] == '/'; c--) { |
|
|
|
286 memmove(traversec, traverse, strlen(traverse)); |
|
|
|
287 /* Prepend to traverse. */ |
|
|
|
288 snprintf(traverse, sizeof(traverse), "/%s", traversec); |
|
|
|
289 c[0] = '\0'; |
|
|
|
290 } |
|
|
|
291 |
|
|
|
292 /* path is now always at least '/' */ |
|
|
|
293 if (snprintf(path, sizeof(path), "%s%s%s", base, |
|
|
|
294 (*recvb != '/')? "/" : "", |
|
|
|
295 recvb) > sizeof(path)) { |
|
|
|
296 if (loglvl & ERRORS) { |
|
|
|
297 logentry(clienth, clientp, recvc, |
|
|
|
298 "path truncation occurred"); |
|
|
|
299 } |
|
|
|
300 dprintf(sock, toolongerr, recvc); |
|
|
|
301 return; |
|
|
|
302 } |
|
|
|
303 |
|
|
|
304 fd = -1; |
|
|
|
305 /* |
|
|
|
306 * If path could not be found, do: |
|
|
|
307 * 1.) Traverse from base directory one dir by dir. |
|
|
|
308 * 2.) If one path element, separated by "/", is not found, stop. |
|
|
|
309 * 3.) Prepare new args string: |
|
|
|
310 * |
|
|
|
311 * $args = $rest_of_path + "?" + $args |
|
|
|
312 */ |
|
|
|
313 if (stat(path, &dir) == -1) { |
|
|
|
314 memmove(traversec, traverse, strlen(traverse)); |
|
|
|
315 snprintf(path, sizeof(path), "%s", base); |
|
|
|
316 recvbp = recvb; |
|
|
|
317 |
|
|
|
318 /* |
|
|
|
319 * Walk into the selector until some directory or file |
|
|
|
320 * does not exist. Then reconstruct the args, selector |
|
|
|
321 * etc. |
|
|
|
322 */ |
|
|
|
323 while (recvbp != NULL) { |
|
|
|
324 /* Traverse multiple empty / in selector. */ |
|
|
|
325 while(recvbp[0] == '/') |
|
|
|
326 recvbp++; |
|
|
|
327 sep = strchr(recvbp, '/'); |
|
|
|
328 if (sep != NULL) |
|
|
|
329 *sep++ = '\0'; |
|
|
|
330 |
|
|
|
331 snprintf(path+strlen(path), sizeof(path)-strlen(path), |
|
|
|
332 "/%s", recvbp); |
|
|
|
333 /* path is now always at least '/' */ |
|
|
|
334 if (stat(path, &dir) == -1) { |
|
|
|
335 path[strlen(path)-strlen(recvbp)-1] = '\0'; |
|
|
|
336 snprintf(traverse, sizeof(traverse), |
|
|
|
337 "/%s%s%s%s", |
|
|
|
338 recvbp, |
|
|
|
339 (sep != NULL)? "/" : "", |
|
|
|
340 (sep != NULL)? sep : "", |
|
|
|
341 (traversec[0] != '\0')? traversec : "" |
|
|
|
342 ); |
|
|
|
343 /* path fallthrough */ |
|
|
|
344 pathfallthrough = 1; |
|
|
|
345 break; |
|
|
|
346 } |
|
|
|
347 /* Append found directory to path. */ |
|
|
|
348 recvbp = sep; |
|
|
|
349 } |
|
|
|
350 } |
|
|
|
351 |
|
|
|
352 if (stat(path, &dir) != -1) { |
|
|
|
353 /* |
|
|
|
354 * If sticky bit is set, only serve if this is encrypted. |
|
|
|
355 */ |
|
|
|
356 if ((dir.st_mode & S_ISVTX) && !istls) { |
|
|
|
357 dprintf(sock, tlserr, recvc); |
|
|
|
358 if (loglvl & ERRORS) { |
|
|
|
359 logentry(clienth, clientp, recvc, |
|
|
|
360 "encryption only"); |
|
|
|
361 } |
|
|
|
362 return; |
|
|
|
363 } |
|
|
|
364 |
|
|
|
365 if (S_ISDIR(dir.st_mode)) { |
|
|
|
366 for (i = 0; i < sizeof(indexf)/sizeof(indexf[0]); |
|
|
|
367 i++) { |
|
|
|
368 len = strlen(path); |
|
|
|
369 if (len + strlen(indexf[i]) + ((path[len-1] == '/')? 0 : 1) |
|
|
|
370 >= sizeof(path)) { |
|
|
|
371 if (loglvl & ERRORS) { |
|
|
|
372 logentry(clienth, clientp, |
|
|
|
373 recvc, |
|
|
|
374 "path truncation occurred"); |
|
|
|
375 } |
|
|
|
376 return; |
|
|
|
377 } |
|
|
|
378 /* |
|
|
|
379 * The size check for strcat to work is |
|
|
|
380 * calculated above this comment. |
|
|
|
381 * |
|
|
|
382 * Until strlcat isn't properly in all |
|
|
|
383 * linux libcs, we keep to this. OpenBSD |
|
|
|
384 * will complain about strcat and |
|
|
|
385 * smart-ass gcc will cmplain about |
|
|
|
386 * strncat of one char static char array |
|
|
|
387 * is an overflow. |
|
|
|
388 */ |
|
|
|
389 if (path[len-1] != '/') |
|
|
|
390 strcat(path, "/"); |
|
|
|
391 strcat(path, indexf[i]); |
|
|
|
392 fd = open(path, O_RDONLY); |
|
|
|
393 if (fd >= 0) |
|
|
|
394 break; |
|
|
|
395 |
|
|
|
396 /* Not found. Clear path from indexf. */ |
|
|
|
397 path[len] = '\0'; |
|
|
|
398 } |
|
|
|
399 } else { |
|
|
|
400 fd = open(path, O_RDONLY); |
|
|
|
401 if (fd < 0) { |
|
|
|
402 dprintf(sock, notfounderr, recvc); |
|
|
|
403 if (loglvl & ERRORS) { |
|
|
|
404 logentry(clienth, clientp, recvc, |
|
|
|
405 strerror(errno)); |
|
|
|
406 } |
|
|
|
407 return; |
|
|
|
408 } |
|
|
|
409 } |
|
|
|
410 } |
|
|
|
411 |
|
|
|
412 /* Some file was opened. Serve it. */ |
|
|
|
413 if (fd >= 0) { |
|
|
|
414 close(fd); |
|
|
|
415 |
|
|
|
416 c = strrchr(path, '/'); |
|
|
|
417 if (c == NULL) |
|
|
|
418 c = path; |
|
|
|
419 type = gettype(c); |
|
|
|
420 |
|
|
|
421 /* |
|
|
|
422 * If we had to traverse the path to find some, only |
|
|
|
423 * allow index.dcgi and index.cgi as handlers. |
|
|
|
424 */ |
|
|
|
425 if (pathfallthrough && |
|
|
|
426 !(type->f == handledcgi || type->f == handlecgi)) { |
|
|
|
427 dprintf(sock, notfounderr, recvc); |
|
|
|
428 if (loglvl & ERRORS) { |
|
|
|
429 logentry(clienth, clientp, recvc, |
|
|
|
430 "handler in path fallthrough not allowed"); |
|
|
|
431 } |
|
|
|
432 return; |
|
|
|
433 } |
|
|
|
434 |
|
|
|
435 if (nocgi && (type->f == handledcgi || type->f == handlecgi)) { |
|
|
|
436 dprintf(sock, nocgierr, recvc); |
|
|
|
437 if (loglvl & ERRORS) |
|
|
|
438 logentry(clienth, clientp, recvc, "nocgi error"); |
|
|
|
439 } else { |
|
|
|
440 if (loglvl & FILES) |
|
|
|
441 logentry(clienth, clientp, recvc, "serving"); |
|
|
|
442 |
|
|
|
443 type->f(sock, path, port, base, args, sear, ohost, |
|
|
|
444 clienth, serverh, istls, recvc, traverse); |
|
|
|
445 } |
|
|
|
446 } else { |
|
|
|
447 if (pathfallthrough && S_ISDIR(dir.st_mode)) { |
|
|
|
448 dprintf(sock, notfounderr, recvc); |
|
|
|
449 if (loglvl & ERRORS) { |
|
|
|
450 logentry(clienth, clientp, recvc, |
|
|
|
451 "directory listing in traversal not allowed"); |
|
|
|
452 } |
|
|
|
453 return; |
|
|
|
454 } |
|
|
|
455 |
|
|
|
456 if (!pathfallthrough && S_ISDIR(dir.st_mode)) { |
|
|
|
457 handledir(sock, path, port, base, args, sear, ohost, |
|
|
|
458 clienth, serverh, istls, recvc, traverse); |
|
|
|
459 if (loglvl & DIRS) { |
|
|
|
460 logentry(clienth, clientp, recvc, |
|
|
|
461 "dir listing"); |
|
|
|
462 } |
|
|
|
463 return; |
|
|
|
464 } |
|
|
|
465 |
|
|
|
466 dprintf(sock, notfounderr, recvc); |
|
|
|
467 if (loglvl & ERRORS) |
|
|
|
468 logentry(clienth, clientp, recvc, "not found"); |
|
|
|
469 } |
|
|
|
470 |
|
|
|
471 return; |
|
|
|
472 } |
|
|
|
473 |
|
|
|
474 void |
|
|
|
475 sighandler(int sig) |
|
|
|
476 { |
|
|
|
477 int i; |
|
|
|
478 |
|
|
|
479 switch (sig) { |
|
|
|
480 case SIGCHLD: |
|
|
|
481 while (waitpid(-1, NULL, WNOHANG) > 0); |
|
|
|
482 break; |
|
|
|
483 case SIGINT: |
|
|
|
484 case SIGQUIT: |
|
|
|
485 case SIGABRT: |
|
|
|
486 case SIGTERM: |
|
|
|
487 if (dosyslog) { |
|
|
|
488 closelog(); |
|
|
|
489 } else if (logfile != NULL && glfd != -1) { |
|
|
|
490 close(glfd); |
|
|
|
491 glfd = -1; |
|
|
|
492 } |
|
|
|
493 |
|
|
|
494 for (i = 0; i < nlistfds; i++) { |
|
|
|
495 shutdown(listfds[i], SHUT_RDWR); |
|
|
|
496 close(listfds[i]); |
|
|
|
497 } |
|
|
|
498 free(listfds); |
|
|
|
499 exit(0); |
|
|
|
500 break; |
|
|
|
501 default: |
|
|
|
502 break; |
|
|
|
503 } |
|
|
|
504 } |
|
|
|
505 |
|
|
|
506 void |
|
|
|
507 initsignals(void) |
|
|
|
508 { |
|
|
|
509 signal(SIGCHLD, sighandler); |
|
|
|
510 signal(SIGHUP, sighandler); |
|
|
|
511 signal(SIGINT, sighandler); |
|
|
|
512 signal(SIGQUIT, sighandler); |
|
|
|
513 signal(SIGABRT, sighandler); |
|
|
|
514 signal(SIGTERM, sighandler); |
|
|
|
515 |
|
|
|
516 signal(SIGPIPE, SIG_IGN); |
|
|
|
517 } |
|
|
|
518 |
|
|
|
519 /* |
|
|
|
520 * TODO: Move Linux and BSD to Plan 9 socket and bind handling, so we do not |
|
|
|
521 * need the inconsistent return and exit on getaddrinfo. |
|
|
|
522 */ |
|
|
|
523 int * |
|
|
|
524 getlistenfd(struct addrinfo *hints, char *bindip, char *port, int *rlfdnum) |
|
|
|
525 { |
|
|
|
526 char addstr[INET6_ADDRSTRLEN]; |
|
|
|
527 struct addrinfo *ai, *rp; |
|
|
|
528 void *sinaddr; |
|
|
|
529 int on, *listenfds, *listenfd, aierr, errno_save; |
|
|
|
530 |
|
|
|
531 if ((aierr = getaddrinfo(bindip, port, hints, &ai)) || ai == NULL) { |
|
|
|
532 fprintf(stderr, "getaddrinfo (%s:%s): %s\n", bindip, port, |
|
|
|
533 gai_strerror(aierr)); |
|
|
|
534 exit(1); |
|
|
|
535 } |
|
|
|
536 |
|
|
|
537 *rlfdnum = 0; |
|
|
|
538 listenfds = NULL; |
|
|
|
539 on = 1; |
|
|
|
540 for (rp = ai; rp != NULL; rp = rp->ai_next) { |
|
|
|
541 listenfds = xrealloc(listenfds, |
|
|
|
542 sizeof(*listenfds) * (++*rlfdnum)); |
|
|
|
543 listenfd = &listenfds[*rlfdnum-1]; |
|
|
|
544 |
|
|
|
545 *listenfd = socket(rp->ai_family, rp->ai_socktype, |
|
|
|
546 rp->ai_protocol); |
|
|
|
547 if (*listenfd < 0) |
|
|
|
548 continue; |
|
|
|
549 if (setsockopt(*listenfd, SOL_SOCKET, SO_REUSEADDR, &on, |
|
|
|
550 sizeof(on)) < 0) { |
|
|
|
551 close(*listenfd); |
|
|
|
552 (*rlfdnum)--; |
|
|
|
553 continue; |
|
|
|
554 } |
|
|
|
555 |
|
|
|
556 if (rp->ai_family == AF_INET6 && (setsockopt(*listenfd, |
|
|
|
557 IPPROTO_IPV6, IPV6_V6ONLY, &on, |
|
|
|
558 sizeof(on)) < 0)) { |
|
|
|
559 close(*listenfd); |
|
|
|
560 (*rlfdnum)--; |
|
|
|
561 continue; |
|
|
|
562 } |
|
|
|
563 |
|
|
|
564 sinaddr = (rp->ai_family == AF_INET) ? |
|
|
|
565 (void *)&((struct sockaddr_in *)rp->ai_addr)->sin_addr : |
|
|
|
566 (void *)&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr; |
|
|
|
567 |
|
|
|
568 if (bind(*listenfd, rp->ai_addr, rp->ai_addrlen) == 0) { |
|
|
|
569 if (loglvl & CONN && inet_ntop(rp->ai_family, sinaddr, |
|
|
|
570 addstr, sizeof(addstr))) { |
|
|
|
571 /* Do not revlookup here. */ |
|
|
|
572 on = revlookup; |
|
|
|
573 revlookup = 0; |
|
|
|
574 logentry(addstr, port, "-", "listening"); |
|
|
|
575 revlookup = on; |
|
|
|
576 } |
|
|
|
577 continue; |
|
|
|
578 } |
|
|
|
579 |
|
|
|
580 /* Save errno, because fprintf in logentry overwrites it. */ |
|
|
|
581 errno_save = errno; |
|
|
|
582 close(*listenfd); |
|
|
|
583 (*rlfdnum)--; |
|
|
|
584 if (loglvl & CONN && inet_ntop(rp->ai_family, sinaddr, |
|
|
|
585 addstr, sizeof(addstr))) { |
|
|
|
586 /* Do not revlookup here. */ |
|
|
|
587 on = revlookup; |
|
|
|
588 revlookup = 0; |
|
|
|
589 logentry(addstr, port, "-", "could not bind"); |
|
|
|
590 revlookup = on; |
|
|
|
591 } |
|
|
|
592 errno = errno_save; |
|
|
|
593 } |
|
|
|
594 freeaddrinfo(ai); |
|
|
|
595 if (*rlfdnum < 1) { |
|
|
|
596 free(listenfds); |
|
|
|
597 return NULL; |
|
|
|
598 } |
|
|
|
599 |
|
|
|
600 return listenfds; |
|
|
|
601 } |
|
|
|
602 |
|
|
|
603 void |
|
|
|
604 usage(void) |
|
|
|
605 { |
|
|
|
606 dprintf(2, "usage: %s [-46cdensy] [-l logfile] " |
|
|
|
607 #ifdef ENABLE_TLS |
|
|
|
608 "[-t keyfile certfile] " |
|
|
|
609 #endif /* ENABLE_TLS */ |
|
|
|
610 "[-v loglvl] [-b base] [-p port] [-o sport] " |
|
|
|
611 "[-u user] [-g group] [-h host] [-i interface ...]\n", |
|
|
|
612 argv0); |
|
|
|
613 exit(1); |
|
|
|
614 } |
|
|
|
615 |
|
|
|
616 int |
|
|
|
617 main(int argc, char *argv[]) |
|
|
|
618 { |
|
|
|
619 struct addrinfo hints; |
|
|
|
620 struct sockaddr_storage clt, slt; |
|
|
|
621 socklen_t cltlen, sltlen; |
|
|
|
622 int sock, dofork = 1, inetf = AF_UNSPEC, usechroot = 0, |
|
|
|
623 nocgi = 0, errno_save, nbindips = 0, i, j, |
|
|
|
624 nlfdret, *lfdret, listfd, maxlfd, istls = 0, |
|
|
|
625 dotls = 0, dohaproxy = 0, tcpver = -1, haret = 0, |
|
|
|
626 #ifdef ENABLE_TLS |
|
|
|
627 tlssocks[2], shufbuf[1025], |
|
|
|
628 shuflen, wlen, shufpos, tlsclientreader, |
|
|
|
629 #endif /* ENABLE_TLS */ |
|
|
|
630 maxrecv, retl, |
|
|
|
631 rlen = 0; |
|
|
|
632 fd_set rfd; |
|
|
|
633 char *port, *base, clienth[NI_MAXHOST], clientp[NI_MAXSERV], |
|
|
|
634 *user = NULL, *group = NULL, **bindips = NULL, |
|
|
|
635 *ohost = NULL, *sport = NULL, *p; |
|
|
|
636 /* Must be as large as recvb, due to scanf restrictions. */ |
|
|
|
637 char hachost[1025], hashost[1025], hacport[1025], hasport[1025], |
|
|
|
638 #ifdef ENABLE_TLS |
|
|
|
639 *certfile = NULL, *keyfile = NULL, |
|
|
|
640 #endif /* ENABLE_TLS */ |
|
|
|
641 byte0, recvb[1025], serverh[NI_MAXHOST], serverp[NI_MAXSERV]; |
|
|
|
642 struct passwd *us = NULL; |
|
|
|
643 struct group *gr = NULL; |
|
|
|
644 #ifdef ENABLE_TLS |
|
|
|
645 struct tls_config *tlsconfig = NULL; |
|
|
|
646 struct tls *tlsctx = NULL, *tlsclientctx; |
|
|
|
647 #endif /* ENABLE_TLS */ |
|
|
|
648 |
|
|
|
649 base = stdbase; |
|
|
|
650 port = stdport; |
|
|
|
651 |
|
|
|
652 ARGBEGIN { |
|
|
|
653 case '4': |
|
|
|
654 inetf = AF_INET; |
|
|
|
655 tcpver = 4; |
|
|
|
656 break; |
|
|
|
657 case '6': |
|
|
|
658 inetf = AF_INET6; |
|
|
|
659 tcpver = 6; |
|
|
|
660 break; |
|
|
|
661 case 'b': |
|
|
|
662 base = EARGF(usage()); |
|
|
|
663 break; |
|
|
|
664 case 'c': |
|
|
|
665 usechroot = 1; |
|
|
|
666 break; |
|
|
|
667 case 'd': |
|
|
|
668 dofork = 0; |
|
|
|
669 break; |
|
|
|
670 case 'e': |
|
|
|
671 nocgi = 1; |
|
|
|
672 break; |
|
|
|
673 case 'g': |
|
|
|
674 group = EARGF(usage()); |
|
|
|
675 break; |
|
|
|
676 case 'h': |
|
|
|
677 ohost = EARGF(usage()); |
|
|
|
678 break; |
|
|
|
679 case 'i': |
|
|
|
680 bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips)); |
|
|
|
681 bindips[nbindips-1] = EARGF(usage()); |
|
|
|
682 break; |
|
|
|
683 case 'l': |
|
|
|
684 logfile = EARGF(usage()); |
|
|
|
685 break; |
|
|
|
686 case 'n': |
|
|
|
687 revlookup = 1; |
|
|
|
688 break; |
|
|
|
689 case 'o': |
|
|
|
690 sport = EARGF(usage()); |
|
|
|
691 break; |
|
|
|
692 case 'p': |
|
|
|
693 port = EARGF(usage()); |
|
|
|
694 if (sport == NULL) |
|
|
|
695 sport = port; |
|
|
|
696 break; |
|
|
|
697 case 's': |
|
|
|
698 dosyslog = 1; |
|
|
|
699 break; |
|
|
|
700 #ifdef ENABLE_TLS |
|
|
|
701 case 't': |
|
|
|
702 dotls = 1; |
|
|
|
703 keyfile = EARGF(usage()); |
|
|
|
704 certfile = EARGF(usage()); |
|
|
|
705 break; |
|
|
|
706 #endif /* ENABLE_TLS */ |
|
|
|
707 case 'u': |
|
|
|
708 user = EARGF(usage()); |
|
|
|
709 break; |
|
|
|
710 case 'v': |
|
|
|
711 loglvl = atoi(EARGF(usage())); |
|
|
|
712 break; |
|
|
|
713 case 'y': |
|
|
|
714 dohaproxy = 1; |
|
|
|
715 break; |
|
|
|
716 default: |
|
|
|
717 usage(); |
|
|
|
718 } ARGEND; |
|
|
|
719 |
|
|
|
720 if (sport == NULL) |
|
|
|
721 sport = port; |
|
|
|
722 |
|
|
|
723 if (argc != 0) |
|
|
|
724 usage(); |
|
|
|
725 |
|
|
|
726 #ifdef ENABLE_TLS |
|
|
|
727 if (dotls) { |
|
|
|
728 if (tls_init() < 0) { |
|
|
|
729 perror("tls_init"); |
|
|
|
730 return 1; |
|
|
|
731 } |
|
|
|
732 if ((tlsconfig = tls_config_new()) == NULL) { |
|
|
|
733 perror("tls_config_new"); |
|
|
|
734 return 1; |
|
|
|
735 } |
|
|
|
736 if ((tlsctx = tls_server()) == NULL) { |
|
|
|
737 perror("tls_server"); |
|
|
|
738 return 1; |
|
|
|
739 } |
|
|
|
740 if (tls_config_set_key_file(tlsconfig, keyfile) < 0) { |
|
|
|
741 perror("tls_config_set_key_file"); |
|
|
|
742 return 1; |
|
|
|
743 } |
|
|
|
744 if (tls_config_set_cert_file(tlsconfig, certfile) < 0) { |
|
|
|
745 perror("tls_config_set_cert_file"); |
|
|
|
746 return 1; |
|
|
|
747 } |
|
|
|
748 if (tls_configure(tlsctx, tlsconfig) < 0) { |
|
|
|
749 perror("tls_configure"); |
|
|
|
750 return 1; |
|
|
|
751 } |
|
|
|
752 } |
|
|
|
753 #endif /* ENABLE_TLS */ |
|
|
|
754 |
|
|
|
755 if (ohost == NULL) { |
|
|
|
756 /* Do not use HOST_NAME_MAX, it is not defined on NetBSD. */ |
|
|
|
757 ohost = xcalloc(1, 256+1); |
|
|
|
758 if (gethostname(ohost, 256) < 0) { |
|
|
|
759 perror("gethostname"); |
|
|
|
760 free(ohost); |
|
|
|
761 return 1; |
|
|
|
762 } |
|
|
|
763 } else { |
|
|
|
764 ohost = xstrdup(ohost); |
|
|
|
765 } |
|
|
|
766 |
|
|
|
767 if (group != NULL) { |
|
|
|
768 errno = 0; |
|
|
|
769 if ((gr = getgrnam(group)) == NULL) { |
|
|
|
770 if (errno == 0) { |
|
|
|
771 fprintf(stderr, "no such group '%s'\n", group); |
|
|
|
772 } else { |
|
|
|
773 perror("getgrnam"); |
|
|
|
774 } |
|
|
|
775 return 1; |
|
|
|
776 } |
|
|
|
777 } |
|
|
|
778 |
|
|
|
779 if (user != NULL) { |
|
|
|
780 errno = 0; |
|
|
|
781 if ((us = getpwnam(user)) == NULL) { |
|
|
|
782 if (errno == 0) { |
|
|
|
783 fprintf(stderr, "no such user '%s'\n", user); |
|
|
|
784 } else { |
|
|
|
785 perror("getpwnam"); |
|
|
|
786 } |
|
|
|
787 return 1; |
|
|
|
788 } |
|
|
|
789 } |
|
|
|
790 |
|
|
|
791 if (dofork) { |
|
|
|
792 switch (fork()) { |
|
|
|
793 case -1: |
|
|
|
794 perror("fork"); |
|
|
|
795 return 1; |
|
|
|
796 case 0: |
|
|
|
797 break; |
|
|
|
798 default: |
|
|
|
799 return 0; |
|
|
|
800 } |
|
|
|
801 } |
|
|
|
802 |
|
|
|
803 if (dosyslog) { |
|
|
|
804 openlog("geomyidae", dofork? LOG_NDELAY|LOG_PID \ |
|
|
|
805 : LOG_CONS|LOG_PERROR, logpriority); |
|
|
|
806 } else if (logfile != NULL) { |
|
|
|
807 glfd = open(logfile, O_APPEND | O_WRONLY | O_CREAT, 0644); |
|
|
|
808 if (glfd < 0) { |
|
|
|
809 perror("log"); |
|
|
|
810 return 1; |
|
|
|
811 } |
|
|
|
812 } else if (!dofork) { |
|
|
|
813 glfd = 1; |
|
|
|
814 } |
|
|
|
815 |
|
|
|
816 if (bindips == NULL) { |
|
|
|
817 if (inetf == AF_INET || inetf == AF_UNSPEC) { |
|
|
|
818 bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips)); |
|
|
|
819 bindips[nbindips-1] = "0.0.0.0"; |
|
|
|
820 } |
|
|
|
821 if (inetf == AF_INET6 || inetf == AF_UNSPEC) { |
|
|
|
822 bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips)); |
|
|
|
823 bindips[nbindips-1] = "::"; |
|
|
|
824 } |
|
|
|
825 } |
|
|
|
826 |
|
|
|
827 for (i = 0; i < nbindips; i++) { |
|
|
|
828 memset(&hints, 0, sizeof(hints)); |
|
|
|
829 hints.ai_family = inetf; |
|
|
|
830 hints.ai_flags = AI_PASSIVE; |
|
|
|
831 hints.ai_socktype = SOCK_STREAM; |
|
|
|
832 if (bindips[i]) |
|
|
|
833 hints.ai_flags |= AI_CANONNAME; |
|
|
|
834 |
|
|
|
835 nlfdret = 0; |
|
|
|
836 lfdret = getlistenfd(&hints, bindips[i], port, &nlfdret); |
|
|
|
837 if (nlfdret < 1) { |
|
|
|
838 errno_save = errno; |
|
|
|
839 fprintf(stderr, "Unable to get a binding socket for " |
|
|
|
840 "%s:%s\n", bindips[i], port); |
|
|
|
841 errno = errno_save; |
|
|
|
842 perror("getlistenfd"); |
|
|
|
843 } |
|
|
|
844 |
|
|
|
845 for (j = 0; j < nlfdret; j++) { |
|
|
|
846 if (listen(lfdret[j], 255) < 0) { |
|
|
|
847 perror("listen"); |
|
|
|
848 close(lfdret[j]); |
|
|
|
849 continue; |
|
|
|
850 } |
|
|
|
851 listfds = xrealloc(listfds, |
|
|
|
852 sizeof(*listfds) * ++nlistfds); |
|
|
|
853 listfds[nlistfds-1] = lfdret[j]; |
|
|
|
854 } |
|
|
|
855 free(lfdret); |
|
|
|
856 } |
|
|
|
857 free(bindips); |
|
|
|
858 |
|
|
|
859 if (nlistfds < 1) |
|
|
|
860 return 1; |
|
|
|
861 |
|
|
|
862 if (usechroot) { |
|
|
|
863 if (chdir(base) < 0) { |
|
|
|
864 perror("chdir"); |
|
|
|
865 return 1; |
|
|
|
866 } |
|
|
|
867 base = ""; |
|
|
|
868 if (chroot(".") < 0) { |
|
|
|
869 perror("chroot"); |
|
|
|
870 return 1; |
|
|
|
871 } |
|
|
|
872 } else if (*base != '/' && !(base = realpath(base, NULL))) { |
|
|
|
873 perror("realpath"); |
|
|
|
874 return 1; |
|
|
|
875 } |
|
|
|
876 |
|
|
|
877 /* strip / at the end of base */ |
|
|
|
878 for (p = base + strlen(base) - 1; p >= base && p[0] == '/'; --p) |
|
|
|
879 p[0] = '\0'; |
|
|
|
880 |
|
|
|
881 if (dropprivileges(gr, us) < 0) { |
|
|
|
882 perror("dropprivileges"); |
|
|
|
883 |
|
|
|
884 for (i = 0; i < nlistfds; i++) { |
|
|
|
885 shutdown(listfds[i], SHUT_RDWR); |
|
|
|
886 close(listfds[i]); |
|
|
|
887 } |
|
|
|
888 free(listfds); |
|
|
|
889 return 1; |
|
|
|
890 } |
|
|
|
891 |
|
|
|
892 initsignals(); |
|
|
|
893 |
|
|
|
894 #ifdef HOT_COMPUTER |
|
|
|
895 #warning "I love you too." |
|
|
|
896 #endif |
|
|
|
897 |
|
|
|
898 #ifdef __OpenBSD__ |
|
|
|
899 char promises[31]; /* check the size needed in the fork too */ |
|
|
|
900 snprintf(promises, sizeof(promises), "rpath inet stdio proc exec %s", |
|
|
|
901 revlookup ? "dns" : ""); |
|
|
|
902 if (pledge(promises, NULL) == -1) { |
|
|
|
903 perror("pledge"); |
|
|
|
904 exit(1); |
|
|
|
905 } |
|
|
|
906 #endif /* __OpenBSD__ */ |
|
|
|
907 |
|
|
|
908 while (1) { |
|
|
|
909 FD_ZERO(&rfd); |
|
|
|
910 maxlfd = 0; |
|
|
|
911 for (i = 0; i < nlistfds; i++) { |
|
|
|
912 FD_SET(listfds[i], &rfd); |
|
|
|
913 if (listfds[i] > maxlfd) |
|
|
|
914 maxlfd = listfds[i]; |
|
|
|
915 } |
|
|
|
916 |
|
|
|
917 if (pselect(maxlfd+1, &rfd, NULL, NULL, NULL, NULL) < 0) { |
|
|
|
918 if (errno == EINTR) |
|
|
|
919 continue; |
|
|
|
920 perror("pselect"); |
|
|
|
921 break; |
|
|
|
922 } |
|
|
|
923 |
|
|
|
924 listfd = -1; |
|
|
|
925 for (i = 0; i < nlistfds; i++) { |
|
|
|
926 if (FD_ISSET(listfds[i], &rfd)) { |
|
|
|
927 listfd = listfds[i]; |
|
|
|
928 break; |
|
|
|
929 } |
|
|
|
930 } |
|
|
|
931 if (listfd < 0) |
|
|
|
932 continue; |
|
|
|
933 |
|
|
|
934 cltlen = sizeof(clt); |
|
|
|
935 sock = accept(listfd, (struct sockaddr *)&clt, &cltlen); |
|
|
|
936 if (sock < 0) { |
|
|
|
937 switch (errno) { |
|
|
|
938 case ECONNABORTED: |
|
|
|
939 case EINTR: |
|
|
|
940 continue; |
|
|
|
941 default: |
|
|
|
942 perror("accept"); |
|
|
|
943 close(listfd); |
|
|
|
944 return 1; |
|
|
|
945 } |
|
|
|
946 } |
|
|
|
947 |
|
|
|
948 sltlen = sizeof(slt); |
|
|
|
949 serverh[0] = serverp[0] = '\0'; |
|
|
|
950 if (getsockname(sock, (struct sockaddr *)&slt, &sltlen) == 0) { |
|
|
|
951 getnameinfo((struct sockaddr *)&slt, sltlen, serverh, |
|
|
|
952 sizeof(serverh), serverp, sizeof(serverp), |
|
|
|
953 NI_NUMERICHOST|NI_NUMERICSERV); |
|
|
|
954 } |
|
|
|
955 if (!strncmp(serverh, "::ffff:", 7)) |
|
|
|
956 memmove(serverh, serverh+7, strlen(serverh)-6); |
|
|
|
957 |
|
|
|
958 if (getnameinfo((struct sockaddr *)&clt, cltlen, clienth, |
|
|
|
959 sizeof(clienth), clientp, sizeof(clientp), |
|
|
|
960 NI_NUMERICHOST|NI_NUMERICSERV)) { |
|
|
|
961 clienth[0] = clientp[0] = '\0'; |
|
|
|
962 } |
|
|
|
963 |
|
|
|
964 if (!strncmp(clienth, "::ffff:", 7)) |
|
|
|
965 memmove(clienth, clienth+7, strlen(clienth)-6); |
|
|
|
966 |
|
|
|
967 if (loglvl & CONN) |
|
|
|
968 logentry(clienth, clientp, "-", "connected"); |
|
|
|
969 |
|
|
|
970 switch (fork()) { |
|
|
|
971 case -1: |
|
|
|
972 perror("fork"); |
|
|
|
973 shutdown(sock, SHUT_RDWR); |
|
|
|
974 break; |
|
|
|
975 case 0: |
|
|
|
976 close(listfd); |
|
|
|
977 |
|
|
|
978 signal(SIGHUP, SIG_DFL); |
|
|
|
979 signal(SIGQUIT, SIG_DFL); |
|
|
|
980 signal(SIGINT, SIG_DFL); |
|
|
|
981 signal(SIGTERM, SIG_DFL); |
|
|
|
982 signal(SIGALRM, SIG_DFL); |
|
|
|
983 |
|
|
|
984 #ifdef __OpenBSD__ |
|
|
|
985 snprintf(promises, sizeof(promises), |
|
|
|
986 "rpath inet stdio %s %s %s", |
|
|
|
987 !nocgi || dotls ? "proc" : "", |
|
|
|
988 nocgi ? "" : "exec", |
|
|
|
989 revlookup ? "dns" : ""); |
|
|
|
990 if (pledge(promises, NULL) == -1) { |
|
|
|
991 perror("pledge"); |
|
|
|
992 exit(1); |
|
|
|
993 } |
|
|
|
994 #endif /* __OpenBSD__ */ |
|
|
|
995 |
|
|
|
996 read_selector_again: |
|
|
|
997 rlen = 0; |
|
|
|
998 memset(recvb, 0, sizeof(recvb)); |
|
|
|
999 |
|
|
|
1000 if (recv(sock, &byte0, 1, MSG_PEEK) < 1) |
|
|
|
1001 return 1; |
|
|
|
1002 |
|
|
|
1003 #ifdef ENABLE_TLS |
|
|
|
1004 /* |
|
|
|
1005 * First byte is 0x16 == 22, which is the TLS |
|
|
|
1006 * Handshake first byte. |
|
|
|
1007 */ |
|
|
|
1008 istls = 0; |
|
|
|
1009 if (byte0 == 0x16 && dotls) { |
|
|
|
1010 istls = 1; |
|
|
|
1011 if (tls_accept_socket(tlsctx, &tlsclientctx, sock) < 0) |
|
|
|
1012 return 1; |
|
|
|
1013 wlen = TLS_WANT_POLLIN; |
|
|
|
1014 while (wlen == TLS_WANT_POLLIN \ |
|
|
|
1015 || wlen == TLS_WANT_POLLOUT) { |
|
|
|
1016 wlen = tls_handshake(tlsclientctx); |
|
|
|
1017 } |
|
|
|
1018 if (wlen == -1) |
|
|
|
1019 return 1; |
|
|
|
1020 } |
|
|
|
1021 #endif /* ENABLE_TLS */ |
|
|
|
1022 /* |
|
|
|
1023 * Some TLS request. Help them determine we only |
|
|
|
1024 * serve plaintext. |
|
|
|
1025 */ |
|
|
|
1026 if (byte0 == 0x16 && !dotls) { |
|
|
|
1027 if (loglvl & CONN) { |
|
|
|
1028 logentry(clienth, clientp, "-", |
|
|
|
1029 "disconnected"); |
|
|
|
1030 } |
|
|
|
1031 |
|
|
|
1032 shutdown(sock, SHUT_RDWR); |
|
|
|
1033 close(sock); |
|
|
|
1034 |
|
|
|
1035 return 1; |
|
|
|
1036 } |
|
|
|
1037 |
|
|
|
1038 maxrecv = sizeof(recvb) - 1; |
|
|
|
1039 do { |
|
|
|
1040 #ifdef ENABLE_TLS |
|
|
|
1041 if (istls) { |
|
|
|
1042 retl = tls_read(tlsclientctx, |
|
|
|
1043 recvb+rlen, 1); |
|
|
|
1044 if (retl < 0) |
|
|
|
1045 fprintf(stderr, "tls_read failed: %s\n", tls_error(tlsclientctx)); |
|
|
|
1046 } else |
|
|
|
1047 #endif /* ENABLE_TLS */ |
|
|
|
1048 { |
|
|
|
1049 retl = read(sock, recvb+rlen, |
|
|
|
1050 1); |
|
|
|
1051 if (retl < 0) |
|
|
|
1052 perror("read"); |
|
|
|
1053 } |
|
|
|
1054 if (retl <= 0) |
|
|
|
1055 break; |
|
|
|
1056 rlen += retl; |
|
|
|
1057 } while (recvb[rlen-1] != '\n' |
|
|
|
1058 && --maxrecv > 0); |
|
|
|
1059 if (rlen <= 0) |
|
|
|
1060 return 1; |
|
|
|
1061 |
|
|
|
1062 /* |
|
|
|
1063 * HAProxy v1 protocol support. |
|
|
|
1064 * TODO: Add other protocol version support. |
|
|
|
1065 */ |
|
|
|
1066 if (dohaproxy && !strncmp(recvb, "PROXY TCP", 9)) { |
|
|
|
1067 if (p[-1] == '\r') |
|
|
|
1068 p[-1] = '\0'; |
|
|
|
1069 *p++ = '\0'; |
|
|
|
1070 |
|
|
|
1071 /* |
|
|
|
1072 * Be careful, we are using scanf. |
|
|
|
1073 * TODO: Use some better parsing. |
|
|
|
1074 */ |
|
|
|
1075 memset(hachost, 0, sizeof(hachost)); |
|
|
|
1076 memset(hashost, 0, sizeof(hashost)); |
|
|
|
1077 memset(hacport, 0, sizeof(hacport)); |
|
|
|
1078 memset(hasport, 0, sizeof(hasport)); |
|
|
|
1079 |
|
|
|
1080 haret = sscanf(recvb, "PROXY TCP%d %s %s %s %s", |
|
|
|
1081 &tcpver, hachost, hashost, hacport, |
|
|
|
1082 hasport); |
|
|
|
1083 if (haret != 5) |
|
|
|
1084 return 1; |
|
|
|
1085 |
|
|
|
1086 /* |
|
|
|
1087 * Be careful. Everything could be |
|
|
|
1088 * malicious. |
|
|
|
1089 */ |
|
|
|
1090 memset(clienth, 0, sizeof(clienth)); |
|
|
|
1091 memmove(clienth, hachost, sizeof(clienth)-1); |
|
|
|
1092 memset(serverh, 0, sizeof(serverh)); |
|
|
|
1093 memmove(serverh, hashost, sizeof(serverh)-1); |
|
|
|
1094 memset(clientp, 0, sizeof(clientp)); |
|
|
|
1095 memmove(clientp, hacport, sizeof(clientp)-1); |
|
|
|
1096 memset(serverp, 0, sizeof(serverp)); |
|
|
|
1097 memmove(serverp, hasport, sizeof(serverp)-1); |
|
|
|
1098 |
|
|
|
1099 if (!strncmp(serverh, "::ffff:", 7)) { |
|
|
|
1100 memmove(serverh, serverh+7, |
|
|
|
1101 strlen(serverh)-6); |
|
|
|
1102 } |
|
|
|
1103 if (!strncmp(clienth, "::ffff:", 7)) { |
|
|
|
1104 memmove(clienth, clienth+7, |
|
|
|
1105 strlen(clienth)-6); |
|
|
|
1106 } |
|
|
|
1107 if (loglvl & CONN) { |
|
|
|
1108 logentry(clienth, clientp, "-", |
|
|
|
1109 "haproxy connection"); |
|
|
|
1110 } |
|
|
|
1111 |
|
|
|
1112 goto read_selector_again; |
|
|
|
1113 } |
|
|
|
1114 |
|
|
|
1115 #ifdef ENABLE_TLS |
|
|
|
1116 if (istls) { |
|
|
|
1117 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, tlssocks) < 0) { |
|
|
|
1118 perror("tls_socketpair"); |
|
|
|
1119 return 1; |
|
|
|
1120 } |
|
|
|
1121 |
|
|
|
1122 switch(fork()) { |
|
|
|
1123 case 0: |
|
|
|
1124 sock = tlssocks[1]; |
|
|
|
1125 close(tlssocks[0]); |
|
|
|
1126 break; |
|
|
|
1127 case -1: |
|
|
|
1128 perror("fork"); |
|
|
|
1129 return 1; |
|
|
|
1130 default: |
|
|
|
1131 tlsclientreader = 1; |
|
|
|
1132 switch(fork()) { |
|
|
|
1133 case 0: |
|
|
|
1134 break; |
|
|
|
1135 case -1: |
|
|
|
1136 perror("fork"); |
|
|
|
1137 return 1; |
|
|
|
1138 default: |
|
|
|
1139 tlsclientreader = 0; |
|
|
|
1140 } |
|
|
|
1141 |
|
|
|
1142 close(tlssocks[tlsclientreader? 1 : 0]); |
|
|
|
1143 do { |
|
|
|
1144 if (tlsclientreader) { |
|
|
|
1145 shuflen = read(tlssocks[0], |
|
|
|
1146 shufbuf, |
|
|
|
1147 sizeof(shufbuf)-1); |
|
|
|
1148 } else { |
|
|
|
1149 shuflen = tls_read(tlsclientctx, |
|
|
|
1150 shufbuf, |
|
|
|
1151 sizeof(shufbuf)-1); |
|
|
|
1152 if (shuflen == TLS_WANT_POLLIN \ |
|
|
|
1153 || shuflen == TLS_WANT_POLLOUT) { |
|
|
|
1154 continue; |
|
|
|
1155 } |
|
|
|
1156 } |
|
|
|
1157 if (shuflen == -1 && errno == EINTR) |
|
|
|
1158 continue; |
|
|
|
1159 for (shufpos = 0; shufpos < shuflen; |
|
|
|
1160 shufpos += wlen) { |
|
|
|
1161 if (tlsclientreader) { |
|
|
|
1162 wlen = tls_write(tlsclientctx, |
|
|
|
1163 shufbuf+shufpos, |
|
|
|
1164 shuflen-shufpos); |
|
|
|
1165 if (wlen == TLS_WANT_POLLIN |
|
|
|
1166 || wlen == TLS_WANT_POLLOUT) { |
|
|
|
1167 wlen = 0; |
|
|
|
1168 continue; |
|
|
|
1169 } |
|
|
|
1170 if (wlen < 0) { |
|
|
|
1171 fprintf(stderr, |
|
|
|
1172 "tls_write failed: %s\n", |
|
|
|
1173 tls_error(tlsclientctx)); |
|
|
|
1174 return 1; |
|
|
|
1175 } |
|
|
|
1176 } else { |
|
|
|
1177 wlen = write(tlssocks[1], |
|
|
|
1178 shufbuf+shufpos, |
|
|
|
1179 shuflen-shufpos); |
|
|
|
1180 if (wlen < 0) { |
|
|
|
1181 perror("write"); |
|
|
|
1182 return 1; |
|
|
|
1183 } |
|
|
|
1184 } |
|
|
|
1185 } |
|
|
|
1186 } while (shuflen > 0); |
|
|
|
1187 |
|
|
|
1188 if (tlsclientreader) { |
|
|
|
1189 wlen = TLS_WANT_POLLIN; |
|
|
|
1190 while (wlen == TLS_WANT_POLLIN \ |
|
|
|
1191 || wlen == TLS_WANT_POLLOUT) { |
|
|
|
1192 wlen = tls_close(tlsclientctx); |
|
|
|
1193 } |
|
|
|
1194 tls_free(tlsclientctx); |
|
|
|
1195 } |
|
|
|
1196 |
|
|
|
1197 lingersock(tlssocks[tlsclientreader? 0 : 1]); |
|
|
|
1198 shutdown(tlssocks[tlsclientreader? 0 : 1], |
|
|
|
1199 tlsclientreader? SHUT_WR : SHUT_RD); |
|
|
|
1200 close(tlssocks[tlsclientreader? 0 : 1]); |
|
|
|
1201 |
|
|
|
1202 if (tlsclientreader) { |
|
|
|
1203 lingersock(sock); |
|
|
|
1204 close(sock); |
|
|
|
1205 } |
|
|
|
1206 return 0; |
|
|
|
1207 } |
|
|
|
1208 } |
|
|
|
1209 #endif /* ENABLE_TLS */ |
|
|
|
1210 |
|
|
|
1211 handlerequest(sock, recvb, rlen, base, |
|
|
|
1212 (dohaproxy)? serverh : ohost, |
|
|
|
1213 (dohaproxy)? serverp : sport, |
|
|
|
1214 clienth, clientp, serverh, serverp, |
|
|
|
1215 nocgi, istls); |
|
|
|
1216 |
|
|
|
1217 lingersock(sock); |
|
|
|
1218 shutdown(sock, SHUT_RDWR); |
|
|
|
1219 close(sock); |
|
|
|
1220 |
|
|
|
1221 if (loglvl & CONN) { |
|
|
|
1222 logentry(clienth, clientp, "-", |
|
|
|
1223 "disconnected"); |
|
|
|
1224 } |
|
|
|
1225 |
|
|
|
1226 return 0; |
|
|
|
1227 default: |
|
|
|
1228 break; |
|
|
|
1229 } |
|
|
|
1230 close(sock); |
|
|
|
1231 } |
|
|
|
1232 |
|
|
|
1233 if (dosyslog) { |
|
|
|
1234 closelog(); |
|
|
|
1235 } else if (logfile != NULL && glfd != -1) { |
|
|
|
1236 close(glfd); |
|
|
|
1237 glfd = -1; |
|
|
|
1238 } |
|
|
|
1239 free(ohost); |
|
|
|
1240 |
|
|
|
1241 for (i = 0; i < nlistfds; i++) { |
|
|
|
1242 shutdown(listfds[i], SHUT_RDWR); |
|
|
|
1243 close(listfds[i]); |
|
|
|
1244 } |
|
|
|
1245 free(listfds); |
|
|
|
1246 |
|
|
|
1247 #ifdef ENABLE_TLS |
|
|
|
1248 if (dotls) { |
|
|
|
1249 tls_close(tlsctx); |
|
|
|
1250 tls_free(tlsctx); |
|
|
|
1251 tls_config_free(tlsconfig); |
|
|
|
1252 } |
|
|
|
1253 #endif /* ENABLE_TLS */ |
|
|
|
1254 |
|
|
|
1255 return 0; |
|
|
|
1256 } |
|
|
|
1257 |
|