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 }