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 }