Coverage Report

Created: 2025-07-18 06:52

/src/dropbear/src/netio.c
Line
Count
Source (jump to first uncovered line)
1
#include "netio.h"
2
#include "list.h"
3
#include "dbutil.h"
4
#include "session.h"
5
#include "debug.h"
6
#include "runopts.h"
7
8
struct dropbear_progress_connection {
9
  struct addrinfo *res;
10
  struct addrinfo *res_iter;
11
12
  char *remotehost, *remoteport; /* For error reporting */
13
14
  connect_callback cb;
15
  void *cb_data;
16
17
  struct Queue *writequeue; /* A queue of encrypted packets to send with TCP fastopen,
18
                or NULL. */
19
20
  int sock;
21
22
  char* errstring;
23
  char *bind_address, *bind_port;
24
  enum dropbear_prio prio;
25
};
26
27
/* Deallocate a progress connection. Removes from the pending list if iter!=NULL.
28
Does not close sockets */
29
0
static void remove_connect(struct dropbear_progress_connection *c, m_list_elem *iter) {
30
0
  if (c->res) {
31
    /* Only call freeaddrinfo if connection is not AF_UNIX. */
32
0
    if (c->res->ai_family != AF_UNIX) {
33
0
      freeaddrinfo(c->res);
34
0
    } else {
35
0
      m_free(c->res);
36
0
    }
37
0
  }
38
0
  m_free(c->remotehost);
39
0
  m_free(c->remoteport);
40
0
  m_free(c->errstring);
41
0
  m_free(c->bind_address);
42
0
  m_free(c->bind_port);
43
0
  m_free(c);
44
45
0
  if (iter) {
46
0
    list_remove(iter);
47
0
  }
48
0
}
49
50
0
static void cancel_callback(int result, int sock, void* UNUSED(data), const char* UNUSED(errstring)) {
51
0
  if (result == DROPBEAR_SUCCESS)
52
0
  {
53
0
    m_close(sock);
54
0
  }
55
0
}
56
57
0
void cancel_connect(struct dropbear_progress_connection *c) {
58
0
  c->cb = cancel_callback;
59
0
  c->cb_data = NULL;
60
0
}
61
62
0
static void connect_try_next(struct dropbear_progress_connection *c) {
63
0
  struct addrinfo *r;
64
0
  int err;
65
0
  int res = 0;
66
0
  int fastopen = 0;
67
0
  int retry_errno = EINPROGRESS;
68
#if DROPBEAR_CLIENT_TCP_FAST_OPEN
69
  struct msghdr message;
70
#endif
71
72
0
  for (r = c->res_iter; r; r = r->ai_next)
73
0
  {
74
0
    dropbear_assert(c->sock == -1);
75
76
0
    c->sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
77
0
    if (c->sock < 0) {
78
0
      continue;
79
0
    }
80
81
    /* According to the connect(2) manpage it should be testing EAGAIN
82
     * rather than EINPROGRESS for unix sockets.
83
     */
84
0
    retry_errno = r->ai_family == AF_UNIX ? EAGAIN : EINPROGRESS;
85
86
0
    if (c->bind_address || c->bind_port) {
87
      /* bind to a source port/address */
88
0
      struct addrinfo hints;
89
0
      struct addrinfo *bindaddr = NULL;
90
0
      memset(&hints, 0, sizeof(hints));
91
0
      hints.ai_socktype = SOCK_STREAM;
92
0
      hints.ai_family = r->ai_family;
93
0
      hints.ai_flags = AI_PASSIVE;
94
95
0
      err = getaddrinfo(c->bind_address, c->bind_port, &hints, &bindaddr);
96
0
      if (err) {
97
0
        int len = 100 + strlen(gai_strerror(err));
98
0
        m_free(c->errstring);
99
0
        c->errstring = (char*)m_malloc(len);
100
0
        snprintf(c->errstring, len, "Error resolving bind address '%s' (port %s). %s", 
101
0
            c->bind_address, c->bind_port, gai_strerror(err));
102
0
        TRACE(("Error resolving bind: %s", gai_strerror(err)))
103
0
        close(c->sock);
104
0
        c->sock = -1;
105
0
        continue;
106
0
      }
107
0
      res = bind(c->sock, bindaddr->ai_addr, bindaddr->ai_addrlen);
108
0
      freeaddrinfo(bindaddr);
109
0
      bindaddr = NULL;
110
0
      if (res < 0) {
111
        /* failure */
112
0
        int keep_errno = errno;
113
0
        int len = 300;
114
0
        m_free(c->errstring);
115
0
        c->errstring = m_malloc(len);
116
0
        snprintf(c->errstring, len, "Error binding local address '%s' (port %s). %s", 
117
0
            c->bind_address, c->bind_port, strerror(keep_errno));
118
0
        close(c->sock);
119
0
        c->sock = -1;
120
0
        continue;
121
0
      }
122
0
    }
123
124
0
    ses.maxfd = MAX(ses.maxfd, c->sock);
125
0
    set_sock_nodelay(c->sock);
126
0
    set_sock_priority(c->sock, c->prio);
127
0
    setnonblocking(c->sock);
128
129
#if DROPBEAR_CLIENT_TCP_FAST_OPEN
130
    fastopen = (c->writequeue != NULL && r->ai_family != AF_UNIX);
131
132
    if (fastopen) {
133
      memset(&message, 0x0, sizeof(message));
134
      message.msg_name = r->ai_addr;
135
      message.msg_namelen = r->ai_addrlen;
136
      /* 6 is arbitrary, enough to hold initial packets */
137
      unsigned int iovlen = 6; /* Linux msg_iovlen is a size_t */
138
      struct iovec iov[6];
139
      packet_queue_to_iovec(c->writequeue, iov, &iovlen);
140
      message.msg_iov = iov;
141
      message.msg_iovlen = iovlen;
142
      res = sendmsg(c->sock, &message, MSG_FASTOPEN);
143
      /* Returns EINPROGRESS if FASTOPEN wasn't available */
144
      if (res < 0) {
145
        if (errno != EINPROGRESS) {
146
          m_free(c->errstring);
147
          c->errstring = m_strdup(strerror(errno));
148
          /* Not entirely sure which kind of errors are normal - 2.6.32 seems to 
149
          return EPIPE for any (nonblocking?) sendmsg(). just fall back */
150
          TRACE(("sendmsg tcp_fastopen failed, falling back. %s", strerror(errno)));
151
          /* No kernel MSG_FASTOPEN support. Fall back below */
152
          fastopen = 0;
153
          /* Set to NULL to avoid trying again */
154
          c->writequeue = NULL;
155
        }
156
      } else {
157
        packet_queue_consume(c->writequeue, res);
158
      }
159
    }
160
#endif
161
162
    /* Normal connect(), used as fallback for TCP fastopen too */
163
0
    if (!fastopen) {
164
0
      res = connect(c->sock, r->ai_addr, r->ai_addrlen);
165
0
    }
166
167
0
    if (res < 0 && errno != retry_errno) {
168
      /* failure */
169
0
      m_free(c->errstring);
170
0
      c->errstring = m_strdup(strerror(errno));
171
0
      close(c->sock);
172
0
      c->sock = -1;
173
0
      continue;
174
0
    } else {
175
      /* new connection was successful, wait for it to complete */
176
0
      break;
177
0
    }
178
0
  }
179
180
0
  if (r) {
181
0
    c->res_iter = r->ai_next;
182
0
  } else {
183
0
    c->res_iter = NULL;
184
0
  }
185
0
}
186
187
/* Connect via TCP to a host. */
188
struct dropbear_progress_connection *connect_remote(const char* remotehost, const char* remoteport,
189
  connect_callback cb, void* cb_data,
190
  const char* bind_address, const char* bind_port, enum dropbear_prio prio)
