Coverage Report

Created: 2026-04-29 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}