Mercurial > dropbear
comparison dbutil.c @ 1051:359fba4b1a49
merge tcp fastopen
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sat, 28 Feb 2015 23:24:30 +0800 |
parents | 01eea88963f3 |
children | 36557295418e |
comparison
equal
deleted
inserted
replaced
1045:31727a8abd4b | 1051:359fba4b1a49 |
---|---|
211 fprintf(stderr, "\n"); | 211 fprintf(stderr, "\n"); |
212 va_end(param); | 212 va_end(param); |
213 } | 213 } |
214 #endif /* DEBUG_TRACE */ | 214 #endif /* DEBUG_TRACE */ |
215 | 215 |
216 void set_sock_nodelay(int sock) { | |
217 int val; | |
218 | |
219 /* disable nagle */ | |
220 val = 1; | |
221 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val)); | |
222 } | |
223 | |
224 void set_sock_priority(int sock, enum dropbear_prio prio) { | |
225 | |
226 int iptos_val = 0, so_prio_val = 0, rc; | |
227 | |
228 /* Don't log ENOTSOCK errors so that this can harmlessly be called | |
229 * on a client '-J' proxy pipe */ | |
230 | |
231 /* set the TOS bit for either ipv4 or ipv6 */ | |
232 #ifdef IPTOS_LOWDELAY | |
233 if (prio == DROPBEAR_PRIO_LOWDELAY) { | |
234 iptos_val = IPTOS_LOWDELAY; | |
235 } else if (prio == DROPBEAR_PRIO_BULK) { | |
236 iptos_val = IPTOS_THROUGHPUT; | |
237 } | |
238 #if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS) | |
239 rc = setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, (void*)&iptos_val, sizeof(iptos_val)); | |
240 if (rc < 0 && errno != ENOTSOCK) { | |
241 TRACE(("Couldn't set IPV6_TCLASS (%s)", strerror(errno))); | |
242 } | |
243 #endif | |
244 rc = setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&iptos_val, sizeof(iptos_val)); | |
245 if (rc < 0 && errno != ENOTSOCK) { | |
246 TRACE(("Couldn't set IP_TOS (%s)", strerror(errno))); | |
247 } | |
248 #endif | |
249 | |
250 #ifdef SO_PRIORITY | |
251 if (prio == DROPBEAR_PRIO_LOWDELAY) { | |
252 so_prio_val = TC_PRIO_INTERACTIVE; | |
253 } else if (prio == DROPBEAR_PRIO_BULK) { | |
254 so_prio_val = TC_PRIO_BULK; | |
255 } | |
256 /* linux specific, sets QoS class. see tc-prio(8) */ | |
257 rc = setsockopt(sock, SOL_SOCKET, SO_PRIORITY, (void*) &so_prio_val, sizeof(so_prio_val)); | |
258 if (rc < 0 && errno != ENOTSOCK) | |
259 dropbear_log(LOG_WARNING, "Couldn't set SO_PRIORITY (%s)", | |
260 strerror(errno)); | |
261 #endif | |
262 | |
263 } | |
264 | |
265 /* Listen on address:port. | |
266 * Special cases are address of "" listening on everything, | |
267 * and address of NULL listening on localhost only. | |
268 * Returns the number of sockets bound on success, or -1 on failure. On | |
269 * failure, if errstring wasn't NULL, it'll be a newly malloced error | |
270 * string.*/ | |
271 int dropbear_listen(const char* address, const char* port, | |
272 int *socks, unsigned int sockcount, char **errstring, int *maxfd) { | |
273 | |
274 struct addrinfo hints, *res = NULL, *res0 = NULL; | |
275 int err; | |
276 unsigned int nsock; | |
277 struct linger linger; | |
278 int val; | |
279 int sock; | |
280 | |
281 TRACE(("enter dropbear_listen")) | |
282 | |
283 memset(&hints, 0, sizeof(hints)); | |
284 hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */ | |
285 hints.ai_socktype = SOCK_STREAM; | |
286 | |
287 /* for calling getaddrinfo: | |
288 address == NULL and !AI_PASSIVE: local loopback | |
289 address == NULL and AI_PASSIVE: all interfaces | |
290 address != NULL: whatever the address says */ | |
291 if (!address) { | |
292 TRACE(("dropbear_listen: local loopback")) | |
293 } else { | |
294 if (address[0] == '\0') { | |
295 TRACE(("dropbear_listen: all interfaces")) | |
296 address = NULL; | |
297 } | |
298 hints.ai_flags = AI_PASSIVE; | |
299 } | |
300 err = getaddrinfo(address, port, &hints, &res0); | |
301 | |
302 if (err) { | |
303 if (errstring != NULL && *errstring == NULL) { | |
304 int len; | |
305 len = 20 + strlen(gai_strerror(err)); | |
306 *errstring = (char*)m_malloc(len); | |
307 snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err)); | |
308 } | |
309 if (res0) { | |
310 freeaddrinfo(res0); | |
311 res0 = NULL; | |
312 } | |
313 TRACE(("leave dropbear_listen: failed resolving")) | |
314 return -1; | |
315 } | |
316 | |
317 | |
318 nsock = 0; | |
319 for (res = res0; res != NULL && nsock < sockcount; | |
320 res = res->ai_next) { | |
321 | |
322 /* Get a socket */ | |
323 socks[nsock] = socket(res->ai_family, res->ai_socktype, | |
324 res->ai_protocol); | |
325 | |
326 sock = socks[nsock]; /* For clarity */ | |
327 | |
328 if (sock < 0) { | |
329 err = errno; | |
330 TRACE(("socket() failed")) | |
331 continue; | |
332 } | |
333 | |
334 /* Various useful socket options */ | |
335 val = 1; | |
336 /* set to reuse, quick timeout */ | |
337 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof(val)); | |
338 linger.l_onoff = 1; | |
339 linger.l_linger = 5; | |
340 setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger)); | |
341 | |
342 #if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) | |
343 if (res->ai_family == AF_INET6) { | |
344 int on = 1; | |
345 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, | |
346 &on, sizeof(on)) == -1) { | |
347 dropbear_log(LOG_WARNING, "Couldn't set IPV6_V6ONLY"); | |
348 } | |
349 } | |
350 #endif | |
351 | |
352 set_sock_nodelay(sock); | |
353 | |
354 if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { | |
355 err = errno; | |
356 close(sock); | |
357 TRACE(("bind(%s) failed", port)) | |
358 continue; | |
359 } | |
360 | |
361 if (listen(sock, DROPBEAR_LISTEN_BACKLOG) < 0) { | |
362 err = errno; | |
363 close(sock); | |
364 TRACE(("listen() failed")) | |
365 continue; | |
366 } | |
367 | |
368 *maxfd = MAX(*maxfd, sock); | |
369 | |
370 nsock++; | |
371 } | |
372 | |
373 if (res0) { | |
374 freeaddrinfo(res0); | |
375 res0 = NULL; | |
376 } | |
377 | |
378 if (nsock == 0) { | |
379 if (errstring != NULL && *errstring == NULL) { | |
380 int len; | |
381 len = 20 + strlen(strerror(err)); | |
382 *errstring = (char*)m_malloc(len); | |
383 snprintf(*errstring, len, "Error listening: %s", strerror(err)); | |
384 } | |
385 TRACE(("leave dropbear_listen: failure, %s", strerror(err))) | |
386 return -1; | |
387 } | |
388 | |
389 TRACE(("leave dropbear_listen: success, %d socks bound", nsock)) | |
390 return nsock; | |
391 } | |
392 | |
393 /* Connect to a given unix socket. The socket is blocking */ | 216 /* Connect to a given unix socket. The socket is blocking */ |
394 #ifdef ENABLE_CONNECT_UNIX | 217 #ifdef ENABLE_CONNECT_UNIX |
395 int connect_unix(const char* path) { | 218 int connect_unix(const char* path) { |
396 struct sockaddr_un addr; | 219 struct sockaddr_un addr; |
397 int fd = -1; | 220 int fd = -1; |
410 return -1; | 233 return -1; |
411 } | 234 } |
412 return fd; | 235 return fd; |
413 } | 236 } |
414 #endif | 237 #endif |
415 | |
416 #if defined(__linux__) && defined(TCP_DEFER_ACCEPT) | |
417 static void set_piggyback_ack(int sock) { | |
418 /* Undocumented Linux feature - set TCP_DEFER_ACCEPT and data will be piggybacked | |
419 on the 3rd packet (ack) of the TCP handshake. Saves a IP packet. | |
420 http://thread.gmane.org/gmane.linux.network/224627/focus=224727 | |
421 "Piggyback the final ACK of the three way TCP connection establishment with the data" */ | |
422 int val = 1; | |
423 /* No error checking, this is opportunistic */ | |
424 int err = setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, (void*)&val, sizeof(val)); | |
425 if (err) | |
426 { | |
427 TRACE(("Failed setsockopt TCP_DEFER_ACCEPT: %s", strerror(errno))) | |
428 } | |
429 } | |
430 #endif | |
431 | |
432 | |
433 /* Connect via TCP to a host. Connection will try ipv4 or ipv6, will | |
434 * return immediately if nonblocking is set. On failure, if errstring | |
435 * wasn't null, it will be a newly malloced error message */ | |
436 | |
437 /* TODO: maxfd */ | |
438 int connect_remote(const char* remotehost, const char* remoteport, char ** errstring) { | |
439 | |
440 struct addrinfo *res0 = NULL, *res = NULL, hints; | |
441 int sock; | |
442 int err; | |
443 | |
444 TRACE(("enter connect_remote")) | |
445 | |
446 if (errstring != NULL) { | |
447 *errstring = NULL; | |
448 } | |
449 | |
450 memset(&hints, 0, sizeof(hints)); | |
451 hints.ai_socktype = SOCK_STREAM; | |
452 hints.ai_family = PF_UNSPEC; | |
453 | |
454 err = getaddrinfo(remotehost, remoteport, &hints, &res0); | |
455 if (err) { | |
456 if (errstring != NULL && *errstring == NULL) { | |
457 int len; | |
458 len = 100 + strlen(gai_strerror(err)); | |
459 *errstring = (char*)m_malloc(len); | |
460 snprintf(*errstring, len, "Error resolving '%s' port '%s'. %s", | |
461 remotehost, remoteport, gai_strerror(err)); | |
462 } | |
463 TRACE(("Error resolving: %s", gai_strerror(err))) | |
464 return -1; | |
465 } | |
466 | |
467 sock = -1; | |
468 err = EADDRNOTAVAIL; | |
469 for (res = res0; res; res = res->ai_next) { | |
470 | |
471 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | |
472 if (sock < 0) { | |
473 err = errno; | |
474 continue; | |
475 } | |
476 | |
477 setnonblocking(sock); | |
478 | |
479 #if defined(__linux__) && defined(TCP_DEFER_ACCEPT) | |
480 set_piggyback_ack(sock); | |
481 #endif | |
482 | |
483 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { | |
484 if (errno == EINPROGRESS) { | |
485 TRACE(("Connect in progress")) | |
486 break; | |
487 } else { | |
488 err = errno; | |
489 close(sock); | |
490 sock = -1; | |
491 continue; | |
492 } | |
493 } | |
494 | |
495 break; /* Success */ | |
496 } | |
497 | |
498 if (sock < 0 && !(errno == EINPROGRESS)) { | |
499 /* Failed */ | |
500 if (errstring != NULL && *errstring == NULL) { | |
501 int len; | |
502 len = 20 + strlen(strerror(err)); | |
503 *errstring = (char*)m_malloc(len); | |
504 snprintf(*errstring, len, "Error connecting: %s", strerror(err)); | |
505 } | |
506 TRACE(("Error connecting: %s", strerror(err))) | |
507 } else { | |
508 /* Success */ | |
509 set_sock_nodelay(sock); | |
510 } | |
511 | |
512 freeaddrinfo(res0); | |
513 if (sock > 0 && errstring != NULL && *errstring != NULL) { | |
514 m_free(*errstring); | |
515 } | |
516 | |
517 TRACE(("leave connect_remote: sock %d\n", sock)) | |
518 return sock; | |
519 } | |
520 | 238 |
521 /* Sets up a pipe for a, returning three non-blocking file descriptors | 239 /* Sets up a pipe for a, returning three non-blocking file descriptors |
522 * and the pid. exec_fn is the function that will actually execute the child process, | 240 * and the pid. exec_fn is the function that will actually execute the child process, |
523 * it will be run after the child has fork()ed, and is passed exec_data. | 241 * it will be run after the child has fork()ed, and is passed exec_data. |
524 * If ret_errfd == NULL then stderr will not be captured. | 242 * If ret_errfd == NULL then stderr will not be captured. |
649 for (i = 3; i <= maxfd; i++) { | 367 for (i = 3; i <= maxfd; i++) { |
650 m_close(i); | 368 m_close(i); |
651 } | 369 } |
652 | 370 |
653 execv(usershell, argv); | 371 execv(usershell, argv); |
654 } | |
655 | |
656 void get_socket_address(int fd, char **local_host, char **local_port, | |
657 char **remote_host, char **remote_port, int host_lookup) | |
658 { | |
659 struct sockaddr_storage addr; | |
660 socklen_t addrlen; | |
661 | |
662 if (local_host || local_port) { | |
663 addrlen = sizeof(addr); | |
664 if (getsockname(fd, (struct sockaddr*)&addr, &addrlen) < 0) { | |
665 dropbear_exit("Failed socket address: %s", strerror(errno)); | |
666 } | |
667 getaddrstring(&addr, local_host, local_port, host_lookup); | |
668 } | |
669 if (remote_host || remote_port) { | |
670 addrlen = sizeof(addr); | |
671 if (getpeername(fd, (struct sockaddr*)&addr, &addrlen) < 0) { | |
672 dropbear_exit("Failed socket address: %s", strerror(errno)); | |
673 } | |
674 getaddrstring(&addr, remote_host, remote_port, host_lookup); | |
675 } | |
676 } | |
677 | |
678 /* Return a string representation of the socket address passed. The return | |
679 * value is allocated with malloc() */ | |
680 void getaddrstring(struct sockaddr_storage* addr, | |
681 char **ret_host, char **ret_port, | |
682 int host_lookup) { | |
683 | |
684 char host[NI_MAXHOST+1], serv[NI_MAXSERV+1]; | |
685 unsigned int len; | |
686 int ret; | |
687 | |
688 int flags = NI_NUMERICSERV | NI_NUMERICHOST; | |
689 | |
690 #ifndef DO_HOST_LOOKUP | |
691 host_lookup = 0; | |
692 #endif | |
693 | |
694 if (host_lookup) { | |
695 flags = NI_NUMERICSERV; | |
696 } | |
697 | |
698 len = sizeof(struct sockaddr_storage); | |
699 /* Some platforms such as Solaris 8 require that len is the length | |
700 * of the specific structure. Some older linux systems (glibc 2.1.3 | |
701 * such as debian potato) have sockaddr_storage.__ss_family instead | |
702 * but we'll ignore them */ | |
703 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY | |
704 if (addr->ss_family == AF_INET) { | |
705 len = sizeof(struct sockaddr_in); | |
706 } | |
707 #ifdef AF_INET6 | |
708 if (addr->ss_family == AF_INET6) { | |
709 len = sizeof(struct sockaddr_in6); | |
710 } | |
711 #endif | |
712 #endif | |
713 | |
714 ret = getnameinfo((struct sockaddr*)addr, len, host, sizeof(host)-1, | |
715 serv, sizeof(serv)-1, flags); | |
716 | |
717 if (ret != 0) { | |
718 if (host_lookup) { | |
719 /* On some systems (Darwin does it) we get EINTR from getnameinfo | |
720 * somehow. Eew. So we'll just return the IP, since that doesn't seem | |
721 * to exhibit that behaviour. */ | |
722 getaddrstring(addr, ret_host, ret_port, 0); | |
723 return; | |
724 } else { | |
725 /* if we can't do a numeric lookup, something's gone terribly wrong */ | |
726 dropbear_exit("Failed lookup: %s", gai_strerror(ret)); | |
727 } | |
728 } | |
729 | |
730 if (ret_host) { | |
731 *ret_host = m_strdup(host); | |
732 } | |
733 if (ret_port) { | |
734 *ret_port = m_strdup(serv); | |
735 } | |
736 } | 372 } |
737 | 373 |
738 #ifdef DEBUG_TRACE | 374 #ifdef DEBUG_TRACE |
739 void printhex(const char * label, const unsigned char * buf, int len) { | 375 void printhex(const char * label, const unsigned char * buf, int len) { |
740 | 376 |
1057 | 693 |
1058 /* Fallback for everything else - this will sometimes go backwards */ | 694 /* Fallback for everything else - this will sometimes go backwards */ |
1059 return time(NULL); | 695 return time(NULL); |
1060 } | 696 } |
1061 | 697 |
698 |