191
0
{
192
0
  struct dropbear_progress_connection *c = NULL;
193
0
  int err;
194
0
  struct addrinfo hints;
195
196
0
  c = m_malloc(sizeof(*c));
197
0
  c->remotehost = m_strdup(remotehost);
198
0
  c->remoteport = m_strdup(remoteport);
199
0
  c->sock = -1;
200
0
  c->cb = cb;
201
0
  c->cb_data = cb_data;
202
0
  c->prio = prio;
203
204
0
  list_append(&ses.conn_pending, c);
205
206
0
#if DROPBEAR_FUZZ
207
0
  if (fuzz.fuzzing) {
208
0
    c->errstring = m_strdup("fuzzing connect_remote always fails");
209
0
    return c;
210
0
  }
211
0
#endif
212
213
0
  memset(&hints, 0, sizeof(hints));
214
0
  hints.ai_socktype = SOCK_STREAM;
215
0
  hints.ai_family = AF_UNSPEC;
216
217
0
  err = getaddrinfo(remotehost, remoteport, &hints, &c->res);
218
0
  if (err) {
219
0
    int len;
220
0
    len = 100 + strlen(gai_strerror(err));
221
0
    c->errstring = (char*)m_malloc(len);
222
0
    snprintf(c->errstring, len, "Error resolving '%s' port '%s'. %s", 
223
0
        remotehost, remoteport, gai_strerror(err));
224
0
    TRACE(("Error resolving: %s", gai_strerror(err)))
225
0
  } else {
226
0
    c->res_iter = c->res;
227
0
  }
228
  
229
0
  if (bind_address) {
230
0
    c->bind_address = m_strdup(bind_address);
231
0
  }
232
0
  if (bind_port) {
233
0
    c->bind_port = m_strdup(bind_port);
234
0
  }
235
236
0
  return c;
237
0
}
238
239
240
/* Connect to stream local socket. */
241
struct dropbear_progress_connection *connect_streamlocal(const char* localpath,
242
  connect_callback cb, void* cb_data, enum dropbear_prio prio)
