7 struct dropbear_progress_connection
{
9 struct addrinfo
*res_iter
;
11 char *remotehost
, *remoteport
; /* For error reporting */
16 struct Queue
*writequeue
; /* A queue of encrypted packets to send with TCP fastopen,
24 /* Deallocate a progress connection. Removes from the pending list if iter!=NULL.
25 Does not close sockets */
26 static void remove_connect(struct dropbear_progress_connection
*c
, m_list_elem
*iter
) {
30 m_free(c
->remotehost
);
31 m_free(c
->remoteport
);
40 static void cancel_callback(int result
, int sock
, void* UNUSED(data
), const char* UNUSED(errstring
)) {
41 if (result
== DROPBEAR_SUCCESS
)
47 void cancel_connect(struct dropbear_progress_connection
*c
) {
48 c
->cb
= cancel_callback
;
52 static void connect_try_next(struct dropbear_progress_connection
*c
) {
56 #ifdef DROPBEAR_CLIENT_TCP_FAST_OPEN
57 struct msghdr message
;
60 for (r
= c
->res_iter
; r
; r
= r
->ai_next
)
62 dropbear_assert(c
->sock
== -1);
64 c
->sock
= socket(c
->res_iter
->ai_family
, c
->res_iter
->ai_socktype
, c
->res_iter
->ai_protocol
);
69 ses
.maxfd
= MAX(ses
.maxfd
, c
->sock
);
70 set_sock_nodelay(c
->sock
);
71 setnonblocking(c
->sock
);
73 #ifdef DROPBEAR_CLIENT_TCP_FAST_OPEN
74 fastopen
= (c
->writequeue
!= NULL
);
77 memset(&message
, 0x0, sizeof(message
));
78 message
.msg_name
= r
->ai_addr
;
79 message
.msg_namelen
= r
->ai_addrlen
;
80 /* 6 is arbitrary, enough to hold initial packets */
81 unsigned int iovlen
= 6; /* Linux msg_iovlen is a size_t */
83 packet_queue_to_iovec(c
->writequeue
, iov
, &iovlen
);
84 message
.msg_iov
= iov
;
85 message
.msg_iovlen
= iovlen
;
86 res
= sendmsg(c
->sock
, &message
, MSG_FASTOPEN
);
87 /* Returns EINPROGRESS if FASTOPEN wasn't available */
89 if (errno
!= EINPROGRESS
) {
91 c
->errstring
= m_strdup(strerror(errno
));
92 /* Not entirely sure which kind of errors are normal - 2.6.32 seems to
93 return EPIPE for any (nonblocking?) sendmsg(). just fall back */
94 TRACE(("sendmsg tcp_fastopen failed, falling back. %s", strerror(errno
)));
95 /* No kernel MSG_FASTOPEN support. Fall back below */
97 /* Set to NULL to avoid trying again */
101 packet_queue_consume(c
->writequeue
, res
);
106 /* Normal connect(), used as fallback for TCP fastopen too */
108 res
= connect(c
->sock
, r
->ai_addr
, r
->ai_addrlen
);
111 if (res
< 0 && errno
!= EINPROGRESS
) {
113 m_free(c
->errstring
);
114 c
->errstring
= m_strdup(strerror(errno
));
119 /* new connection was successful, wait for it to complete */
125 c
->res_iter
= r
->ai_next
;
131 /* Connect via TCP to a host. */
132 struct dropbear_progress_connection
*connect_remote(const char* remotehost
, const char* remoteport
,
133 connect_callback cb
, void* cb_data
)
135 struct dropbear_progress_connection
*c
= NULL
;
137 struct addrinfo hints
;
139 c
= m_malloc(sizeof(*c
));
140 c
->remotehost
= m_strdup(remotehost
);
141 c
->remoteport
= m_strdup(remoteport
);
144 c
->cb_data
= cb_data
;
146 list_append(&ses
.conn_pending
, c
);
148 memset(&hints
, 0, sizeof(hints
));
149 hints
.ai_socktype
= SOCK_STREAM
;
150 hints
.ai_family
= AF_UNSPEC
;
152 err
= getaddrinfo(remotehost
, remoteport
, &hints
, &c
->res
);
155 len
= 100 + strlen(gai_strerror(err
));
156 c
->errstring
= (char*)m_malloc(len
);
157 snprintf(c
->errstring
, len
, "Error resolving '%s' port '%s'. %s",
158 remotehost
, remoteport
, gai_strerror(err
));
159 TRACE(("Error resolving: %s", gai_strerror(err
)))
161 c
->res_iter
= c
->res
;
167 void remove_connect_pending() {
168 while (ses
.conn_pending
.first
) {
169 struct dropbear_progress_connection
*c
= ses
.conn_pending
.first
->item
;
170 remove_connect(c
, ses
.conn_pending
.first
);
175 void set_connect_fds(fd_set
*writefd
) {
177 TRACE(("enter set_connect_fds"))
178 iter
= ses
.conn_pending
.first
;
180 m_list_elem
*next_iter
= iter
->next
;
181 struct dropbear_progress_connection
*c
= iter
->item
;
183 while (c
->res_iter
&& c
->sock
< 0) {
187 FD_SET(c
->sock
, writefd
);
191 c
->errstring
= m_strdup("unexpected failure");
193 c
->cb(DROPBEAR_FAILURE
, -1, c
->cb_data
, c
->errstring
);
194 remove_connect(c
, iter
);
200 void handle_connect_fds(fd_set
*writefd
) {
202 TRACE(("enter handle_connect_fds"))
203 for (iter
= ses
.conn_pending
.first
; iter
; iter
= iter
->next
) {
205 socklen_t vallen
= sizeof(val
);
206 struct dropbear_progress_connection
*c
= iter
->item
;
208 if (c
->sock
< 0 || !FD_ISSET(c
->sock
, writefd
)) {
212 TRACE(("handling %s port %s socket %d", c
->remotehost
, c
->remoteport
, c
->sock
));
214 if (getsockopt(c
->sock
, SOL_SOCKET
, SO_ERROR
, &val
, &vallen
) != 0) {
215 TRACE(("handle_connect_fds getsockopt(%d) SO_ERROR failed: %s", c
->sock
, strerror(errno
)))
216 /* This isn't expected to happen - Unix has surprises though, continue gracefully. */
219 } else if (val
!= 0) {
221 TRACE(("connect to %s port %s failed.", c
->remotehost
, c
->remoteport
))
225 m_free(c
->errstring
);
226 c
->errstring
= m_strdup(strerror(val
));
228 /* New connection has been established */
229 c
->cb(DROPBEAR_SUCCESS
, c
->sock
, c
->cb_data
, NULL
);
230 remove_connect(c
, iter
);
231 TRACE(("leave handle_connect_fds - success"))
232 /* Must return here - remove_connect() invalidates iter */
236 TRACE(("leave handle_connect_fds - end iter"))
239 void connect_set_writequeue(struct dropbear_progress_connection
*c
, struct Queue
*writequeue
) {
240 c
->writequeue
= writequeue
;
243 void packet_queue_to_iovec(struct Queue
*queue
, struct iovec
*iov
, unsigned int *iov_count
) {
250 #define IOV_MAX UIO_MAXIOV
253 *iov_count
= MIN(MIN(queue
->count
, IOV_MAX
), *iov_count
);
255 for (l
= queue
->head
, i
= 0; i
< *iov_count
; l
= l
->link
, i
++)
257 writebuf
= (buffer
*)l
->item
;
258 len
= writebuf
->len
- 1 - writebuf
->pos
;
259 dropbear_assert(len
> 0);
260 TRACE2(("write_packet writev #%d type %d len %d/%d", i
, writebuf
->data
[writebuf
->len
-1],
261 len
, writebuf
->len
-1))
262 iov
[i
].iov_base
= buf_getptr(writebuf
, len
);
263 iov
[i
].iov_len
= len
;
267 void packet_queue_consume(struct Queue
*queue
, ssize_t written
) {
270 while (written
> 0) {
271 writebuf
= (buffer
*)examine(queue
);
272 len
= writebuf
->len
- 1 - writebuf
->pos
;
274 /* partial buffer write */
275 buf_incrpos(writebuf
, written
);
285 void set_sock_nodelay(int sock
) {
290 setsockopt(sock
, IPPROTO_TCP
, TCP_NODELAY
, (void*)&val
, sizeof(val
));
293 #ifdef DROPBEAR_SERVER_TCP_FAST_OPEN
294 void set_listen_fast_open(int sock
) {
295 int qlen
= MAX(MAX_UNAUTH_PER_IP
, 5);
296 if (setsockopt(sock
, SOL_TCP
, TCP_FASTOPEN
, &qlen
, sizeof(qlen
)) != 0) {
297 TRACE(("set_listen_fast_open failed for socket %d: %s", sock
, strerror(errno
)))
303 void set_sock_priority(int sock
, enum dropbear_prio prio
) {
306 #ifdef IPTOS_LOWDELAY
314 /* Don't log ENOTSOCK errors so that this can harmlessly be called
315 * on a client '-J' proxy pipe */
317 /* set the TOS bit for either ipv4 or ipv6 */
318 #ifdef IPTOS_LOWDELAY
319 if (prio
== DROPBEAR_PRIO_LOWDELAY
) {
320 iptos_val
= IPTOS_LOWDELAY
;
321 } else if (prio
== DROPBEAR_PRIO_BULK
) {
322 iptos_val
= IPTOS_THROUGHPUT
;
324 #if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS)
325 rc
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_TCLASS
, (void*)&iptos_val
, sizeof(iptos_val
));
326 if (rc
< 0 && errno
!= ENOTSOCK
) {
327 TRACE(("Couldn't set IPV6_TCLASS (%s)", strerror(errno
)));
330 rc
= setsockopt(sock
, IPPROTO_IP
, IP_TOS
, (void*)&iptos_val
, sizeof(iptos_val
));
331 if (rc
< 0 && errno
!= ENOTSOCK
) {
332 TRACE(("Couldn't set IP_TOS (%s)", strerror(errno
)));
337 if (prio
== DROPBEAR_PRIO_LOWDELAY
) {
338 so_prio_val
= TC_PRIO_INTERACTIVE
;
339 } else if (prio
== DROPBEAR_PRIO_BULK
) {
340 so_prio_val
= TC_PRIO_BULK
;
342 /* linux specific, sets QoS class. see tc-prio(8) */
343 rc
= setsockopt(sock
, SOL_SOCKET
, SO_PRIORITY
, (void*) &so_prio_val
, sizeof(so_prio_val
));
344 if (rc
< 0 && errno
!= ENOTSOCK
)
345 dropbear_log(LOG_WARNING
, "Couldn't set SO_PRIORITY (%s)",
351 /* Listen on address:port.
352 * Special cases are address of "" listening on everything,
353 * and address of NULL listening on localhost only.
354 * Returns the number of sockets bound on success, or -1 on failure. On
355 * failure, if errstring wasn't NULL, it'll be a newly malloced error
357 int dropbear_listen(const char* address
, const char* port
,
358 int *socks
, unsigned int sockcount
, char **errstring
, int *maxfd
) {
360 struct addrinfo hints
, *res
= NULL
, *res0
= NULL
;
363 struct linger linger
;
367 TRACE(("enter dropbear_listen"))
369 memset(&hints
, 0, sizeof(hints
));
370 hints
.ai_family
= AF_UNSPEC
; /* TODO: let them flag v4 only etc */
371 hints
.ai_socktype
= SOCK_STREAM
;
373 /* for calling getaddrinfo:
374 address == NULL and !AI_PASSIVE: local loopback
375 address == NULL and AI_PASSIVE: all interfaces
376 address != NULL: whatever the address says */
378 TRACE(("dropbear_listen: local loopback"))
380 if (address
[0] == '\0') {
381 TRACE(("dropbear_listen: all interfaces"))
384 hints
.ai_flags
= AI_PASSIVE
;
386 err
= getaddrinfo(address
, port
, &hints
, &res0
);
389 if (errstring
!= NULL
&& *errstring
== NULL
) {
391 len
= 20 + strlen(gai_strerror(err
));
392 *errstring
= (char*)m_malloc(len
);
393 snprintf(*errstring
, len
, "Error resolving: %s", gai_strerror(err
));
399 TRACE(("leave dropbear_listen: failed resolving"))
405 for (res
= res0
; res
!= NULL
&& nsock
< sockcount
;
406 res
= res
->ai_next
) {
409 socks
[nsock
] = socket(res
->ai_family
, res
->ai_socktype
,
412 sock
= socks
[nsock
]; /* For clarity */
416 TRACE(("socket() failed"))
420 /* Various useful socket options */
422 /* set to reuse, quick timeout */
423 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void*) &val
, sizeof(val
));
426 setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, (void*)&linger
, sizeof(linger
));
428 #if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
429 if (res
->ai_family
== AF_INET6
) {
431 if (setsockopt(sock
, IPPROTO_IPV6
, IPV6_V6ONLY
,
432 &on
, sizeof(on
)) == -1) {
433 dropbear_log(LOG_WARNING
, "Couldn't set IPV6_V6ONLY");
438 set_sock_nodelay(sock
);
440 if (bind(sock
, res
->ai_addr
, res
->ai_addrlen
) < 0) {
443 TRACE(("bind(%s) failed", port
))
447 if (listen(sock
, DROPBEAR_LISTEN_BACKLOG
) < 0) {
450 TRACE(("listen() failed"))
454 *maxfd
= MAX(*maxfd
, sock
);
465 if (errstring
!= NULL
&& *errstring
== NULL
) {
467 len
= 20 + strlen(strerror(err
));
468 *errstring
= (char*)m_malloc(len
);
469 snprintf(*errstring
, len
, "Error listening: %s", strerror(err
));
471 TRACE(("leave dropbear_listen: failure, %s", strerror(err
)))
475 TRACE(("leave dropbear_listen: success, %d socks bound", nsock
))
479 void get_socket_address(int fd
, char **local_host
, char **local_port
,
480 char **remote_host
, char **remote_port
, int host_lookup
)
482 struct sockaddr_storage addr
;
485 if (local_host
|| local_port
) {
486 addrlen
= sizeof(addr
);
487 if (getsockname(fd
, (struct sockaddr
*)&addr
, &addrlen
) < 0) {
488 dropbear_exit("Failed socket address: %s", strerror(errno
));
490 getaddrstring(&addr
, local_host
, local_port
, host_lookup
);
492 if (remote_host
|| remote_port
) {
493 addrlen
= sizeof(addr
);
494 if (getpeername(fd
, (struct sockaddr
*)&addr
, &addrlen
) < 0) {
495 dropbear_exit("Failed socket address: %s", strerror(errno
));
497 getaddrstring(&addr
, remote_host
, remote_port
, host_lookup
);
501 /* Return a string representation of the socket address passed. The return
502 * value is allocated with malloc() */
503 void getaddrstring(struct sockaddr_storage
* addr
,
504 char **ret_host
, char **ret_port
,
507 char host
[NI_MAXHOST
+1], serv
[NI_MAXSERV
+1];
511 int flags
= NI_NUMERICSERV
| NI_NUMERICHOST
;
513 #ifndef DO_HOST_LOOKUP
518 flags
= NI_NUMERICSERV
;
521 len
= sizeof(struct sockaddr_storage
);
522 /* Some platforms such as Solaris 8 require that len is the length
523 * of the specific structure. Some older linux systems (glibc 2.1.3
524 * such as debian potato) have sockaddr_storage.__ss_family instead
525 * but we'll ignore them */
526 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY
527 if (addr
->ss_family
== AF_INET
) {
528 len
= sizeof(struct sockaddr_in
);
531 if (addr
->ss_family
== AF_INET6
) {
532 len
= sizeof(struct sockaddr_in6
);
537 ret
= getnameinfo((struct sockaddr
*)addr
, len
, host
, sizeof(host
)-1,
538 serv
, sizeof(serv
)-1, flags
);
542 /* On some systems (Darwin does it) we get EINTR from getnameinfo
543 * somehow. Eew. So we'll just return the IP, since that doesn't seem
544 * to exhibit that behaviour. */
545 getaddrstring(addr
, ret_host
, ret_port
, 0);
548 /* if we can't do a numeric lookup, something's gone terribly wrong */
549 dropbear_exit("Failed lookup: %s", gai_strerror(ret
));
554 *ret_host
= m_strdup(host
);
557 *ret_port
= m_strdup(serv
);