Mercurial > dropbear
comparison dbutil.c @ 1032:0da8ba489c23 fastopen
Move generic network routines to netio.c
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Fri, 20 Feb 2015 23:16:38 +0800 |
parents | 1fff5d7163f6 |
children | 01eea88963f3 |
comparison
equal
deleted
inserted
replaced
1031:64c0aa01e2b6 | 1032:0da8ba489c23 |
---|---|
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 #ifdef DROPBEAR_TCP_FAST_OPEN | |
225 void set_listen_fast_open(int sock) { | |
226 int qlen = MAX(MAX_UNAUTH_PER_IP, 5); | |
227 if (setsockopt(sock, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)) != 0) { | |
228 TRACE(("set_listen_fast_open failed for socket %d: %s", sock, strerror(errno))) | |
229 } | |
230 } | |
231 | |
232 #endif | |
233 | |
234 void set_sock_priority(int sock, enum dropbear_prio prio) { | |
235 | |
236 int iptos_val = 0, so_prio_val = 0, rc; | |
237 | |
238 /* Don't log ENOTSOCK errors so that this can harmlessly be called | |
239 * on a client '-J' proxy pipe */ | |
240 | |
241 /* set the TOS bit for either ipv4 or ipv6 */ | |
242 #ifdef IPTOS_LOWDELAY | |
243 if (prio == DROPBEAR_PRIO_LOWDELAY) { | |
244 iptos_val = IPTOS_LOWDELAY; | |
245 } else if (prio == DROPBEAR_PRIO_BULK) { | |
246 iptos_val = IPTOS_THROUGHPUT; | |
247 } | |
248 #if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS) | |
249 rc = setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, (void*)&iptos_val, sizeof(iptos_val)); | |
250 if (rc < 0 && errno != ENOTSOCK) { | |
251 TRACE(("Couldn't set IPV6_TCLASS (%s)", strerror(errno))); | |
252 } | |
253 #endif | |
254 rc = setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&iptos_val, sizeof(iptos_val)); | |
255 if (rc < 0 && errno != ENOTSOCK) { | |
256 TRACE(("Couldn't set IP_TOS (%s)", strerror(errno))); | |
257 } | |
258 #endif | |
259 | |
260 #ifdef SO_PRIORITY | |
261 if (prio == DROPBEAR_PRIO_LOWDELAY) { | |
262 so_prio_val = TC_PRIO_INTERACTIVE; | |
263 } else if (prio == DROPBEAR_PRIO_BULK) { | |
264 so_prio_val = TC_PRIO_BULK; | |
265 } | |
266 /* linux specific, sets QoS class. see tc-prio(8) */ | |
267 rc = setsockopt(sock, SOL_SOCKET, SO_PRIORITY, (void*) &so_prio_val, sizeof(so_prio_val)); | |
268 if (rc < 0 && errno != ENOTSOCK) | |
269 dropbear_log(LOG_WARNING, "Couldn't set SO_PRIORITY (%s)", | |
270 strerror(errno)); | |
271 #endif | |
272 | |
273 } | |
274 | |
275 /* Listen on address:port. | |
276 * Special cases are address of "" listening on everything, | |
277 * and address of NULL listening on localhost only. | |
278 * Returns the number of sockets bound on success, or -1 on failure. On | |
279 * failure, if errstring wasn't NULL, it'll be a newly malloced error | |
280 * string.*/ | |
281 int dropbear_listen(const char* address, const char* port, | |
282 int *socks, unsigned int sockcount, char **errstring, int *maxfd) { | |
283 | |
284 struct addrinfo hints, *res = NULL, *res0 = NULL; | |
285 int err; | |
286 unsigned int nsock; | |
287 struct linger linger; | |
288 int val; | |
289 int sock; | |
290 | |
291 TRACE(("enter dropbear_listen")) | |
292 | |
293 memset(&hints, 0, sizeof(hints)); | |
294 hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */ | |
295 hints.ai_socktype = SOCK_STREAM; | |
296 | |
297 /* for calling getaddrinfo: | |
298 address == NULL and !AI_PASSIVE: local loopback | |
299 address == NULL and AI_PASSIVE: all interfaces | |
300 address != NULL: whatever the address says */ | |
301 if (!address) { | |
302 TRACE(("dropbear_listen: local loopback")) | |
303 } else { | |
304 if (address[0] == '\0') { | |
305 TRACE(("dropbear_listen: all interfaces")) | |
306 address = NULL; | |
307 } | |
308 hints.ai_flags = AI_PASSIVE; | |
309 } | |
310 err = getaddrinfo(address, port, &hints, &res0); | |
311 | |
312 if (err) { | |
313 if (errstring != NULL && *errstring == NULL) { | |
314 int len; | |
315 len = 20 + strlen(gai_strerror(err)); | |
316 *errstring = (char*)m_malloc(len); | |
317 snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err)); | |
318 } | |
319 if (res0) { | |
320 freeaddrinfo(res0); | |
321 res0 = NULL; | |
322 } | |
323 TRACE(("leave dropbear_listen: failed resolving")) | |
324 return -1; | |
325 } | |
326 | |
327 | |
328 nsock = 0; | |
329 for (res = res0; res != NULL && nsock < sockcount; | |
330 res = res->ai_next) { | |
331 | |
332 /* Get a socket */ | |
333 socks[nsock] = socket(res->ai_family, res->ai_socktype, | |
334 res->ai_protocol); | |
335 | |
336 sock = socks[nsock]; /* For clarity */ | |
337 | |
338 if (sock < 0) { | |
339 err = errno; | |
340 TRACE(("socket() failed")) | |
341 continue; | |
342 } | |
343 | |
344 /* Various useful socket options */ | |
345 val = 1; | |
346 /* set to reuse, quick timeout */ | |
347 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof(val)); | |
348 linger.l_onoff = 1; | |
349 linger.l_linger = 5; | |
350 setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger)); | |
351 | |
352 #if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) | |
353 if (res->ai_family == AF_INET6) { | |
354 int on = 1; | |
355 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, | |
356 &on, sizeof(on)) == -1) { | |
357 dropbear_log(LOG_WARNING, "Couldn't set IPV6_V6ONLY"); | |
358 } | |
359 } | |
360 #endif | |
361 | |
362 set_sock_nodelay(sock); | |
363 | |
364 if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { | |
365 err = errno; | |
366 close(sock); | |
367 TRACE(("bind(%s) failed", port)) | |
368 continue; | |
369 } | |
370 | |
371 if (listen(sock, DROPBEAR_LISTEN_BACKLOG) < 0) { | |
372 err = errno; | |
373 close(sock); | |
374 TRACE(("listen() failed")) | |
375 continue; | |
376 } | |
377 | |
378 *maxfd = MAX(*maxfd, sock); | |
379 | |
380 nsock++; | |
381 } | |
382 | |
383 if (res0) { | |
384 freeaddrinfo(res0); | |
385 res0 = NULL; | |
386 } | |
387 | |
388 if (nsock == 0) { | |
389 if (errstring != NULL && *errstring == NULL) { | |
390 int len; | |
391 len = 20 + strlen(strerror(err)); | |
392 *errstring = (char*)m_malloc(len); | |
393 snprintf(*errstring, len, "Error listening: %s", strerror(err)); | |
394 } | |
395 TRACE(("leave dropbear_listen: failure, %s", strerror(err))) | |
396 return -1; | |
397 } | |
398 | |
399 TRACE(("leave dropbear_listen: success, %d socks bound", nsock)) | |
400 return nsock; | |
401 } | |
402 | |
403 /* Connect to a given unix socket. The socket is blocking */ | 216 /* Connect to a given unix socket. The socket is blocking */ |
404 #ifdef ENABLE_CONNECT_UNIX | 217 #ifdef ENABLE_CONNECT_UNIX |
405 int connect_unix(const char* path) { | 218 int connect_unix(const char* path) { |
406 struct sockaddr_un addr; | 219 struct sockaddr_un addr; |
407 int fd = -1; | 220 int fd = -1; |
418 TRACE(("Failed to connect to '%s' socket", path)) | 231 TRACE(("Failed to connect to '%s' socket", path)) |
419 m_close(fd); | 232 m_close(fd); |
420 return -1; | 233 return -1; |
421 } | 234 } |
422 return fd; | 235 return fd; |
423 } | |
424 #endif | |
425 | |
426 #if defined(__linux__) && defined(TCP_DEFER_ACCEPT) | |
427 static void set_piggyback_ack(int sock) { | |
428 /* Undocumented Linux feature - set TCP_DEFER_ACCEPT and data will be piggybacked | |
429 on the 3rd packet (ack) of the TCP handshake. Saves a IP packet. | |
430 http://thread.gmane.org/gmane.linux.network/224627/focus=224727 | |
431 "Piggyback the final ACK of the three way TCP connection establishment with the data" */ | |
432 int val = 1; | |
433 /* No error checking, this is opportunistic */ | |
434 int err = setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, (void*)&val, sizeof(val)); | |
435 if (err) | |
436 { | |
437 TRACE(("Failed setsockopt TCP_DEFER_ACCEPT: %s", strerror(errno))) | |
438 } | |
439 } | 236 } |
440 #endif | 237 #endif |
441 | 238 |
442 /* 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 |
443 * 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, |
570 for (i = 3; i <= maxfd; i++) { | 367 for (i = 3; i <= maxfd; i++) { |
571 m_close(i); | 368 m_close(i); |
572 } | 369 } |
573 | 370 |
574 execv(usershell, argv); | 371 execv(usershell, argv); |
575 } | |
576 | |
577 void get_socket_address(int fd, char **local_host, char **local_port, | |
578 char **remote_host, char **remote_port, int host_lookup) | |
579 { | |
580 struct sockaddr_storage addr; | |
581 socklen_t addrlen; | |
582 | |
583 if (local_host || local_port) { | |
584 addrlen = sizeof(addr); | |
585 if (getsockname(fd, (struct sockaddr*)&addr, &addrlen) < 0) { | |
586 dropbear_exit("Failed socket address: %s", strerror(errno)); | |
587 } | |
588 getaddrstring(&addr, local_host, local_port, host_lookup); | |
589 } | |
590 if (remote_host || remote_port) { | |
591 addrlen = sizeof(addr); | |
592 if (getpeername(fd, (struct sockaddr*)&addr, &addrlen) < 0) { | |
593 dropbear_exit("Failed socket address: %s", strerror(errno)); | |
594 } | |
595 getaddrstring(&addr, remote_host, remote_port, host_lookup); | |
596 } | |
597 } | |
598 | |
599 /* Return a string representation of the socket address passed. The return | |
600 * value is allocated with malloc() */ | |
601 void getaddrstring(struct sockaddr_storage* addr, | |
602 char **ret_host, char **ret_port, | |
603 int host_lookup) { | |
604 | |
605 char host[NI_MAXHOST+1], serv[NI_MAXSERV+1]; | |
606 unsigned int len; | |
607 int ret; | |
608 | |
609 int flags = NI_NUMERICSERV | NI_NUMERICHOST; | |
610 | |
611 #ifndef DO_HOST_LOOKUP | |
612 host_lookup = 0; | |
613 #endif | |
614 | |
615 if (host_lookup) { | |
616 flags = NI_NUMERICSERV; | |
617 } | |
618 | |
619 len = sizeof(struct sockaddr_storage); | |
620 /* Some platforms such as Solaris 8 require that len is the length | |
621 * of the specific structure. Some older linux systems (glibc 2.1.3 | |
622 * such as debian potato) have sockaddr_storage.__ss_family instead | |
623 * but we'll ignore them */ | |
624 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY | |
625 if (addr->ss_family == AF_INET) { | |
626 len = sizeof(struct sockaddr_in); | |
627 } | |
628 #ifdef AF_INET6 | |
629 if (addr->ss_family == AF_INET6) { | |
630 len = sizeof(struct sockaddr_in6); | |
631 } | |
632 #endif | |
633 #endif | |
634 | |
635 ret = getnameinfo((struct sockaddr*)addr, len, host, sizeof(host)-1, | |
636 serv, sizeof(serv)-1, flags); | |
637 | |
638 if (ret != 0) { | |
639 if (host_lookup) { | |
640 /* On some systems (Darwin does it) we get EINTR from getnameinfo | |
641 * somehow. Eew. So we'll just return the IP, since that doesn't seem | |
642 * to exhibit that behaviour. */ | |
643 getaddrstring(addr, ret_host, ret_port, 0); | |
644 return; | |
645 } else { | |
646 /* if we can't do a numeric lookup, something's gone terribly wrong */ | |
647 dropbear_exit("Failed lookup: %s", gai_strerror(ret)); | |
648 } | |
649 } | |
650 | |
651 if (ret_host) { | |
652 *ret_host = m_strdup(host); | |
653 } | |
654 if (ret_port) { | |
655 *ret_port = m_strdup(serv); | |
656 } | |
657 } | 372 } |
658 | 373 |
659 #ifdef DEBUG_TRACE | 374 #ifdef DEBUG_TRACE |
660 void printhex(const char * label, const unsigned char * buf, int len) { | 375 void printhex(const char * label, const unsigned char * buf, int len) { |
661 | 376 |
979 /* Fallback for everything else - this will sometimes go backwards */ | 694 /* Fallback for everything else - this will sometimes go backwards */ |
980 return time(NULL); | 695 return time(NULL); |
981 } | 696 } |
982 | 697 |
983 | 698 |
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 char* errstring; | |
1000 }; | |
1001 | |
1002 /* Deallocate a progress connection. Removes from the pending list if iter!=NULL. | |
1003 Does not close sockets */ | |
1004 static void remove_connect(struct dropbear_progress_connection *c, m_list_elem *iter) { | |
1005 if (c->res) { | |
1006 freeaddrinfo(c->res); | |
1007 } | |
1008 m_free(c->remotehost); | |
1009 m_free(c->remoteport); | |
1010 m_free(c->errstring); | |
1011 m_free(c); | |
1012 | |
1013 if (iter) { | |
1014 list_remove(iter); | |
1015 } | |
1016 } | |
1017 | |
1018 static void cancel_callback(int result, int sock, void* UNUSED(data), const char* UNUSED(errstring)) { | |
1019 if (result == DROPBEAR_SUCCESS) | |
1020 { | |
1021 m_close(sock); | |
1022 } | |
1023 } | |
1024 | |
1025 void cancel_connect(struct dropbear_progress_connection *c) { | |
1026 c->cb = cancel_callback; | |
1027 c->cb_data = NULL; | |
1028 } | |
1029 | |
1030 static void connect_try_next(struct dropbear_progress_connection *c) { | |
1031 int err = EADDRNOTAVAIL; | |
1032 struct addrinfo *r; | |
1033 | |
1034 if (!c->res_iter) { | |
1035 return; | |
1036 } | |
1037 | |
1038 for (r = c->res_iter; r; r = r->ai_next) | |
1039 { | |
1040 assert(c->sock == -1); | |
1041 | |
1042 c->sock = socket(c->res_iter->ai_family, c->res_iter->ai_socktype, c->res_iter->ai_protocol); | |
1043 if (c->sock < 0) { | |
1044 err = errno; | |
1045 continue; | |
1046 } | |
1047 | |
1048 ses.maxfd = MAX(ses.maxfd, c->sock); | |
1049 setnonblocking(c->sock); | |
1050 | |
1051 #if defined(__linux__) && defined(TCP_DEFER_ACCEPT) | |
1052 //set_piggyback_ack(c->sock); | |
1053 #endif | |
1054 | |
1055 #ifdef PROGRESS_CONNECT_FALLBACK | |
1056 #if 0 | |
1057 if (connect(c->sock, r->ai_addr, r->ai_addrlen) < 0) { | |
1058 if (errno == EINPROGRESS) { | |
1059 TRACE(("Connect in progress")) | |
1060 break; | |
1061 } else { | |
1062 err = errno; | |
1063 close(c->sock); | |
1064 c->sock = -1; | |
1065 continue; | |
1066 } | |
1067 } | |
1068 | |
1069 break; /* Success. Treated the same as EINPROGRESS */ | |
1070 #endif | |
1071 #else | |
1072 { | |
1073 struct msghdr message; | |
1074 int flags; | |
1075 int res; | |
1076 memset(&message, 0x0, sizeof(message)); | |
1077 message.msg_name = r->ai_addr; | |
1078 message.msg_namelen = r->ai_addrlen; | |
1079 | |
1080 if (c->writequeue) { | |
1081 int iovlen; /* Linux msg_iovlen is a size_t */ | |
1082 message.msg_iov = packet_queue_to_iovec(c->writequeue, &iovlen); | |
1083 message.msg_iovlen = iovlen; | |
1084 res = sendmsg(c->sock, &message, MSG_FASTOPEN); | |
1085 if (res < 0 && errno == EOPNOTSUPP) { | |
1086 TRACE(("Fastopen not supported")); | |
1087 /* No kernel MSG_FASTOPEN support. Fall back below */ | |
1088 c->writequeue = NULL; | |
1089 } | |
1090 m_free(message.msg_iov); | |
1091 if (res > 0) { | |
1092 packet_queue_consume(c->writequeue, res); | |
1093 } | |
1094 } | |
1095 | |
1096 if (!c->writequeue) { | |
1097 res = connect(c->sock, r->ai_addr, r->ai_addrlen); | |
1098 } | |
1099 if (res < 0 && errno != EINPROGRESS) { | |
1100 err = errno; | |
1101 close(c->sock); | |
1102 c->sock = -1; | |
1103 continue; | |
1104 } else { | |
1105 break; | |
1106 } | |
1107 } | |
1108 #endif | |
1109 } | |
1110 | |
1111 | |
1112 if (r) { | |
1113 c->res_iter = r->ai_next; | |
1114 } else { | |
1115 c->res_iter = NULL; | |
1116 } | |
1117 | |
1118 if (c->sock >= 0 || (errno == EINPROGRESS)) { | |
1119 /* Success */ | |
1120 set_sock_nodelay(c->sock); | |
1121 return; | |
1122 } else { | |
1123 if (!c->res_iter) | |
1124 { | |
1125 | |
1126 } | |
1127 /* XXX - returning error message through */ | |
1128 #if 0 | |
1129 /* Failed */ | |
1130 if (errstring != NULL && *errstring == NULL) { | |
1131 int len; | |
1132 len = 20 + strlen(strerror(err)); | |
1133 *errstring = (char*)m_malloc(len); | |
1134 snprintf(*errstring, len, "Error connecting: %s", strerror(err)); | |
1135 } | |
1136 TRACE(("Error connecting: %s", strerror(err))) | |
1137 #endif | |
1138 } | |
1139 } | |
1140 | |
1141 /* Connect via TCP to a host. */ | |
1142 struct dropbear_progress_connection *connect_remote(const char* remotehost, const char* remoteport, | |
1143 connect_callback cb, void* cb_data) | |
1144 { | |
1145 struct dropbear_progress_connection *c = NULL; | |
1146 int err; | |
1147 struct addrinfo hints; | |
1148 | |
1149 c = m_malloc(sizeof(*c)); | |
1150 c->remotehost = m_strdup(remotehost); | |
1151 c->remoteport = m_strdup(remoteport); | |
1152 c->sock = -1; | |
1153 c->cb = cb; | |
1154 c->cb_data = cb_data; | |
1155 | |
1156 list_append(&ses.conn_pending, c); | |
1157 | |
1158 memset(&hints, 0, sizeof(hints)); | |
1159 hints.ai_socktype = SOCK_STREAM; | |
1160 hints.ai_family = PF_UNSPEC; | |
1161 | |
1162 err = getaddrinfo(remotehost, remoteport, &hints, &c->res); | |
1163 if (err) { | |
1164 int len; | |
1165 len = 100 + strlen(gai_strerror(err)); | |
1166 c->errstring = (char*)m_malloc(len); | |
1167 snprintf(c->errstring, len, "Error resolving '%s' port '%s'. %s", | |
1168 remotehost, remoteport, gai_strerror(err)); | |
1169 TRACE(("Error resolving: %s", gai_strerror(err))) | |
1170 return NULL; | |
1171 } | |
1172 | |
1173 c->res_iter = c->res; | |
1174 | |
1175 return c; | |
1176 } | |
1177 | |
1178 | |
1179 void set_connect_fds(fd_set *writefd) { | |
1180 m_list_elem *iter; | |
1181 TRACE(("enter handle_connect_fds")) | |
1182 for (iter = ses.conn_pending.first; iter; iter = iter->next) { | |
1183 struct dropbear_progress_connection *c = iter->item; | |
1184 /* Set one going */ | |
1185 while (c->res_iter && c->sock < 0) | |
1186 { | |
1187 connect_try_next(c); | |
1188 } | |
1189 if (c->sock >= 0) { | |
1190 FD_SET(c->sock, writefd); | |
1191 } else { | |
1192 m_list_elem *remove_iter; | |
1193 /* Final failure */ | |
1194 if (!c->errstring) { | |
1195 c->errstring = m_strdup("unexpected failure"); | |
1196 } | |
1197 c->cb(DROPBEAR_FAILURE, -1, c->cb_data, c->errstring); | |
1198 /* Safely remove without invalidating iter */ | |
1199 remove_iter = iter; | |
1200 iter = iter->prev; | |
1201 remove_connect(c, remove_iter); | |
1202 } | |
1203 } | |
1204 } | |
1205 | |
1206 void handle_connect_fds(fd_set *writefd) { | |
1207 m_list_elem *iter; | |
1208 TRACE(("enter handle_connect_fds")) | |
1209 for (iter = ses.conn_pending.first; iter; iter = iter->next) { | |
1210 int val; | |
1211 socklen_t vallen = sizeof(val); | |
1212 struct dropbear_progress_connection *c = iter->item; | |
1213 | |
1214 if (!FD_ISSET(c->sock, writefd)) { | |
1215 continue; | |
1216 } | |
1217 | |
1218 TRACE(("handling %s port %s socket %d", c->remotehost, c->remoteport, c->sock)); | |
1219 | |
1220 if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &val, &vallen) != 0) { | |
1221 TRACE(("handle_connect_fds getsockopt(%d) SO_ERROR failed: %s", c->sock, strerror(errno))) | |
1222 /* This isn't expected to happen - Unix has surprises though, continue gracefully. */ | |
1223 m_close(c->sock); | |
1224 c->sock = -1; | |
1225 } else if (val != 0) { | |
1226 /* Connect failed */ | |
1227 TRACE(("connect to %s port %s failed.", c->remotehost, c->remoteport)) | |
1228 m_close(c->sock); | |
1229 c->sock = -1; | |
1230 | |
1231 m_free(c->errstring); | |
1232 c->errstring = strerror(val); | |
1233 } else { | |
1234 /* New connection has been established */ | |
1235 c->cb(DROPBEAR_SUCCESS, c->sock, c->cb_data, NULL); | |
1236 remove_connect(c, iter); | |
1237 TRACE(("leave handle_connect_fds - success")) | |
1238 /* Must return here - remove_connect() invalidates iter */ | |
1239 return; | |
1240 } | |
1241 } | |
1242 TRACE(("leave handle_connect_fds - end iter")) | |
1243 } | |
1244 | |
1245 void connect_set_writequeue(struct dropbear_progress_connection *c, struct Queue *writequeue) { | |
1246 c->writequeue = writequeue; | |
1247 } |