243
0
{
244
0
  struct dropbear_progress_connection *c = NULL;
245
0
  struct sockaddr_un *sunaddr;
246
247
0
  c = m_malloc(sizeof(*c));
248
0
  c->remotehost = m_strdup(localpath);
249
0
  c->remoteport = NULL;
250
0
  c->sock = -1;
251
0
  c->cb = cb;
252
0
  c->cb_data = cb_data;
253
0
  c->prio = prio;
254
255
0
  list_append(&ses.conn_pending, c);
256
257
0
#if DROPBEAR_FUZZ
258
0
  if (fuzz.fuzzing) {
259
0
    c->errstring = m_strdup("fuzzing connect_streamlocal always fails");
260
0
    return c;
261
0
  }
262
0
#endif
263
264
0
  if (strlen(localpath) >= sizeof(sunaddr->sun_path)) {
265
0
    c->errstring = m_strdup("Stream path too long");
266
0
    TRACE(("localpath: %s is too long", localpath));
267
0
    return c;
268
0
  }
269
270
  /*
271
   * Fake up a struct addrinfo for AF_UNIX connections.
272
   * remove_connect() must check ai_family
273
   * and use m_free() not freeaddirinfo() for AF_UNIX.
274
   */
275
0
  c->res = m_malloc(sizeof(*c->res) + sizeof(*sunaddr));
276
0
  c->res->ai_addr = (struct sockaddr *)(c->res + 1);
277
0
  c->res->ai_addrlen = sizeof(*sunaddr);
278
0
  c->res->ai_family = AF_UNIX;
279
0
  c->res->ai_socktype = SOCK_STREAM;
280
0
  c->res->ai_protocol = PF_UNSPEC;
281
0
  sunaddr = (struct sockaddr_un *)c->res->ai_addr;
282
0
  sunaddr->sun_family = AF_UNIX;
283
0
  strlcpy(sunaddr->sun_path, localpath, sizeof(sunaddr->sun_path));
284
285
  /* Copy to target iter */ 
286
0
  c->res_iter = c->res;
287
288
0
  return c;
289
0
}
290
291
0
void remove_connect_pending() {
292
0
  while (ses.conn_pending.first) {
293
0
    struct dropbear_progress_connection *c = ses.conn_pending.first->item;
294
0
    remove_connect(c, ses.conn_pending.first);
295
0
  }
296
0
}
297
298
299
0
void set_connect_fds(fd_set *writefd) {
300
0
  m_list_elem *iter;
301
0
  iter = ses.conn_pending.first;
302
0
  while (iter) {
303
0
    m_list_elem *next_iter = iter->next;
304
0
    struct dropbear_progress_connection *c = iter->item;
305
    /* Set one going */
306
0
    while (c->res_iter && c->sock < 0) {
307
0
      connect_try_next(c);
308
0
    }
309
0
    if (c->sock >= 0) {
310
0
      FD_SET(c->sock, writefd);
311
0
    } else {
312
      /* Final failure */
313
0
      if (!c->errstring) {
314
0
        c->errstring = m_strdup("unexpected failure");
315
0
      }
316
0
      c->cb(DROPBEAR_FAILURE, -1, c->cb_data, c->errstring);
317
0
      remove_connect(c, iter);
318
0
    }
319
0
    iter = next_iter;
320
0
  }
321
0
}
322
323
0
void handle_connect_fds(const fd_set *writefd) {
324
0
  m_list_elem *iter;
325
0
  for (iter = ses.conn_pending.first; iter; iter = iter->next) {
326
0
    int val;
327
0
    socklen_t vallen = sizeof(val);
328
0
    struct dropbear_progress_connection *c = iter->item;
329
330
0
    if (c->sock < 0 || !FD_ISSET(c->sock, writefd)) {
331
0
      continue;
332
0
    }
333
334
0
    TRACE(("handling %s port %s socket %d", c->remotehost, c->remoteport, c->sock));
335
336
0
    if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &val, &vallen) != 0) {
337
0
      TRACE(("handle_connect_fds getsockopt(%d) SO_ERROR failed: %s", c->sock, strerror(errno)))
338
      /* This isn't expected to happen - Unix has surprises though, continue gracefully. */
339
0
      m_close(c->sock);
340
0
      c->sock = -1;
341
0
    } else if (val != 0) {
342
      /* Connect failed */
343
0
      TRACE(("connect to %s port %s failed.", c->remotehost, c->remoteport))
344
0
      m_close(c->sock);
345
0
      c->sock = -1;
346
347
0
      m_free(c->errstring);
348
0
      c->errstring = m_strdup(strerror(val));
349
0
    } else {
350
      /* New connection has been established */
351
0
      c->cb(DROPBEAR_SUCCESS, c->sock, c->cb_data, NULL);
352
0
      remove_connect(c, iter);
353
0
      TRACE(("leave handle_connect_fds - success"))
354
      /* Must return here - remove_connect() invalidates iter */
355
0
      return; 
356
0
    }
