Mercurial > dropbear
comparison dbutil.c @ 1024:aac0095dc3b4 fastopen
work in progress for async connect
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Wed, 18 Feb 2015 00:05:27 +0800 |
parents | a00303a7d247 |
children | 02baa0b334e8 |
comparison
equal
deleted
inserted
replaced
1023:a00303a7d247 | 1024:aac0095dc3b4 |
---|---|
437 TRACE(("Failed setsockopt TCP_DEFER_ACCEPT: %s", strerror(errno))) | 437 TRACE(("Failed setsockopt TCP_DEFER_ACCEPT: %s", strerror(errno))) |
438 } | 438 } |
439 } | 439 } |
440 #endif | 440 #endif |
441 | 441 |
442 | |
443 /* Connect via TCP to a host. Connection will try ipv4 or ipv6, will | |
444 * return immediately if nonblocking is set. On failure, if errstring | |
445 * wasn't null, it will be a newly malloced error message */ | |
446 | |
447 /* TODO: maxfd */ | |
448 int connect_remote(const char* remotehost, const char* remoteport, char ** errstring) { | |
449 | |
450 struct addrinfo *res0 = NULL, *res = NULL, hints; | |
451 int sock; | |
452 int err; | |
453 | |
454 TRACE(("enter connect_remote")) | |
455 | |
456 if (errstring != NULL) { | |
457 *errstring = NULL; | |
458 } | |
459 | |
460 memset(&hints, 0, sizeof(hints)); | |
461 hints.ai_socktype = SOCK_STREAM; | |
462 hints.ai_family = PF_UNSPEC; | |
463 | |
464 err = getaddrinfo(remotehost, remoteport, &hints, &res0); | |
465 if (err) { | |
466 if (errstring != NULL && *errstring == NULL) { | |
467 int len; | |
468 len = 100 + strlen(gai_strerror(err)); | |
469 *errstring = (char*)m_malloc(len); | |
470 snprintf(*errstring, len, "Error resolving '%s' port '%s'. %s", | |
471 remotehost, remoteport, gai_strerror(err)); | |
472 } | |
473 TRACE(("Error resolving: %s", gai_strerror(err))) | |
474 return -1; | |
475 } | |
476 | |
477 sock = -1; | |
478 err = EADDRNOTAVAIL; | |
479 for (res = res0; res; res = res->ai_next) { | |
480 | |
481 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | |
482 if (sock < 0) { | |
483 err = errno; | |
484 continue; | |
485 } | |
486 | |
487 setnonblocking(sock); | |
488 | |
489 #if defined(__linux__) && defined(TCP_DEFER_ACCEPT) | |
490 set_piggyback_ack(sock); | |
491 #endif | |
492 | |
493 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { | |
494 if (errno == EINPROGRESS) { | |
495 TRACE(("Connect in progress")) | |
496 break; | |
497 } else { | |
498 err = errno; | |
499 close(sock); | |
500 sock = -1; | |
501 continue; | |
502 } | |
503 } | |
504 | |
505 break; /* Success */ | |
506 } | |
507 | |
508 if (sock < 0 && !(errno == EINPROGRESS)) { | |
509 /* Failed */ | |
510 if (errstring != NULL && *errstring == NULL) { | |
511 int len; | |
512 len = 20 + strlen(strerror(err)); | |
513 *errstring = (char*)m_malloc(len); | |
514 snprintf(*errstring, len, "Error connecting: %s", strerror(err)); | |
515 } | |
516 TRACE(("Error connecting: %s", strerror(err))) | |
517 } else { | |
518 /* Success */ | |
519 set_sock_nodelay(sock); | |
520 } | |
521 | |
522 freeaddrinfo(res0); | |
523 if (sock > 0 && errstring != NULL && *errstring != NULL) { | |
524 m_free(*errstring); | |
525 } | |
526 | |
527 TRACE(("leave connect_remote: sock %d\n", sock)) | |
528 return sock; | |
529 } | |
530 | |
531 /* Sets up a pipe for a, returning three non-blocking file descriptors | 442 /* Sets up a pipe for a, returning three non-blocking file descriptors |
532 * and the pid. exec_fn is the function that will actually execute the child process, | 443 * and the pid. exec_fn is the function that will actually execute the child process, |
533 * it will be run after the child has fork()ed, and is passed exec_data. | 444 * it will be run after the child has fork()ed, and is passed exec_data. |
534 * If ret_errfd == NULL then stderr will not be captured. | 445 * If ret_errfd == NULL then stderr will not be captured. |
535 * ret_pid can be passed as NULL to discard the pid. */ | 446 * ret_pid can be passed as NULL to discard the pid. */ |
1067 | 978 |
1068 /* Fallback for everything else - this will sometimes go backwards */ | 979 /* Fallback for everything else - this will sometimes go backwards */ |
1069 return time(NULL); | 980 return time(NULL); |
1070 } | 981 } |
1071 | 982 |
983 | |
984 struct dropbear_progress_connection | |
985 { | |
986 struct addrinfo *res; | |
987 struct addrinfo *res_iter; | |
988 | |
989 char *remotehost, *remoteport; /* For error reporting */ | |
990 | |
991 connect_callback cb; | |
992 void *cb_data; | |
993 | |
994 struct Queue *writequeue; /* A queue of encrypted packets to send with TCP fastopen, | |
995 or NULL. */ | |
996 | |
997 int sock; | |
998 }; | |
999 | |
1000 /* Deallocate a progress connection. Removes from the pending list if iter!=NULL. | |
1001 Does not close sockets */ | |
1002 static void remove_connect(struct dropbear_progress_connection *c, m_list_elem *iter) { | |
1003 if (c->res) { | |
1004 freeaddrinfo(c->res); | |
1005 } | |
1006 m_free(c->remotehost); | |
1007 m_free(c->remoteport); | |
1008 m_free(c); | |
1009 | |
1010 if (iter) { | |
1011 list_remove(iter); | |
1012 } | |
1013 } | |
1014 | |
1015 static int connect_try_next(struct dropbear_progress_connection *c) { | |
1016 int err = EADDRNOTAVAIL; | |
1017 struct addrinfo *r; | |
1018 | |
1019 if (!c->res_iter) { | |
1020 return DROPBEAR_FAILURE; | |
1021 } | |
1022 | |
1023 for (r = c->res_iter; r; r = r->ai_next) | |
1024 { | |
1025 assert(c->sock == -1); | |
1026 | |
1027 c->sock = socket(c->res_iter->ai_family, c->res_iter->ai_socktype, c->res_iter->ai_protocol); | |
1028 if (c->sock < 0) { | |
1029 err = errno; | |
1030 continue; | |
1031 } | |
1032 | |
1033 setnonblocking(c->sock); | |
1034 | |
1035 #if defined(__linux__) && defined(TCP_DEFER_ACCEPT) | |
1036 set_piggyback_ack(sock); | |
1037 #endif | |
1038 | |
1039 if (connect(c->sock, r->ai_addr, r->ai_addrlen) < 0) { | |
1040 if (errno == EINPROGRESS) { | |
1041 TRACE(("Connect in progress")) | |
1042 break; | |
1043 } else { | |
1044 err = errno; | |
1045 close(c->sock); | |
1046 c->sock = -1; | |
1047 continue; | |
1048 } | |
1049 } | |
1050 | |
1051 break; /* Success. Treated the same as EINPROGRESS */ | |
1052 } | |
1053 | |
1054 if (r) { | |
1055 c->res_iter = r->ai_next; | |
1056 } else { | |
1057 c->res_iter = NULL; | |
1058 } | |
1059 | |
1060 if (c->sock >= 0 || (errno == EINPROGRESS)) { | |
1061 /* Success */ | |
1062 set_sock_nodelay(c->sock); | |
1063 return DROPBEAR_SUCCESS; | |
1064 } else { | |
1065 /* XXX - returning error message through */ | |
1066 #if 0 | |
1067 /* Failed */ | |
1068 if (errstring != NULL && *errstring == NULL) { | |
1069 int len; | |
1070 len = 20 + strlen(strerror(err)); | |
1071 *errstring = (char*)m_malloc(len); | |
1072 snprintf(*errstring, len, "Error connecting: %s", strerror(err)); | |
1073 } | |
1074 TRACE(("Error connecting: %s", strerror(err))) | |
1075 #endif | |
1076 return DROPBEAR_FAILURE; | |
1077 } | |
1078 } | |
1079 | |
1080 /* Connect via TCP to a host. Connection will try ipv4 or ipv6, will | |
1081 * return immediately if nonblocking is set. On failure, if errstring | |
1082 * wasn't null, it will be a newly malloced error message */ | |
1083 | |
1084 /* TODO: maxfd */ | |
1085 struct dropbear_progress_connection *connect_remote(const char* remotehost, const char* remoteport, | |
1086 connect_callback cb, void* cb_data) | |
1087 { | |
1088 struct dropbear_progress_connection *c = NULL; | |
1089 int err; | |
1090 struct addrinfo hints; | |
1091 | |
1092 c = m_malloc(sizeof(*c)); | |
1093 c->remotehost = m_strdup(remotehost); | |
1094 c->remoteport = m_strdup(remoteport); | |
1095 c->sock = -1; | |
1096 c->cb = cb; | |
1097 c->cb_data = cb_data; | |
1098 | |
1099 memset(&hints, 0, sizeof(hints)); | |
1100 hints.ai_socktype = SOCK_STREAM; | |
1101 hints.ai_family = PF_UNSPEC; | |
1102 | |
1103 err = getaddrinfo(remotehost, remoteport, &hints, &c->res); | |
1104 if (err) { | |
1105 int len; | |
1106 char *errstring; | |
1107 len = 100 + strlen(gai_strerror(err)); | |
1108 errstring = (char*)m_malloc(len); | |
1109 snprintf(errstring, len, "Error resolving '%s' port '%s'. %s", | |
1110 remotehost, remoteport, gai_strerror(err)); | |
1111 c->cb(DROPBEAR_FAILURE, -1, c->cb_data, errstring); | |
1112 m_free(errstring); | |
1113 TRACE(("Error resolving: %s", gai_strerror(err))) | |
1114 remove_connect(c, NULL); | |
1115 return NULL; | |
1116 } | |
1117 | |
1118 c->res_iter = c->res; | |
1119 | |
1120 if (connect_try_next(c) == DROPBEAR_FAILURE) { | |
1121 /* Should not happen - getaddrinfo() should return failure if there are no addresses */ | |
1122 c->cb(DROPBEAR_FAILURE, -1, c->cb_data, "No address to try"); | |
1123 TRACE(("leave handle_connect_fds - failed")) | |
1124 remove_connect(c, NULL); | |
1125 return NULL; | |
1126 } | |
1127 | |
1128 list_append(&ses.conn_pending, c); | |
1129 | |
1130 return c; | |
1131 } | |
1132 | |
1133 | |
1134 void set_connect_fds(fd_set *writefd) { | |
1135 m_list_elem *iter; | |
1136 TRACE(("enter handle_connect_fds")) | |
1137 for (iter = ses.conn_pending.first; iter; iter = iter->next) { | |
1138 struct dropbear_progress_connection *c = iter->item; | |
1139 if (c->sock >= 0) { | |
1140 FD_SET(c->sock, writefd); | |
1141 } | |
1142 else | |
1143 { | |
1144 | |
1145 } | |
1146 } | |
1147 } | |
1148 | |
1149 void handle_connect_fds(fd_set *writefd) { | |
1150 m_list_elem *iter; | |
1151 TRACE(("enter handle_connect_fds")) | |
1152 for (iter = ses.conn_pending.first; iter; iter = iter->next) { | |
1153 int val; | |
1154 socklen_t vallen = sizeof(val); | |
1155 struct dropbear_progress_connection *c = iter->item; | |
1156 | |
1157 if (!FD_ISSET(c->sock, writefd)) { | |
1158 continue; | |
1159 } | |
1160 | |
1161 TRACE(("handling %s port %s socket %d", c->remotehost, c->remoteport, c->sock)); | |
1162 | |
1163 if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &val, &vallen) != 0) { | |
1164 TRACE(("handle_connect_fds getsockopt(%d) SO_ERROR failed: %s", c->sock, strerror(errno))) | |
1165 } else if (val != 0) { | |
1166 /* Connect failed */ | |
1167 TRACE(("connect to %s port %s failed.", c->remotehost, c->remoteport)) | |
1168 m_close(c->sock); | |
1169 c->sock = -1; | |
1170 | |
1171 if (connect_try_next(c) == DROPBEAR_FAILURE) { | |
1172 c->cb(DROPBEAR_FAILURE, -1, c->cb_data, strerror(val)); | |
1173 TRACE(("leave handle_connect_fds - failed")) | |
1174 remove_connect(c, iter); | |
1175 /* Must return here - remove_connect() invalidates iter */ | |
1176 return; | |
1177 } else { | |
1178 /* new connection try was successfuly started, will be finished by a | |
1179 later call to handle_connect_fds() */ | |
1180 TRACE(("leave handle_connect_fds - new try")) | |
1181 continue; | |
1182 } | |
1183 } | |
1184 /* New connection has been established */ | |
1185 c->cb(DROPBEAR_SUCCESS, c->sock, c->cb_data, ""); | |
1186 remove_connect(c, iter); | |
1187 TRACE(("leave handle_connect_fds - success")) | |
1188 /* Must return here - remove_connect() invalidates iter */ | |
1189 return; | |
1190 } | |
1191 TRACE(("leave handle_connect_fds - end iter")) | |
1192 } |