/src/wpantund/src/util/socket-utils.c
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (c) 2016 Nest Labs, Inc. |
4 | | * All rights reserved. |
5 | | * |
6 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | | * you may not use this file except in compliance with the License. |
8 | | * You may obtain a copy of the License at |
9 | | * |
10 | | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | | * |
12 | | * Unless required by applicable law or agreed to in writing, software |
13 | | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | | * See the License for the specific language governing permissions and |
16 | | * limitations under the License. |
17 | | * |
18 | | */ |
19 | | |
20 | | #if HAVE_CONFIG_H |
21 | | #include <config.h> |
22 | | #endif |
23 | | |
24 | | #define _GNU_SOURCE 1 |
25 | | |
26 | | #include "assert-macros.h" |
27 | | |
28 | | #include <stdio.h> |
29 | | #include "socket-utils.h" |
30 | | #include "string-utils.h" |
31 | | #include <ctype.h> |
32 | | #include <syslog.h> |
33 | | #include <errno.h> |
34 | | #include <string.h> |
35 | | #include <stdlib.h> |
36 | | #include <unistd.h> |
37 | | #include <termios.h> |
38 | | #include <fcntl.h> |
39 | | #include <arpa/inet.h> |
40 | | #include <sys/socket.h> |
41 | | #include <sys/types.h> |
42 | | #include <netinet/in.h> |
43 | | #include <sys/ioctl.h> |
44 | | #include <ctype.h> |
45 | | #include <signal.h> |
46 | | |
47 | | #if HAVE_SYS_UN_H |
48 | | #include <sys/un.h> |
49 | | #endif |
50 | | |
51 | | #if HAVE_SYS_WAIT_H |
52 | | #include <sys/wait.h> |
53 | | #endif |
54 | | |
55 | | #if HAVE_PTY_H |
56 | | #include <pty.h> |
57 | | #endif |
58 | | |
59 | | #if HAVE_UTIL_H |
60 | | #include <util.h> |
61 | | #endif |
62 | | |
63 | | #if HAVE_SYS_PRCTL_H |
64 | | #include <sys/prctl.h> |
65 | | #endif |
66 | | |
67 | | |
68 | | #if !defined(HAVE_PTSNAME) && __APPLE__ |
69 | | #define HAVE_PTSNAME 1 |
70 | | #endif |
71 | | |
72 | | #if !HAVE_GETDTABLESIZE |
73 | | #define getdtablesize() (int)sysconf(_SC_OPEN_MAX) |
74 | | #endif |
75 | | |
76 | | #ifndef O_NONBLOCK |
77 | | #define O_NONBLOCK O_NDELAY |
78 | | #endif |
79 | | |
80 | | #include <poll.h> |
81 | | |
82 | | static struct { |
83 | | int fd; |
84 | | pid_t pid; |
85 | | } gSystemSocketTable[5]; |
86 | | |
87 | | static void |
88 | | system_socket_table_close_alarm_(int sig) |
89 | 0 | { |
90 | 0 | static const char message[] = "\nclose_super_socket: Unable to terminate child in a timely manner, watchdog fired\n"; |
91 | | |
92 | | // Can't use syslog here, write to stderr instead. |
93 | 0 | (void)write(STDERR_FILENO, message, sizeof(message) - 1); |
94 | |
|
95 | 0 | _exit(EXIT_FAILURE); |
96 | 0 | } |
97 | | |
98 | | static void |
99 | | system_socket_table_atexit_(void) |
100 | 1 | { |
101 | 1 | int i; |
102 | | |
103 | 6 | for (i = 0; i < sizeof(gSystemSocketTable)/sizeof(gSystemSocketTable[0]); i++) { |
104 | 5 | if (gSystemSocketTable[i].pid != 0) { |
105 | 0 | kill(gSystemSocketTable[i].pid, SIGTERM); |
106 | 0 | } |
107 | 5 | } |
108 | 1 | } |
109 | | |
110 | | static void |
111 | | system_socket_table_add_(int fd, pid_t pid) |
112 | 681 | { |
113 | 681 | static bool did_init = false; |
114 | 681 | int i; |
115 | | |
116 | 681 | if (!did_init) { |
117 | 1 | atexit(&system_socket_table_atexit_); |
118 | 1 | did_init = true; |
119 | 1 | } |
120 | | |
121 | 1.00k | for (i = 0; i < sizeof(gSystemSocketTable)/sizeof(gSystemSocketTable[0]); i++) { |
122 | 1.00k | if (gSystemSocketTable[i].pid == 0) { |
123 | 681 | break; |
124 | 681 | } |
125 | 1.00k | } |
126 | | |
127 | | // If we have more than 5 of these types of sockets |
128 | | // open, then there is likely a serious problem going |
129 | | // on that we should immediately deal with. |
130 | 681 | assert(i < sizeof(gSystemSocketTable)/sizeof(gSystemSocketTable[0])); |
131 | | |
132 | 681 | if (i < sizeof(gSystemSocketTable)/sizeof(gSystemSocketTable[0])) { |
133 | 681 | gSystemSocketTable[i].fd = fd; |
134 | 681 | gSystemSocketTable[i].pid = pid; |
135 | 681 | } |
136 | 681 | } |
137 | | |
138 | | int |
139 | | close_super_socket(int fd) |
140 | 12.7k | { |
141 | 12.7k | int i; |
142 | 12.7k | int ret; |
143 | 73.6k | for (i = 0; i < sizeof(gSystemSocketTable)/sizeof(gSystemSocketTable[0]); i++) { |
144 | 61.5k | if ( gSystemSocketTable[i].fd == fd |
145 | 681 | && gSystemSocketTable[i].pid != 0 |
146 | 61.5k | ) { |
147 | 681 | break; |
148 | 681 | } |
149 | 61.5k | } |
150 | | |
151 | 12.7k | ret = close(fd); |
152 | | |
153 | 12.7k | if (i < sizeof(gSystemSocketTable)/sizeof(gSystemSocketTable[0])) { |
154 | 681 | pid_t pid = gSystemSocketTable[i].pid; |
155 | 681 | int x = 0, j = 0; |
156 | | |
157 | 681 | kill(gSystemSocketTable[i].pid, SIGHUP); |
158 | | |
159 | 1.20k | for (j = 0; j < 100; ++j) { |
160 | 1.20k | pid = waitpid(gSystemSocketTable[i].pid, &x, WNOHANG); |
161 | 1.20k | if (pid > 0) { |
162 | 681 | break; |
163 | 681 | } |
164 | 520 | usleep(100000); |
165 | 520 | } |
166 | | |
167 | 681 | if (pid <= 0) { |
168 | | // Five second watchdog. |
169 | 0 | void (*prev_alarm_handler)(int) = signal(SIGALRM, &system_socket_table_close_alarm_); |
170 | 0 | unsigned int prev_alarm_remaining = alarm(5); |
171 | |
|
172 | 0 | syslog(LOG_WARNING, "close_super_socket: PID %d didn't respond to SIGHUP, trying SIGTERM", gSystemSocketTable[i].pid); |
173 | |
|
174 | 0 | kill(gSystemSocketTable[i].pid, SIGTERM); |
175 | |
|
176 | 0 | do { |
177 | 0 | errno = 0; |
178 | 0 | pid = waitpid(gSystemSocketTable[i].pid, &x, 0); |
179 | 0 | } while ((pid == -1) && (errno == EINTR)); |
180 | | |
181 | | // Disarm watchdog. |
182 | 0 | alarm(prev_alarm_remaining); |
183 | 0 | signal(SIGALRM, prev_alarm_handler); |
184 | 0 | } |
185 | | |
186 | 681 | if (pid == -1) { |
187 | 0 | perror(strerror(errno)); |
188 | 0 | } |
189 | | |
190 | | |
191 | 681 | gSystemSocketTable[i].pid = 0; |
192 | 681 | gSystemSocketTable[i].fd = -1; |
193 | 681 | } |
194 | | |
195 | 12.7k | return ret; |
196 | 12.7k | } |
197 | | |
198 | | int |
199 | | checkpoll(int fd, int poll_flags) |
200 | 2.38M | { |
201 | 2.38M | if (fd >= 0) { |
202 | 2.38M | struct pollfd pollfd = { fd, (short)poll_flags, 0 }; |
203 | 2.38M | IGNORE_RETURN_VALUE( poll(&pollfd, 1, 0) ); |
204 | 2.38M | return pollfd.revents; |
205 | 2.38M | } |
206 | | |
207 | 0 | return -1; |
208 | 2.38M | } |
209 | | |
210 | | int |
211 | | fd_has_error(int fd) |
212 | 10.8k | { |
213 | 10.8k | const int flags = (POLLPRI|POLLRDBAND|POLLERR|POLLHUP|POLLNVAL); |
214 | 10.8k | struct pollfd pollfd = { fd, flags, 0 }; |
215 | 10.8k | int count = poll(&pollfd, 1, 0); |
216 | | |
217 | 10.8k | if (count<0) { |
218 | 0 | return -errno; |
219 | 10.8k | } else if (count > 0) { |
220 | 0 | if (pollfd.revents&POLLHUP) { |
221 | 0 | return -EPIPE; |
222 | 0 | } |
223 | | |
224 | 0 | if (pollfd.revents&(POLLRDBAND|POLLPRI)) { |
225 | 0 | return -EPIPE; |
226 | 0 | } |
227 | | |
228 | 0 | if (pollfd.revents&POLLNVAL) { |
229 | 0 | return -EINVAL; |
230 | 0 | } |
231 | | |
232 | 0 | if (pollfd.revents&POLLERR) { |
233 | 0 | return -EIO; |
234 | 0 | } |
235 | 0 | } |
236 | 10.8k | return 0; |
237 | 10.8k | } |
238 | | |
239 | | int gSocketWrapperBaud = 115200; |
240 | | |
241 | | static bool |
242 | | socket_name_is_system_command(const char* socket_name) |
243 | 9.72k | { |
244 | 9.72k | return strhasprefix(socket_name, SOCKET_SYSTEM_COMMAND_PREFIX) |
245 | 9.72k | || strhasprefix(socket_name, SOCKET_SYSTEM_FORKPTY_COMMAND_PREFIX) |
246 | 9.72k | || strhasprefix(socket_name, SOCKET_SYSTEM_SOCKETPAIR_COMMAND_PREFIX) |
247 | 9.72k | ; |
248 | 9.72k | } |
249 | | |
250 | | static bool |
251 | | socket_name_is_port(const char* socket_name) |
252 | 11.3k | { |
253 | | // It's a port if the string is just a number. |
254 | 11.8k | while (*socket_name) { |
255 | 10.2k | if (!isdigit(*socket_name++)) { |
256 | 9.77k | return false; |
257 | 9.77k | } |
258 | 10.2k | } |
259 | | |
260 | 1.60k | return true; |
261 | 11.3k | } |
262 | | |
263 | | static bool |
264 | | socket_name_is_inet(const char* socket_name) |
265 | 19.5k | { |
266 | 19.5k | if (*socket_name == 0) { |
267 | 1 | return false; |
268 | 1 | } |
269 | | // It's an inet address if it Contains no slashes or starts with a '['. |
270 | 19.5k | if (*socket_name == '[') { |
271 | 48 | return true; |
272 | 48 | } |
273 | 40.4k | do { |
274 | 40.4k | if (*socket_name == '/') { |
275 | 17.8k | return false; |
276 | 17.8k | } |
277 | 40.4k | } while (*++socket_name); |
278 | 1.57k | return !socket_name_is_port(socket_name) && !socket_name_is_system_command(socket_name); |
279 | 19.4k | } |
280 | | |
281 | | bool |
282 | | socket_name_is_device(const char* socket_name) |
283 | 9.72k | { |
284 | 9.72k | return !socket_name_is_system_command(socket_name) && !socket_name_is_inet(socket_name); |
285 | 9.72k | } |
286 | | |
287 | | int |
288 | | lookup_sockaddr_from_host_and_port( |
289 | | struct sockaddr_in6* outaddr, const char* host, const char* port |
290 | | ) |
291 | 67 | { |
292 | 67 | int ret = 0; |
293 | 67 | struct addrinfo hint = { |
294 | 67 | }; |
295 | | |
296 | 67 | hint.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED | AI_ALL; |
297 | 67 | hint.ai_family = AF_INET6; |
298 | | |
299 | 67 | struct addrinfo *results = NULL; |
300 | 67 | struct addrinfo *iter = NULL; |
301 | | |
302 | 67 | if (!port) { |
303 | 47 | port = "4951"; |
304 | 47 | } |
305 | | |
306 | 67 | if (!host) { |
307 | 14 | host = "::1"; |
308 | 14 | } |
309 | | |
310 | 67 | syslog(LOG_INFO, "Looking up [%s]:%s", host, port); |
311 | | |
312 | 67 | if (isdigit(port[0]) && strcmp(host, "::1") == 0) { |
313 | | // Special case to avoid calling getaddrinfo() when |
314 | | // we really don't need to. |
315 | 13 | memset(outaddr, 0, sizeof(struct sockaddr_in6)); |
316 | 13 | outaddr->sin6_family = AF_INET6; |
317 | 13 | outaddr->sin6_addr.s6_addr[15] = 1; |
318 | 13 | outaddr->sin6_port = htons(atoi(port)); |
319 | 54 | } else if (isdigit(port[0]) && inet_addr(host) != 0) { |
320 | 51 | in_addr_t v4addr = inet_addr(host); |
321 | 51 | memset(outaddr, 0, sizeof(struct sockaddr_in6)); |
322 | 51 | outaddr->sin6_family = AF_INET6; |
323 | 51 | outaddr->sin6_addr.s6_addr[10] = 0xFF; |
324 | 51 | outaddr->sin6_addr.s6_addr[11] = 0xFF; |
325 | 51 | outaddr->sin6_port = htons(atoi(port)); |
326 | 51 | memcpy(outaddr->sin6_addr.s6_addr + 12, &v4addr, sizeof(v4addr)); |
327 | 51 | outaddr->sin6_port = htons(atoi(port)); |
328 | 51 | } else { |
329 | 3 | int error = getaddrinfo(host, port, &hint, &results); |
330 | | |
331 | 3 | require_action_string( |
332 | 3 | !error, |
333 | 3 | bail, |
334 | 3 | ret = -1, |
335 | 3 | gai_strerror(error) |
336 | 3 | ); |
337 | | |
338 | 0 | for (iter = results; |
339 | 0 | iter && (iter->ai_family != AF_INET6); |
340 | 0 | iter = iter->ai_next) ; |
341 | |
|
342 | 0 | require_action(NULL != iter, bail, ret = -1); |
343 | | |
344 | 0 | memcpy(outaddr, iter->ai_addr, iter->ai_addrlen); |
345 | 0 | } |
346 | | |
347 | 67 | bail: |
348 | 67 | if (results) |
349 | 0 | freeaddrinfo(results); |
350 | | |
351 | 67 | return ret; |
352 | 67 | } |
353 | | |
354 | | #if HAVE_FORKPTY |
355 | | |
356 | | // Declare function to avoid compile warning: implicit declaration of function ‘posix_openpt’ |
357 | | extern int posix_openpt(int flags); |
358 | | |
359 | | static int |
360 | | diagnose_forkpty_problem() |
361 | 0 | { |
362 | 0 | int ret = -1; |
363 | | // COM-S-7530: Do some more diagnostics on the situation, because |
364 | | // sort of failure is weird. We step through some of the same sorts of |
365 | | // things that forkpty would do so that we can see where we are failing. |
366 | 0 | int pty_master_fd = -1; |
367 | 0 | int pty_slave_fd = -1; |
368 | 0 | do { |
369 | 0 | if( access( "/dev/ptmx", F_OK ) < 0 ) { |
370 | 0 | syslog(LOG_WARNING, "Call to access(\"/dev/ptmx\",F_OK) failed: %s (%d)", strerror(errno), errno); |
371 | 0 | perror("access(\"/dev/ptmx\",F_OK)"); |
372 | 0 | } |
373 | |
|
374 | 0 | if( access( "/dev/ptmx", R_OK|W_OK ) < 0 ) { |
375 | 0 | syslog(LOG_WARNING, "Call to access(\"/dev/ptmx\",R_OK|W_OK) failed: %s (%d)", strerror(errno), errno); |
376 | 0 | perror("access(\"/dev/ptmx\",R_OK|W_OK)"); |
377 | 0 | } |
378 | |
|
379 | 0 | pty_master_fd = posix_openpt(O_NOCTTY | O_RDWR); |
380 | |
|
381 | 0 | if (pty_master_fd < 0) { |
382 | 0 | syslog(LOG_CRIT, "Call to posix_openpt() failed: %s (%d)", strerror(errno), errno); |
383 | 0 | perror("posix_openpt(O_NOCTTY | O_RDWR)"); |
384 | 0 | break; |
385 | 0 | } |
386 | | |
387 | 0 | if (grantpt(pty_master_fd) < 0) { |
388 | 0 | syslog(LOG_CRIT, "Call to grantpt() failed: %s (%d)", strerror(errno), errno); |
389 | 0 | perror("grantpt"); |
390 | 0 | } |
391 | |
|
392 | 0 | if (unlockpt(pty_master_fd) < 0) { |
393 | 0 | syslog(LOG_CRIT, "Call to unlockpt() failed: %s (%d)", strerror(errno), errno); |
394 | 0 | perror("unlockpt"); |
395 | 0 | } |
396 | |
|
397 | 0 | #if HAVE_PTSNAME |
398 | 0 | if (NULL == ptsname(pty_master_fd)) { |
399 | 0 | syslog(LOG_CRIT, "Call to ptsname() failed: %s (%d)", strerror(errno), errno); |
400 | 0 | perror("ptsname"); |
401 | 0 | break; |
402 | 0 | } |
403 | | |
404 | 0 | pty_slave_fd = open(ptsname(pty_master_fd), O_RDWR | O_NOCTTY); |
405 | |
|
406 | 0 | if (pty_slave_fd < 0) { |
407 | 0 | syslog(LOG_CRIT, "Call to open(\"%s\",O_RDWR|O_NOCTTY) failed: %s (%d)", ptsname(pty_master_fd), strerror(errno), errno); |
408 | 0 | perror("open(ptsname(pty_master_fd),O_RDWR|O_NOCTTY)"); |
409 | 0 | break; |
410 | 0 | } |
411 | 0 | #endif |
412 | | |
413 | 0 | ret = 0; |
414 | 0 | } while(0); |
415 | 0 | close(pty_master_fd); |
416 | 0 | close(pty_slave_fd); |
417 | 0 | return ret; |
418 | 0 | } |
419 | | |
420 | | static int |
421 | | open_system_socket_forkpty(const char* command) |
422 | 528 | { |
423 | 528 | int ret_fd = -1; |
424 | 528 | int stderr_copy_fd; |
425 | 528 | pid_t pid; |
426 | 528 | struct termios tios = { .c_cflag = CS8|HUPCL|CREAD|CLOCAL }; |
427 | | |
428 | 528 | cfmakeraw(&tios); |
429 | | |
430 | | // Duplicate stderr so that we can hook it back up in the forked process. |
431 | 528 | stderr_copy_fd = dup(STDERR_FILENO); |
432 | 528 | if (stderr_copy_fd < 0) { |
433 | 0 | syslog(LOG_ERR, "Call to dup() failed: %s (%d)", strerror(errno), errno); |
434 | 0 | goto cleanup_and_fail; |
435 | 0 | } |
436 | | |
437 | 528 | pid = forkpty(&ret_fd, NULL, &tios, NULL); |
438 | 528 | if (pid < 0) { |
439 | 0 | syslog(LOG_ERR, "Call to forkpty() failed: %s (%d)", strerror(errno), errno); |
440 | |
|
441 | 0 | if (0 == diagnose_forkpty_problem()) { |
442 | 0 | syslog(LOG_CRIT, "FORKPTY() FAILED BUT NOTHING WAS OBVIOUSLY WRONG!!!"); |
443 | 0 | } |
444 | |
|
445 | 0 | goto cleanup_and_fail; |
446 | 0 | } |
447 | | |
448 | | // Check to see if we are the forked process or not. |
449 | 528 | if (0 == pid) { |
450 | | // We are the forked process. |
451 | 0 | const int dtablesize = getdtablesize(); |
452 | 0 | int i; |
453 | |
|
454 | 0 | #if defined(_LINUX_PRCTL_H) |
455 | 0 | prctl(PR_SET_PDEATHSIG, SIGHUP); |
456 | 0 | #endif |
457 | | |
458 | | // Re-instate our original stderr. |
459 | 0 | dup2(stderr_copy_fd, STDERR_FILENO); |
460 | |
|
461 | 0 | syslog(LOG_DEBUG, "Forked!"); |
462 | | |
463 | | // Re-instate our original stderr (clobbered by login_tty) |
464 | 0 | dup2(stderr_copy_fd, STDERR_FILENO); |
465 | | |
466 | | // Set the shell environment variable if it isn't set already. |
467 | 0 | setenv("SHELL", SOCKET_UTILS_DEFAULT_SHELL, 0); |
468 | | |
469 | | // Close all file descriptors larger than STDERR_FILENO. |
470 | 0 | for (i = (STDERR_FILENO + 1); i < dtablesize; i++) { |
471 | 0 | close(i); |
472 | 0 | } |
473 | |
|
474 | 0 | syslog(LOG_NOTICE,"About to exec \"%s\"",command); |
475 | |
|
476 | 0 | execl(getenv("SHELL"),getenv("SHELL"),"-c",command,NULL); |
477 | |
|
478 | 0 | syslog(LOG_ERR,"Failed for fork and exec of \"%s\": %s (%d)", command, strerror(errno), errno); |
479 | |
|
480 | 0 | _exit(errno); |
481 | 0 | } |
482 | | |
483 | | // Clean up our copy of stderr |
484 | 528 | close(stderr_copy_fd); |
485 | | |
486 | 528 | #if HAVE_PTSNAME |
487 | | // See http://stackoverflow.com/questions/3486491/ |
488 | 528 | close(open(ptsname(ret_fd), O_RDWR | O_NOCTTY)); |
489 | 528 | #endif |
490 | | |
491 | 528 | system_socket_table_add_(ret_fd, pid); |
492 | | |
493 | 528 | return ret_fd; |
494 | | |
495 | 0 | cleanup_and_fail: |
496 | 0 | { |
497 | 0 | int prevErrno = errno; |
498 | |
|
499 | 0 | close(ret_fd); |
500 | 0 | close(stderr_copy_fd); |
501 | |
|
502 | 0 | errno = prevErrno; |
503 | 0 | } |
504 | 0 | return -1; |
505 | 528 | } |
506 | | #endif // HAVE_FORKPTY |
507 | | |
508 | | #ifdef PF_UNIX |
509 | | |
510 | | int |
511 | | fork_unixdomain_socket(int* fd_pointer) |
512 | 208 | { |
513 | 208 | int fd[2] = { -1, -1 }; |
514 | 208 | int i; |
515 | 208 | pid_t pid = -1; |
516 | | |
517 | 208 | if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd) < 0) { |
518 | 0 | syslog(LOG_ERR, "Call to socketpair() failed: %s (%d)", strerror(errno), errno); |
519 | 0 | goto bail; |
520 | 0 | } |
521 | | |
522 | 208 | pid = fork(); |
523 | 208 | if (pid < 0) { |
524 | 0 | syslog(LOG_ERR, "Call to fork() failed: %s (%d)", strerror(errno), errno); |
525 | 0 | goto bail; |
526 | 0 | } |
527 | | |
528 | | // Check to see if we are the forked process or not. |
529 | 208 | if (0 == pid) { |
530 | 0 | const int dtablesize = getdtablesize(); |
531 | | // We are the forked process. |
532 | |
|
533 | 0 | #if defined(_LINUX_PRCTL_H) |
534 | 0 | prctl(PR_SET_PDEATHSIG, SIGHUP); |
535 | 0 | #endif |
536 | |
|
537 | 0 | close(fd[0]); |
538 | |
|
539 | 0 | dup2(fd[1], STDIN_FILENO); |
540 | 0 | dup2(fd[1], STDOUT_FILENO); |
541 | |
|
542 | 0 | syslog(LOG_DEBUG, "Forked!"); |
543 | | |
544 | | // Close all file descriptors larger than STDERR_FILENO. |
545 | 0 | for (i = (STDERR_FILENO + 1); i < dtablesize; i++) { |
546 | 0 | close(i); |
547 | 0 | } |
548 | |
|
549 | 0 | *fd_pointer = STDIN_FILENO; |
550 | 208 | } else { |
551 | 208 | close(fd[1]); |
552 | 208 | *fd_pointer = fd[0]; |
553 | 208 | } |
554 | | |
555 | 208 | fd[0] = -1; |
556 | 208 | fd[1] = -1; |
557 | | |
558 | 208 | bail: |
559 | | |
560 | 208 | { |
561 | 208 | int prevErrno = errno; |
562 | 208 | if (fd[0] >= 0) { |
563 | 0 | close(fd[0]); |
564 | 0 | } |
565 | 208 | if (fd[1] >= 0) { |
566 | 0 | close(fd[1]); |
567 | 0 | } |
568 | 208 | errno = prevErrno; |
569 | 208 | } |
570 | | |
571 | 208 | return pid; |
572 | 208 | } |
573 | | |
574 | | static int |
575 | | open_system_socket_unix_domain(const char* command) |
576 | 153 | { |
577 | 153 | int fd = -1; |
578 | 153 | pid_t pid = -1; |
579 | | |
580 | 153 | pid = fork_unixdomain_socket(&fd); |
581 | | |
582 | 153 | if (pid < 0) { |
583 | 0 | syslog(LOG_ERR, "Call to fork() failed: %s (%d)", strerror(errno), errno); |
584 | 0 | goto cleanup_and_fail; |
585 | 0 | } |
586 | | |
587 | | // Check to see if we are the forked process or not. |
588 | 153 | if (0 == pid) { |
589 | | // Set the shell environment variable if it isn't set already. |
590 | 0 | setenv("SHELL","/bin/sh",0); |
591 | |
|
592 | 0 | syslog(LOG_NOTICE, "About to exec \"%s\"", command); |
593 | |
|
594 | 0 | execl(getenv("SHELL"), getenv("SHELL"),"-c", command, NULL); |
595 | |
|
596 | 0 | syslog(LOG_ERR, "Failed for fork and exec of \"%s\": %s (%d)", command, strerror(errno), errno); |
597 | |
|
598 | 0 | _exit(EXIT_FAILURE); |
599 | 0 | } |
600 | | |
601 | 153 | system_socket_table_add_(fd, pid); |
602 | | |
603 | 153 | return fd; |
604 | | |
605 | 0 | cleanup_and_fail: |
606 | |
|
607 | 0 | if (fd >= 0) { |
608 | 0 | int prevErrno = errno; |
609 | |
|
610 | 0 | close(fd); |
611 | |
|
612 | 0 | errno = prevErrno; |
613 | 0 | } |
614 | |
|
615 | 0 | return -1; |
616 | 153 | } |
617 | | #endif // PF_UNIX |
618 | | |
619 | | static int |
620 | | open_system_socket(const char* command) |
621 | 510 | { |
622 | 510 | int ret_fd = -1; |
623 | | |
624 | 510 | #if HAVE_FORKPTY |
625 | 510 | ret_fd = open_system_socket_forkpty(command); |
626 | 510 | #endif |
627 | | |
628 | 510 | #if defined(PF_UNIX) |
629 | | // Fall back to unix-domain socket-based mechanism: |
630 | 510 | if (ret_fd < 0) { |
631 | 0 | ret_fd = open_system_socket_unix_domain(command); |
632 | 0 | } |
633 | 510 | #endif |
634 | | |
635 | | #if !HAVE_FORKPTY && !defined(PF_UNIX) |
636 | | assert_printf("%s","Process pipe sockets are not supported in current configuration"); |
637 | | errno = ENOTSUP; |
638 | | #endif |
639 | | |
640 | 510 | return ret_fd; |
641 | 510 | } |
642 | | |
643 | | int |
644 | | get_super_socket_type_from_path(const char* socket_name) |
645 | 25.6k | { |
646 | 25.6k | int socket_type = SUPER_SOCKET_TYPE_UNKNOWN; |
647 | | |
648 | 25.6k | if (strcasehasprefix(socket_name, SOCKET_SYSTEM_COMMAND_PREFIX)) { |
649 | 1.02k | socket_type = SUPER_SOCKET_TYPE_SYSTEM; |
650 | 24.6k | } else if (strcasehasprefix(socket_name, SOCKET_SYSTEM_FORKPTY_COMMAND_PREFIX)) { |
651 | 36 | socket_type = SUPER_SOCKET_TYPE_SYSTEM_FORKPTY; |
652 | 24.5k | } else if (strcasehasprefix(socket_name, SOCKET_SYSTEM_SOCKETPAIR_COMMAND_PREFIX)) { |
653 | 306 | socket_type = SUPER_SOCKET_TYPE_SYSTEM_SOCKETPAIR; |
654 | 24.2k | } else if (strcasehasprefix(socket_name, SOCKET_FD_COMMAND_PREFIX)) { |
655 | 13.8k | socket_type = SUPER_SOCKET_TYPE_FD; |
656 | 13.8k | } else if (strcasehasprefix(socket_name, SOCKET_FILE_COMMAND_PREFIX)) { |
657 | 662 | socket_type = SUPER_SOCKET_TYPE_DEVICE; |
658 | 9.82k | } else if (strcasehasprefix(socket_name, SOCKET_SERIAL_COMMAND_PREFIX)) { |
659 | 40 | socket_type = SUPER_SOCKET_TYPE_DEVICE; |
660 | 9.78k | } else if (strcasehasprefix(socket_name, SOCKET_TCP_COMMAND_PREFIX)) { |
661 | 5 | socket_type = SUPER_SOCKET_TYPE_TCP; |
662 | 9.78k | } else if (socket_name_is_inet(socket_name) || socket_name_is_port(socket_name)) { |
663 | 62 | socket_type = SUPER_SOCKET_TYPE_TCP; |
664 | 9.72k | } else if (socket_name_is_device(socket_name)) { |
665 | 9.72k | socket_type = SUPER_SOCKET_TYPE_DEVICE; |
666 | 9.72k | } |
667 | 25.6k | return socket_type; |
668 | 25.6k | } |
669 | | |
670 | | int |
671 | | baud_rate_to_termios_constant(int baud) |
672 | 9.25k | { |
673 | 9.25k | int ret; |
674 | | // Standard "TERMIOS" uses constants to get and set |
675 | | // the baud rate, but we use the actual baud rate. |
676 | | // This function converts from the actual baud rate |
677 | | // to the constant supported by this platform. It |
678 | | // returns zero if the baud rate is unsupported. |
679 | 9.25k | switch(baud) { |
680 | 152 | case 9600: |
681 | 152 | ret = B9600; |
682 | 152 | break; |
683 | 190 | case 19200: |
684 | 190 | ret = B19200; |
685 | 190 | break; |
686 | 181 | case 38400: |
687 | 181 | ret = B38400; |
688 | 181 | break; |
689 | 316 | case 57600: |
690 | 316 | ret = B57600; |
691 | 316 | break; |
692 | 2.01k | case 115200: |
693 | 2.01k | ret = B115200; |
694 | 2.01k | break; |
695 | 0 | #ifdef B230400 |
696 | 447 | case 230400: |
697 | 447 | ret = B230400; |
698 | 447 | break; |
699 | 0 | #endif |
700 | 0 | #ifdef B460800 |
701 | 260 | case 460800: |
702 | 260 | ret = B460800; |
703 | 260 | break; |
704 | 0 | #endif |
705 | 0 | #ifdef B500000 |
706 | 121 | case 500000: |
707 | 121 | ret = B500000; |
708 | 121 | break; |
709 | 0 | #endif |
710 | 0 | #ifdef B576000 |
711 | 316 | case 576000: |
712 | 316 | ret = B576000; |
713 | 316 | break; |
714 | 0 | #endif |
715 | 0 | #ifdef B921600 |
716 | 652 | case 921600: |
717 | 652 | ret = B921600; |
718 | 652 | break; |
719 | 0 | #endif |
720 | 0 | #ifdef B1000000 |
721 | 77 | case 1000000: |
722 | 77 | ret = B1000000; |
723 | 77 | break; |
724 | 0 | #endif |
725 | 4.52k | default: |
726 | 4.52k | ret = 0; |
727 | 4.52k | break; |
728 | 9.25k | } |
729 | 9.25k | return ret; |
730 | 9.25k | } |
731 | | |
732 | | int |
733 | | open_super_socket(const char* socket_name) |
734 | 12.8k | { |
735 | 12.8k | int fd = -1; |
736 | 12.8k | char* host = NULL; // Needs to be freed if set |
737 | 12.8k | const char* port = NULL; |
738 | 12.8k | char* options = strchr(socket_name, ','); |
739 | 12.8k | char* filename = strchr(socket_name, ':'); |
740 | 12.8k | bool socket_name_is_well_formed = true; // True if socket has type name and options |
741 | 12.8k | int socket_type = get_super_socket_type_from_path(socket_name); |
742 | | |
743 | | // Move past the colon, if there was one. |
744 | 12.8k | if (NULL != filename && socket_name[0] != '[') { |
745 | 8.07k | filename++; |
746 | 8.07k | } else { |
747 | 4.78k | filename = ""; |
748 | 4.78k | } |
749 | | |
750 | 12.8k | if (socket_type == SUPER_SOCKET_TYPE_DEVICE) { |
751 | 5.21k | socket_name_is_well_formed = |
752 | 5.21k | (strcasehasprefix(socket_name, SOCKET_SERIAL_COMMAND_PREFIX)) |
753 | 5.19k | || (strcasehasprefix(socket_name, SOCKET_FILE_COMMAND_PREFIX)); |
754 | 5.21k | } |
755 | | |
756 | 12.8k | if (socket_type == SUPER_SOCKET_TYPE_TCP) { |
757 | 67 | socket_name_is_well_formed = |
758 | 67 | (strcasehasprefix(socket_name, SOCKET_TCP_COMMAND_PREFIX)); |
759 | 67 | if (!socket_name_is_well_formed) { |
760 | 62 | filename = (char*)socket_name; |
761 | 62 | } |
762 | 67 | } |
763 | | |
764 | 12.8k | if (!socket_name_is_well_formed) { |
765 | 4.92k | filename = (char*)socket_name; |
766 | 4.92k | if (socket_type == SUPER_SOCKET_TYPE_DEVICE) { |
767 | 4.86k | options = ",default"; |
768 | 4.86k | } else { |
769 | 62 | options = NULL; |
770 | 62 | } |
771 | 4.92k | } |
772 | | |
773 | 12.8k | filename = strdup(filename); |
774 | | |
775 | 12.8k | if (NULL == filename) { |
776 | 0 | perror("strdup"); |
777 | 0 | syslog(LOG_ERR, "strdup failed. \"%s\" (%d)", strerror(errno), errno); |
778 | 0 | goto bail; |
779 | 0 | } |
780 | | |
781 | | // Make sure we zero terminate the file name before |
782 | | // any options. (this is why we needed to strdup) |
783 | 12.8k | if (socket_name_is_well_formed && (NULL != options)) { |
784 | 721 | const char* ptr = strchr(socket_name, ':'); |
785 | 721 | if (NULL == ptr) { |
786 | 0 | ptr = socket_name; |
787 | 721 | } else { |
788 | 721 | ptr++; |
789 | 721 | } |
790 | 721 | filename[options-ptr] = 0; |
791 | 721 | } |
792 | | |
793 | 12.8k | if (SUPER_SOCKET_TYPE_SYSTEM == socket_type) { |
794 | 510 | fd = open_system_socket(filename); |
795 | 510 | #if HAVE_FORKPTY |
796 | 12.3k | } else if (SUPER_SOCKET_TYPE_SYSTEM_FORKPTY == socket_type) { |
797 | 18 | fd = open_system_socket_forkpty(filename); |
798 | 18 | #endif |
799 | 12.3k | } else if (SUPER_SOCKET_TYPE_SYSTEM_SOCKETPAIR == socket_type) { |
800 | 153 | fd = open_system_socket_unix_domain(filename); |
801 | 12.1k | } else if (SUPER_SOCKET_TYPE_FD == socket_type) { |
802 | 6.90k | errno = 0; |
803 | 6.90k | fd = strtol(filename, NULL, 0); |
804 | 6.90k | if (fd == 0 && errno != 0) { |
805 | 0 | perror("strtol"); |
806 | 0 | syslog(LOG_ERR, "strtol failed. \"%s\" (%d)", strerror(errno), errno); |
807 | 0 | fd = -1; |
808 | 0 | goto bail; |
809 | 0 | } |
810 | 6.90k | #if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
811 | 6.90k | switch(fd) { |
812 | 1 | case STDERR_FILENO: |
813 | 3 | case STDIN_FILENO: |
814 | 4 | case STDOUT_FILENO: |
815 | | // Don't mess up the fuzzer's standard input/output streams. |
816 | 4 | fd = -1; |
817 | 4 | goto bail; |
818 | 6.89k | default: |
819 | 6.89k | break; |
820 | 6.90k | } |
821 | 6.89k | #endif |
822 | 6.89k | fd = dup(fd); |
823 | 6.89k | } else if (SUPER_SOCKET_TYPE_DEVICE == socket_type) { |
824 | 5.21k | #if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
825 | | // Don't let the fuzzer go crazy on files. |
826 | 5.21k | free(filename); |
827 | 5.21k | filename = strdup("/dev/null"); |
828 | 5.21k | #endif |
829 | 5.21k | fd = open(filename, O_RDWR | O_NOCTTY | O_NONBLOCK); |
830 | | |
831 | 5.21k | if (fd >= 0) { |
832 | 5.21k | fcntl(fd, F_SETFL, O_NONBLOCK); |
833 | 5.21k | tcflush(fd, TCIOFLUSH); |
834 | 5.21k | } |
835 | 5.21k | } else if (SUPER_SOCKET_TYPE_TCP == socket_type) { |
836 | 67 | struct sockaddr_in6 addr; |
837 | | |
838 | 67 | if (socket_name_is_port(socket_name)) { |
839 | 14 | port = socket_name; |
840 | 53 | } else { |
841 | 53 | ssize_t i; |
842 | 53 | if (filename[0] == '[') { |
843 | 48 | host = strdup(filename + 1); |
844 | 48 | } else { |
845 | 5 | host = strdup(filename); |
846 | 5 | } |
847 | 444 | for (i = strlen(host) - 1; i >= 0; --i) { |
848 | 420 | if (host[i] == ':' && port == NULL) { |
849 | 6 | host[i] = 0; |
850 | 6 | port = host + i + 1; |
851 | 6 | continue; |
852 | 6 | } |
853 | 414 | if (host[i] == ']') { |
854 | 1 | host[i] = 0; |
855 | 1 | break; |
856 | 1 | } |
857 | 413 | if (!isdigit(host[i])) |
858 | 28 | break; |
859 | 413 | } |
860 | 53 | } |
861 | | |
862 | 67 | if (0 != lookup_sockaddr_from_host_and_port(&addr, host, port)) { |
863 | 3 | syslog(LOG_ERR, "Unable to lookup \"%s\"", socket_name); |
864 | 3 | goto bail; |
865 | 3 | } |
866 | | |
867 | 64 | fd = socket(AF_INET6, SOCK_STREAM, 0); |
868 | | |
869 | 64 | if (fd < 0) { |
870 | 0 | syslog(LOG_ERR, "Unable to open socket. \"%s\" (%d)", strerror(errno), errno); |
871 | 0 | goto bail; |
872 | 0 | } |
873 | | |
874 | 64 | if (0 != connect(fd, (struct sockaddr*)&addr, sizeof(addr))) { |
875 | 64 | syslog(LOG_ERR, "Call to connect() failed. \"%s\" (%d)", strerror(errno), errno); |
876 | 64 | close(fd); |
877 | 64 | fd = -1; |
878 | 64 | goto bail; |
879 | 64 | } |
880 | 64 | } else { |
881 | 0 | syslog(LOG_ERR, "I don't know how to open \"%s\" (socket type %d)", socket_name, (int)socket_type); |
882 | 0 | } |
883 | | |
884 | 12.7k | if (fd < 0) { |
885 | 0 | syslog(LOG_ERR, "Unable to open socket. \"%s\" (%d) (filename = %s, type = %d)", strerror(errno), errno, filename, (int)socket_type); |
886 | 0 | goto bail; |
887 | 0 | } |
888 | | |
889 | | // Configure the socket. |
890 | 12.7k | { |
891 | 12.7k | int flags = fcntl(fd, F_GETFL); |
892 | 12.7k | int i; |
893 | 12.7k | struct termios tios; |
894 | 12.7k | fcntl(fd, F_SETFL, flags | O_NONBLOCK); |
895 | | |
896 | | #ifdef SO_NOSIGPIPE |
897 | | int set = 0; |
898 | | setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); |
899 | | #endif |
900 | | |
901 | 12.9k | #define FETCH_TERMIOS() do { if(0 != tcgetattr(fd, &tios)) { \ |
902 | 7.40k | syslog(LOG_DEBUG, "tcgetattr() failed. \"%s\" (%d)", strerror(errno), errno); \ |
903 | 12.9k | } } while(0) |
904 | | |
905 | 12.9k | #define COMMIT_TERMIOS() do { if(0 != tcsetattr(fd, TCSANOW, &tios)) { \ |
906 | 7.40k | syslog(LOG_DEBUG, "tcsetattr() failed. \"%s\" (%d)", strerror(errno), errno); \ |
907 | 12.9k | } } while(0) |
908 | | |
909 | | // Parse the options, if any |
910 | 29.4k | for (; NULL != options; options = strchr(options+1, ',')) { |
911 | 16.6k | if (strcasehasprefix(options, ",b") && isdigit(options[2])) { |
912 | | // Change Baud rate |
913 | 2.84k | const int baud = (int)strtol(options+2,NULL,10); |
914 | 2.84k | const int baud_opt = baud_rate_to_termios_constant(baud); |
915 | 2.84k | if(baud_opt <= 0) { |
916 | 1.47k | syslog(LOG_INFO, "Unknown baud rate %d - using default", baud); |
917 | 1.47k | } else { |
918 | 1.37k | syslog(LOG_DEBUG, "Setting baud rate to %d", baud); |
919 | 1.37k | } |
920 | 2.84k | FETCH_TERMIOS(); |
921 | 2.84k | cfsetspeed(&tios, baud_opt); |
922 | 2.84k | COMMIT_TERMIOS(); |
923 | 13.8k | } else if (strcasehasprefix(options, ",default")) { |
924 | 6.40k | FETCH_TERMIOS(); |
925 | 211k | for (i=0; i < NCCS; i++) { |
926 | 205k | tios.c_cc[i] = _POSIX_VDISABLE; |
927 | 205k | } |
928 | | |
929 | 6.40k | tios.c_cflag = (CS8|HUPCL|CREAD|CLOCAL); |
930 | 6.40k | tios.c_iflag = 0; |
931 | 6.40k | tios.c_oflag = 0; |
932 | 6.40k | tios.c_lflag = 0; |
933 | | |
934 | 6.40k | const int baud_opt = baud_rate_to_termios_constant(gSocketWrapperBaud); |
935 | 6.40k | if(baud_opt <= 0) { |
936 | 3.05k | syslog(LOG_INFO, "Unknown baud rate %d - using default", gSocketWrapperBaud); |
937 | 3.35k | } else { |
938 | 3.35k | syslog(LOG_DEBUG, "Setting baud rate to %d", gSocketWrapperBaud); |
939 | 3.35k | } |
940 | | |
941 | 6.40k | cfmakeraw(&tios); |
942 | 6.40k | cfsetspeed(&tios, baud_opt); |
943 | 6.40k | COMMIT_TERMIOS(); |
944 | 7.40k | } else if (strcasehasprefix(options, ",raw")) { |
945 | | // Raw mode |
946 | 142 | FETCH_TERMIOS(); |
947 | 142 | cfmakeraw(&tios); |
948 | 142 | COMMIT_TERMIOS(); |
949 | 7.26k | } else if (strcasehasprefix(options, ",clocal=")) { |
950 | 491 | FETCH_TERMIOS(); |
951 | 491 | options = strchr(options,'='); |
952 | 491 | if (options[1] == '1') { |
953 | 104 | tios.c_cflag |= CLOCAL; |
954 | 387 | } else if (options[1] == '0') { |
955 | 203 | tios.c_cflag &= ~CLOCAL; |
956 | 203 | } |
957 | 491 | COMMIT_TERMIOS(); |
958 | 6.76k | } else if (strcasehasprefix(options, ",ixoff=")) { |
959 | 553 | FETCH_TERMIOS(); |
960 | 553 | options = strchr(options,'='); |
961 | 553 | if (options[1] == '1') { |
962 | 34 | tios.c_iflag |= IXOFF; |
963 | 519 | } else if (options[1] == '0') { |
964 | 168 | tios.c_iflag &= ~IXOFF; |
965 | 168 | } |
966 | 553 | COMMIT_TERMIOS(); |
967 | 6.21k | } else if (strcasehasprefix(options, ",ixon=")) { |
968 | 908 | FETCH_TERMIOS(); |
969 | 908 | options = strchr(options,'='); |
970 | 908 | if (options[1] == '1') { |
971 | 363 | tios.c_iflag |= IXON; |
972 | 545 | } else if (options[1] == '0') { |
973 | 229 | tios.c_iflag &= ~IXON; |
974 | 229 | } |
975 | 908 | COMMIT_TERMIOS(); |
976 | 5.30k | } else if (strcasehasprefix(options, ",ixany=")) { |
977 | 796 | FETCH_TERMIOS(); |
978 | 796 | options = strchr(options,'='); |
979 | 796 | if (options[1] == '1') { |
980 | 313 | tios.c_iflag |= IXANY; |
981 | 483 | } else if (options[1] == '0') { |
982 | 73 | tios.c_iflag &= ~IXANY; |
983 | 73 | } |
984 | 796 | COMMIT_TERMIOS(); |
985 | 4.51k | } else if (strcasehasprefix(options, ",crtscts=")) { |
986 | 838 | FETCH_TERMIOS(); |
987 | 838 | options = strchr(options,'='); |
988 | | // Hardware flow control |
989 | 838 | if (options[1] == '1') { |
990 | 187 | syslog(LOG_DEBUG, "Using hardware flow control for serial socket."); |
991 | 187 | tios.c_cflag |= CRTSCTS; |
992 | 651 | } else if (options[1] == '0') { |
993 | 486 | tios.c_cflag &= ~CRTSCTS; |
994 | 486 | } |
995 | 838 | COMMIT_TERMIOS(); |
996 | | |
997 | | #ifdef CCTS_OFLOW |
998 | | } else if (strcasehasprefix(options, ",ccts_oflow=")) { |
999 | | FETCH_TERMIOS(); |
1000 | | options = strchr(options,'='); |
1001 | | // Hardware output flow control |
1002 | | if (options[1] == '1') { |
1003 | | syslog(LOG_DEBUG, "Using hardware output flow control for serial socket."); |
1004 | | tios.c_cflag |= CCTS_OFLOW; |
1005 | | } else if (options[1] == '0') { |
1006 | | tios.c_cflag &= ~CCTS_OFLOW; |
1007 | | } |
1008 | | COMMIT_TERMIOS(); |
1009 | | #endif |
1010 | | #ifdef CRTS_IFLOW |
1011 | | } else if (strcasehasprefix(options, ",crts_iflow=")) { |
1012 | | FETCH_TERMIOS(); |
1013 | | options = strchr(options,'='); |
1014 | | // Hardware input flow control |
1015 | | if (options[1] == '1') { |
1016 | | syslog(LOG_DEBUG, "Using hardware input flow control for serial socket."); |
1017 | | tios.c_cflag |= CRTS_IFLOW; |
1018 | | } else if (options[1] == '0') { |
1019 | | tios.c_cflag &= ~CRTS_IFLOW; |
1020 | | } |
1021 | | COMMIT_TERMIOS(); |
1022 | | #endif |
1023 | 3.67k | } else { |
1024 | 3.67k | syslog(LOG_ERR, "Unknown option (%s)", options); |
1025 | 3.67k | } |
1026 | 16.6k | } |
1027 | 12.7k | } |
1028 | 12.8k | bail: |
1029 | 12.8k | free(filename); |
1030 | 12.8k | free(host); |
1031 | 12.8k | return fd; |
1032 | 12.7k | } |