357
0
  }
358
0
}
359
360
0
void connect_set_writequeue(struct dropbear_progress_connection *c, struct Queue *writequeue) {
361
0
  c->writequeue = writequeue;
362
0
}
363
364
0
void packet_queue_to_iovec(const struct Queue *queue, struct iovec *iov, unsigned int *iov_count) {
365
0
  struct Link *l;
366
0
  unsigned int i;
367
0
  int len;
368
0
  buffer *writebuf;
369
370
#ifndef IOV_MAX
371
  #if (defined(__CYGWIN__) || defined(__GNU__)) && !defined(UIO_MAXIOV)
372
    #define IOV_MAX 1024
373
  #elif defined(__sgi)
374
    #define IOV_MAX 512 
375
  #else 
376
    #define IOV_MAX UIO_MAXIOV
377
  #endif
378
#endif
379
380
0
  *iov_count = MIN(MIN(queue->count, IOV_MAX), *iov_count);
381
382
0
  for (l = queue->head, i = 0; i < *iov_count; l = l->link, i++)
383
0
  {
384
0
    writebuf = (buffer*)l->item;
385
0
    len = writebuf->len - writebuf->pos;
386
0
    dropbear_assert(len > 0);
387
0
    TRACE2(("write_packet writev #%d len %d/%d", i,
388
0
        len, writebuf->len))
389
0
    iov[i].iov_base = buf_getptr(writebuf, len);
390
0
    iov[i].iov_len = len;
391
0
  }
392
0
}
393
394
0
void packet_queue_consume(struct Queue *queue, ssize_t written) {
395
0
  buffer *writebuf;
396
0
  int len;
397
0
  while (written > 0) {
398
0
    writebuf = (buffer*)examine(queue);
399
0
    len = writebuf->len - writebuf->pos;
400
0
    if (len > written) {
401
      /* partial buffer write */
402
0
      buf_incrpos(writebuf, written);
403
0
      written = 0;
404
0
    } else {
405
0
      written -= len;
406
0
      dequeue(queue);
407
0
      buf_free(writebuf);
408
0
    }
409
0
  }
410
0
}
411
412
0
void set_sock_nodelay(int sock) {
413
0
  int val;
414
415
  /* disable nagle */
416
0
  val = 1;
417
0
  setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
418
0
}
419
420
#if DROPBEAR_SERVER_TCP_FAST_OPEN
421
0
void set_listen_fast_open(int sock) {
422
0
  int qlen = MAX(MAX_UNAUTH_PER_IP, 5);
423
0
  if (setsockopt(sock, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)) != 0) {
424
0
    TRACE(("set_listen_fast_open failed for socket %d: %s", sock, strerror(errno)))
425
0
  }
426
0
}
427
428
#endif
429
430
0
void set_sock_priority(int sock, enum dropbear_prio prio) {
431
432
0
  int rc;
433
0
  int val;
434
435
0
#if DROPBEAR_FUZZ
436
0
  if (fuzz.fuzzing) {
437
0
    TRACE(("fuzzing skips set_sock_prio"))
438
0
    return;
439
0
  }
440
0
#endif
441
  /* Don't log ENOTSOCK errors so that this can harmlessly be called
442
   * on a client '-J' proxy pipe */
443
444
0
  if (opts.disable_ip_tos == 0) {
445
0
#ifdef IP_TOS
446
  /* Set the DSCP field for outbound IP packet priority.
447
  rfc4594 has some guidance to meanings.
448
449
  We set AF21 as "Low-Latency" class for interactive (tty session,
450
  also handshake/setup packets). Other traffic is left at the default.
451
452
  OpenSSH at present uses AF21/CS1, rationale
453
  https://cvsweb.openbsd.org/src/usr.bin/ssh/readconf.c#rev1.284
454
455
  Old Dropbear/OpenSSH and Debian/Ubuntu OpenSSH (at Jan 2022) use
456
  IPTOS_LOWDELAY/IPTOS_THROUGHPUT
457
458
  DSCP constants are from Linux headers, applicable to other platforms
459
  such as macos.
460
  */
461
0
  if (prio == DROPBEAR_PRIO_LOWDELAY) {
462
0
    val = 0x48; /* IPTOS_DSCP_AF21 */
463
0
  } else {
464
0
    val = 0; /* default */
465
0
  }
466
0
#if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS)
467
0
  rc = setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, (void*)&val, sizeof(val));
468
0
  if (rc < 0 && errno != ENOTSOCK) {
469
0
    TRACE(("Couldn't set IPV6_TCLASS (%s)", strerror(errno)));
470
0
  }
471
0
#endif
472
0
  rc = setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&val, sizeof(val));
473
0
  if (rc < 0 && errno != ENOTSOCK) {
474
0
    TRACE(("Couldn't set IP_TOS (%s)", strerror(errno)));
475
0
  }
476
0
#endif /* IP_TOS */
477
0
  }
478
479
0
#ifdef HAVE_LINUX_PKT_SCHED_H
480
  /* Set scheduling priority within the local Linux network stack */
481
0
  if (prio == DROPBEAR_PRIO_LOWDELAY) {
482
0
    val = TC_PRIO_INTERACTIVE;
483
0
  } else {
484
0
    val = 0;
485
0
  }
486
  /* linux specific, sets QoS class. see tc-prio(8) */
487
0
  rc = setsockopt(sock, SOL_SOCKET, SO_PRIORITY, (void*) &val, sizeof(val));
488
0
  if (rc < 0 && errno != ENOTSOCK) {
489
0
    TRACE(("Couldn't set SO_PRIORITY (%s)", strerror(errno)))
490
0
    }
491
0
#endif
492
493
0
}
494
495
/* from openssh/canohost.c avoid premature-optimization */
496
0
int get_sock_port(int sock) {
497
0
  struct sockaddr_storage from;
498
0
  socklen_t fromlen;
499
0
  char strport[NI_MAXSERV];
500
0
  int r;
501
502
  /* Get IP address of client. */
503
0
  fromlen = sizeof(from);
504
0
  memset(&from, 0, sizeof(from));
505
0
  if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) {
506
0
    TRACE(("getsockname failed: %d", errno))
507
0
    return 0;
508
0
  }
509
510
  /* Work around Linux IPv6 weirdness */
511
0
  if (from.ss_family == AF_INET6)
512
0
    fromlen = sizeof(struct sockaddr_in6);
513
514
  /* Non-inet sockets don't have a port number. */
515
0
  if (from.ss_family != AF_INET && from.ss_family != AF_INET6)
516
0
    return 0;
517
518
  /* Return port number. */
519
0
  if ((r = getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0,
520
0
      strport, sizeof(strport), NI_NUMERICSERV)) != 0) {
521
0
    TRACE(("netio.c/get_sock_port/getnameinfo NI_NUMERICSERV failed: %d", r))
522
0
  }
523
0
  return atoi(strport);
524
0
}
525
526
/* Listen on address:port. 
527
 * Special cases are address of "" listening on everything,
528
 * and address of NULL listening on localhost only.
529
 * Returns the number of sockets bound on success, or -1 on failure. On
530
 * failure, if errstring wasn't NULL, it'll be a newly malloced error
531
 * string.*/
532
int dropbear_listen(const char* address, const char* port,
533
0
    int *socks, unsigned int sockcount, char **errstring, int *maxfd, const char* interface) {
534
535
0
  struct addrinfo hints, *res = NULL, *res0 = NULL;
536
0
  int err;
537
0
  unsigned int nsock;
538
0
  int val;
539
0
  int sock;
540
0
  uint16_t *allocated_lport_p = NULL;
541
0
  int allocated_lport = 0;
542
  
543
0
  TRACE(("enter dropbear_listen"))
544
545
0
#if DROPBEAR_FUZZ
546
0
  if (fuzz.fuzzing) {
547
0
    return fuzz_dropbear_listen(address, port, socks, sockcount, errstring, maxfd);
548
0
  }
549
0
#endif
550
  
551
0
  memset(&hints, 0, sizeof(hints));
552
0
  hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */
553
0
  hints.ai_socktype = SOCK_STREAM;
554
555
  /* for calling getaddrinfo:
556
   address == NULL and !AI_PASSIVE: local loopback
557
   address == NULL and AI_PASSIVE: all interfaces
558
   address != NULL: whatever the address says */
559
0
  if (!address) {
560
0
    TRACE(("dropbear_listen: local loopback"))
561
0
  } else {
562
0
    if (address[0] == '\0') {
563
0
      if (interface) {
564
0
        TRACE(("dropbear_listen: %s", interface))
565
0
      } else {
566
0
        TRACE(("dropbear_listen: all interfaces"))
567
0
      }
568
0
      address = NULL;
569
0
    }
570
0
    hints.ai_flags = AI_PASSIVE;
571
0
  }
572
0
  err = getaddrinfo(address, port, &hints, &res0);
573
574
0
  if (err) {
575
0
    if (errstring != NULL && *errstring == NULL) {
576
0
      int len;
577
0
      len = 20 + strlen(gai_strerror(err));
578
0
      *errstring = (char*)m_malloc(len);
579
0
      snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
580
0
    }
581
0
    if (res0) {
582
0
      freeaddrinfo(res0);
583
0
      res0 = NULL;
584
0
    }
585
0
    TRACE(("leave dropbear_listen: failed resolving"))
586
0
    return -1;
587
0
  }
588
589
  /* When listening on server-assigned-port 0
590
   * the assigned ports may differ for address families (v4/v6)
591
   * causing problems for tcpip-forward.
592
   * Caller can do a get_socket_address to discover assigned-port
593
   * hence, use same port for all address families */
594
0
  allocated_lport = 0;
595
0
  nsock = 0;
596
0
  for (res = res0; res != NULL && nsock < sockcount;
597
0
      res = res->ai_next) {
598
0
    if (allocated_lport > 0) {
599
0
      if (AF_INET == res->ai_family) {
600
0
        allocated_lport_p = &((struct sockaddr_in *)res->ai_addr)->sin_port;
601
0
      } else if (AF_INET6 == res->ai_family) {
602
0
        allocated_lport_p = &((struct sockaddr_in6 *)res->ai_addr)->sin6_port;
603
0
      }
604
0
      *allocated_lport_p = htons(allocated_lport);
605
0
    }
606
607
    /* Get a socket */
608
0
    socks[nsock] = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
609
0
    sock = socks[nsock]; /* For clarity */
610
0
    if (sock < 0) {
611
0
      err = errno;
612
0
      TRACE(("socket() failed"))
613
0
      continue;
614
0
    }
615
616
    /* Various useful socket options */
617
0
    val = 1;
618
    /* set to reuse, quick timeout */
619
0
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof(val));
620
621
0
#ifdef SO_BINDTODEVICE
622
0
    if(interface && setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, interface, strlen(interface)) < 0) {
623
0
      dropbear_log(LOG_WARNING, "Couldn't set SO_BINDTODEVICE");
624
0
      TRACE(("Failed setsockopt with errno failure, %d %s", errno, strerror(errno)))
625
0
    }
626
0
#endif
627
628
0
#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
629
0
    if (res->ai_family == AF_INET6) {
630
0
      int on = 1;
631
0
      if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, 
632
0
            &on, sizeof(on)) == -1) {
633
0
        dropbear_log(LOG_WARNING, "Couldn't set IPV6_V6ONLY");
634
0
      }
635
0
    }
636
0
#endif
637
0
    set_sock_nodelay(sock);
638
639
0
    if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
640
0
      err = errno;
641
0
      close(sock);
642
0
      TRACE(("bind(%s) failed", port))
643
0
      continue;
644
0
    }
645
646
0
    if (listen(sock, DROPBEAR_LISTEN_BACKLOG) < 0) {
647
0
      err = errno;
648
0
      close(sock);
649
0
      TRACE(("listen() failed"))
650
0
      continue;
651
0
    }
652
653
0
    if (0 == allocated_lport) {
654
0
      allocated_lport = get_sock_port(sock);
655
0
    }
656
657
0
    *maxfd = MAX(*maxfd, sock);
658
0
    nsock++;
659
0
  }
660
661
0
  if (res0) {
662
0
    freeaddrinfo(res0);
663
0
    res0 = NULL;
664
0
  }
665
666
0
  if (nsock == 0) {
667
0
    if (errstring != NULL && *errstring == NULL) {
668
0
      int len;
669
0
      len = 20 + strlen(strerror(err));
670
0
      *errstring = (char*)m_malloc(len);
671
0
      snprintf(*errstring, len, "Error listening: %s", strerror(err));
672
0
    }
673
0
    TRACE(("leave dropbear_listen: failure, %s", strerror(err)))
674
0
    return -1;
675
0
  }
676
677
0
  TRACE(("leave dropbear_listen: success, %d socks bound", nsock))
678
0
  return nsock;
679
0
}
680
681
void get_socket_address(int fd, char **local_host, char **local_port,
682
            char **remote_host, char **remote_port, int host_lookup)
683
0
{
684
0
  struct sockaddr_storage addr;
685
0
  socklen_t addrlen;
686
687
0
#if DROPBEAR_FUZZ
688
0
  if (fuzz.fuzzing) {
689
0
    fuzz_get_socket_address(fd, local_host, local_port, remote_host, remote_port, host_lookup);
690
0
    return;
691
0
  }
692
0
#endif
693
  
694
0
  if (local_host || local_port) {
695
0
    addrlen = sizeof(addr);
696
0
    if (getsockname(fd, (struct sockaddr*)&addr, &addrlen) < 0) {
697
0
      dropbear_exit("Failed socket address: %s", strerror(errno));
698
0
    }
699
0
    getaddrstring(&addr, local_host, local_port, host_lookup);    
700
0
  }
701
0
  if (remote_host || remote_port) {
702
0
    addrlen = sizeof(addr);
703
0
    if (getpeername(fd, (struct sockaddr*)&addr, &addrlen) < 0) {
704
0
      dropbear_exit("Failed socket address: %s", strerror(errno));
705
0
    }
706
0
    getaddrstring(&addr, remote_host, remote_port, host_lookup);    
707
0
  }
708
0
}
709
710
/* Return a string representation of the socket address passed. The return
711
 * value is allocated with malloc() */
712
void getaddrstring(struct sockaddr_storage* addr, 
713
      char **ret_host, char **ret_port,
714
0
      int host_lookup) {
715
716
0
  char host[NI_MAXHOST+1], serv[NI_MAXSERV+1];
717
0
  unsigned int len;
718
0
  int ret;
719
  
720
0
  int flags = NI_NUMERICSERV | NI_NUMERICHOST;
721
722
0
#if !DO_HOST_LOOKUP
723
0
  host_lookup = 0;
724
0
#endif
725
  
726
0
  if (host_lookup) {
727
0
    flags = NI_NUMERICSERV;
728
0
  }
729
730
0
  len = sizeof(struct sockaddr_storage);
731
  /* Some platforms such as Solaris 8 require that len is the length
732
   * of the specific structure. Some older linux systems (glibc 2.1.3
733
   * such as debian potato) have sockaddr_storage.__ss_family instead
734
   * but we'll ignore them */
735
0
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY
736
0
  if (addr->ss_family == AF_INET) {
737
0
    len = sizeof(struct sockaddr_in);
738
0
  }
739
0
#ifdef AF_INET6
740
0
  if (addr->ss_family == AF_INET6) {
741
0
    len = sizeof(struct sockaddr_in6);
742
0
  }
743
0
#endif
744
0
#endif
745
746
0
  ret = getnameinfo((struct sockaddr*)addr, len, host, sizeof(host)-1, 
747
0
      serv, sizeof(serv)-1, flags);
748
749
0
  if (ret != 0) {
750
0
    if (host_lookup) {
751
      /* On some systems (Darwin does it) we get EINTR from getnameinfo
752
       * somehow. Eew. So we'll just return the IP, since that doesn't seem
753
       * to exhibit that behaviour. */
754
0
      getaddrstring(addr, ret_host, ret_port, 0);
755
0
      return;
756
0
    } else {
757
      /* if we can't do a numeric lookup, something's gone terribly wrong */
758
0
      dropbear_exit("Failed lookup: %s", gai_strerror(ret));
759
0
    }
760
0
  }
761
762
0
  if (ret_host) {
763
0
    *ret_host = m_strdup(host);
764
0
  }
765
0
  if (ret_port) {
766
0
    *ret_port = m_strdup(serv);
767
0
  }
768
0
}
769