static void ns_read_from_socket(struct ns_connection *conn) { char buf[NS_READ_BUFFER_SIZE]; int n = 0; if (conn->flags & NSF_CONNECTING) { int ok = 1, ret; (void) ret; socklen_t len = sizeof(ok); ret = getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &ok, &len); #ifdef NS_ENABLE_SSL if (ret == 0 && ok == 0 && conn->ssl != NULL) { ns_ssl_begin(conn); } #endif DBG(("%p connect ok=%d", conn, ok)); if (ok != 0) { conn->flags |= NSF_CLOSE_IMMEDIATELY; } else { conn->flags &= ~NSF_CONNECTING; } ns_call(conn, NS_CONNECT, &ok); return; } #ifdef NS_ENABLE_SSL if (conn->ssl != NULL) { if (conn->flags & NSF_SSL_HANDSHAKE_DONE) { /* SSL library may have more bytes ready to read then we ask to read. * Therefore, read in a loop until we read everything. Without the loop, * we skip to the next select() cycle which can just timeout. */ while ((n = SSL_read(conn->ssl, buf, sizeof(buf))) > 0) { DBG(("%p %d bytes <- %d (SSL)", conn, n, conn->sock)); mbuf_append(&conn->recv_mbuf, buf, n); ns_call(conn, NS_RECV, &n); } ns_ssl_err(conn, n); } else { ns_ssl_begin(conn); return; } } else #endif { while ((n = (int) NS_RECV_FUNC( conn->sock, buf, recv_avail_size(conn, sizeof(buf)), 0)) > 0) { DBG(("%p %d bytes (PLAIN) <- %d", conn, n, conn->sock)); mbuf_append(&conn->recv_mbuf, buf, n); ns_call(conn, NS_RECV, &n); } } if (ns_is_error(n)) { conn->flags |= NSF_CLOSE_IMMEDIATELY; } }
static void ns_handle_udp(struct ns_connection *ls) { struct ns_connection nc; char buf[NS_UDP_RECEIVE_BUFFER_SIZE]; int n; socklen_t s_len = sizeof(nc.sa); memset(&nc, 0, sizeof(nc)); n = recvfrom(ls->sock, buf, sizeof(buf), 0, &nc.sa.sa, &s_len); if (n <= 0) { DBG(("%p recvfrom: %s", ls, strerror(errno))); } else { nc.mgr = ls->mgr; nc.recv_iobuf.buf = buf; nc.recv_iobuf.len = nc.recv_iobuf.size = n; nc.sock = ls->sock; nc.handler = ls->handler; nc.user_data = ls->user_data; nc.proto_data = ls->proto_data; nc.proto_handler = ls->proto_handler; nc.mgr = ls->mgr; nc.listener = ls; nc.flags = NSF_UDP; DBG(("%p %d bytes received", ls, n)); ns_call(&nc, NS_RECV, &n); } }
static void ns_write_to_socket(struct ns_connection *conn) { struct iobuf *io = &conn->send_iobuf; int n = 0; #ifdef NS_ENABLE_SSL if (conn->ssl != NULL) { n = SSL_write(conn->ssl, io->buf, io->len); if (n <= 0) { int ssl_err = ns_ssl_err(conn, n); if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) { return; /* Call us again */ } else { conn->flags |= NSF_CLOSE_IMMEDIATELY; } } } else #endif { n = (int) send(conn->sock, io->buf, io->len, 0); } DBG(("%p %lu -> %d bytes", conn, conn->flags, n)); ns_call(conn, NS_SEND, &n); if (ns_is_error(n)) { conn->flags |= NSF_CLOSE_IMMEDIATELY; } else if (n > 0) { iobuf_remove(io, n); } }
static struct ns_connection *accept_conn(struct ns_connection *ls) { struct ns_connection *c = NULL; union socket_address sa; socklen_t len = sizeof(sa); sock_t sock = INVALID_SOCKET; /* NOTE(lsm): on Windows, sock is always > FD_SETSIZE */ if ((sock = accept(ls->sock, &sa.sa, &len)) == INVALID_SOCKET) { } else if ((c = ns_add_sock(ls->mgr, sock, ls->handler)) == NULL) { closesocket(sock); #ifdef NS_ENABLE_SSL } else if (ls->ssl_ctx != NULL && ((c->ssl = SSL_new(ls->ssl_ctx)) == NULL || SSL_set_fd(c->ssl, sock) != 1)) { DBG(("SSL error")); ns_close_conn(c); c = NULL; #endif } else { c->listener = ls; c->proto_data = ls->proto_data; c->proto_handler = ls->proto_handler; c->user_data = ls->user_data; ns_call(c, NS_ACCEPT, &sa); DBG(("%p %d %p %p", c, c->sock, c->ssl_ctx, c->ssl)); } return c; }
static void ns_handle_udp(struct ns_connection *ls) { struct ns_connection nc; char buf[NS_UDP_RECEIVE_BUFFER_SIZE]; int n; socklen_t s_len = sizeof(nc.sa); memset(&nc, 0, sizeof(nc)); n = recvfrom(ls->sock, buf, sizeof(buf), 0, &nc.sa.sa, &s_len); if (n <= 0) { DBG(("%p recvfrom: %s", ls, strerror(errno))); } else { union socket_address sa = nc.sa; /* Copy all attributes, preserving sender address */ nc = *ls; /* Then override some */ nc.sa = sa; nc.recv_mbuf.buf = buf; nc.recv_mbuf.len = nc.recv_mbuf.size = n; nc.listener = ls; nc.flags = NSF_UDP; /* Call NS_RECV handler */ DBG(("%p %d bytes received", ls, n)); ns_call(&nc, NS_RECV, &n); /* * See https://github.com/cesanta/fossa/issues/207 * ns_call migth set flags. They need to be synced back to ls. */ ls->flags = nc.flags; } }
/* * Callback for the async resolver on ns_connect_opt() call. * Main task of this function is to trigger NS_CONNECT event with * either failure (and dealloc the connection) * or success (and proceed with connect() */ static void resolve_cb(struct ns_dns_message *msg, void *data) { struct ns_connection *nc = (struct ns_connection *) data; int i; int failure = -1; if (msg != NULL) { /* * Take the first DNS A answer and run... */ for (i = 0; i < msg->num_answers; i++) { if (msg->answers[i].rtype == NS_DNS_A_RECORD) { static struct ns_add_sock_opts opts; /* * Async resolver guarantees that there is at least one answer. * TODO(lsm): handle IPv6 answers too */ ns_dns_parse_record_data(msg, &msg->answers[i], &nc->sa.sin.sin_addr, 4); /* Make ns_finish_connect() trigger NS_CONNECT on failure */ nc->flags |= NSF_CONNECTING; ns_finish_connect(nc, nc->flags & NSF_UDP ? SOCK_DGRAM : SOCK_STREAM, &nc->sa, opts); return; } } } /* * If we get there was no NS_DNS_A_RECORD in the answer */ ns_call(nc, NS_CONNECT, &failure); ns_destroy_conn(nc); }
static void ns_close_conn(struct ns_connection *conn) { DBG(("%p %lu", conn, conn->flags)); if (!(conn->flags & NSF_CONNECTING)) { ns_call(conn, NS_CLOSE, NULL); } ns_remove_conn(conn); ns_destroy_conn(conn); }
/* * Schedules an async connect for a resolved address and proto. * Called from two places: `ns_connect_opt()` and from async resolver. * When called from the async resolver, it must trigger `NS_CONNECT` event * with a failure flag to indicate connection failure. */ NS_INTERNAL struct ns_connection *ns_finish_connect(struct ns_connection *nc, int proto, union socket_address *sa, struct ns_add_sock_opts o) { sock_t sock = INVALID_SOCKET; int rc; DBG(("%p %s://%s:%hu", nc, proto == SOCK_DGRAM ? "udp" : "tcp", inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) { int failure = errno; NS_SET_PTRPTR(o.error_string, "cannot create socket"); if (nc->flags & NSF_CONNECTING) { ns_call(nc, NS_CONNECT, &failure); } ns_destroy_conn(nc); return NULL; } ns_set_non_blocking_mode(sock); rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa->sa, sizeof(sa->sin)); if (rc != 0 && ns_is_error(rc)) { NS_SET_PTRPTR(o.error_string, "cannot connect to socket"); if (nc->flags & NSF_CONNECTING) { ns_call(nc, NS_CONNECT, &rc); } ns_destroy_conn(nc); close(sock); return NULL; } /* Fire NS_CONNECT on next poll. */ nc->flags |= NSF_CONNECTING; /* No ns_destroy_conn() call after this! */ ns_set_sock(nc, sock); return nc; }
static void ns_write_to_socket(struct ns_connection *conn) { struct mbuf *io = &conn->send_mbuf; int n = 0; assert(io->len > 0); #ifdef NS_ENABLE_SSL if (conn->ssl != NULL) { if (conn->flags & NSF_SSL_HANDSHAKE_DONE) { n = SSL_write(conn->ssl, io->buf, io->len); if (n <= 0) { int ssl_err = ns_ssl_err(conn, n); if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) { return; /* Call us again */ } else { conn->flags |= NSF_CLOSE_IMMEDIATELY; } } else { /* Successful SSL operation, clear off SSL wait flags */ conn->flags &= ~(NSF_WANT_READ | NSF_WANT_WRITE); } } else { ns_ssl_begin(conn); return; } } else #endif { n = (int) NS_SEND_FUNC(conn->sock, io->buf, io->len, 0); } DBG(("%p %d bytes -> %d", conn, n, conn->sock)); if (ns_is_error(n)) { conn->flags |= NSF_CLOSE_IMMEDIATELY; } else if (n > 0) { #ifndef NS_DISABLE_FILESYSTEM /* LCOV_EXCL_START */ if (conn->mgr->hexdump_file != NULL) { ns_hexdump_connection(conn, conn->mgr->hexdump_file, n, NS_SEND); } /* LCOV_EXCL_STOP */ #endif mbuf_remove(io, n); } ns_call(conn, NS_SEND, &n); }
static void ns_mgr_handle_connection(struct ns_connection *nc, int fd_flags, time_t now) { DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); if (fd_flags != 0) nc->last_io_time = now; if (nc->flags & NSF_CONNECTING) { if (fd_flags != 0) { ns_read_from_socket(nc); } return; } if (nc->flags & NSF_LISTENING) { /* * We're not looping here, and accepting just one connection at * a time. The reason is that eCos does not respect non-blocking * flag on a listening socket and hangs in a loop. */ if (fd_flags & _NSF_FD_CAN_READ) accept_conn(nc); return; } if (fd_flags & _NSF_FD_CAN_READ) { if (nc->flags & NSF_UDP) { ns_handle_udp(nc); } else { ns_read_from_socket(nc); } if (nc->flags & NSF_CLOSE_IMMEDIATELY) return; } if ((fd_flags & _NSF_FD_CAN_WRITE) && !(nc->flags & NSF_DONT_SEND) && !(nc->flags & NSF_UDP)) { /* Writes to UDP sockets are not buffered. */ ns_write_to_socket(nc); } if (!(fd_flags & (_NSF_FD_CAN_READ | _NSF_FD_CAN_WRITE))) { ns_call(nc, NS_POLL, &now); } DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); }
static void ns_ssl_begin(struct ns_connection *nc) { int server_side = nc->listener != NULL; int res = server_side ? SSL_accept(nc->ssl) : SSL_connect(nc->ssl); if (res == 1) { nc->flags |= NSF_SSL_HANDSHAKE_DONE; nc->flags &= ~(NSF_WANT_READ | NSF_WANT_WRITE); if (server_side) { union socket_address sa; socklen_t sa_len = sizeof(sa); /* In case port was set to 0, get the real port number */ (void) getsockname(nc->sock, &sa.sa, &sa_len); ns_call(nc, NS_ACCEPT, &sa); } } else { int ssl_err = ns_ssl_err(nc, res); if (ssl_err != SSL_ERROR_WANT_READ && ssl_err != SSL_ERROR_WANT_WRITE) { nc->flags |= NSF_CLOSE_IMMEDIATELY; } } }
/* * This function performs the actual IO, and must be called in a loop * (an event loop). Returns the current timestamp. */ time_t ns_mgr_poll(struct ns_mgr *mgr, int milli) { struct ns_connection *nc, *tmp; struct timeval tv; fd_set read_set, write_set, err_set; sock_t max_fd = INVALID_SOCKET; time_t current_time = time(NULL); FD_ZERO(&read_set); FD_ZERO(&write_set); FD_ZERO(&err_set); ns_add_to_set(mgr->ctl[1], &read_set, &max_fd); for (nc = mgr->active_connections; nc != NULL; nc = tmp) { tmp = nc->next; if (!(nc->flags & (NSF_LISTENING | NSF_CONNECTING))) { ns_call(nc, NS_POLL, ¤t_time); } /* * NS_POLL handler could have signaled us to close the connection * by setting NSF_CLOSE_IMMEDIATELY flag. In this case, we don't want to * trigger any other events on that connection, but close it right away. */ if (nc->flags & NSF_CLOSE_IMMEDIATELY) { /* NOTE(lsm): this call removes nc from the mgr->active_connections */ ns_close_conn(nc); continue; } if (!(nc->flags & NSF_WANT_WRITE)) { /*DBG(("%p read_set", nc)); */ ns_add_to_set(nc->sock, &read_set, &max_fd); } if (((nc->flags & NSF_CONNECTING) && !(nc->flags & NSF_WANT_READ)) || (nc->send_iobuf.len > 0 && !(nc->flags & NSF_CONNECTING) && !(nc->flags & NSF_BUFFER_BUT_DONT_SEND))) { /*DBG(("%p write_set", nc)); */ ns_add_to_set(nc->sock, &write_set, &max_fd); ns_add_to_set(nc->sock, &err_set, &max_fd); } } tv.tv_sec = milli / 1000; tv.tv_usec = (milli % 1000) * 1000; if (select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv) > 0) { /* select() might have been waiting for a long time, reset current_time * now to prevent last_io_time being set to the past. */ current_time = time(NULL); /* Read wakeup messages */ if (mgr->ctl[1] != INVALID_SOCKET && FD_ISSET(mgr->ctl[1], &read_set)) { struct ctl_msg ctl_msg; int len = (int) recv(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0); send(mgr->ctl[1], ctl_msg.message, 1, 0); if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) { struct ns_connection *c; for (c = ns_next(mgr, NULL); c != NULL; c = ns_next(mgr, c)) { ctl_msg.callback(c, NS_POLL, ctl_msg.message); } } } for (nc = mgr->active_connections; nc != NULL; nc = tmp) { tmp = nc->next; /* Windows reports failed connect() requests in err_set */ if (FD_ISSET(nc->sock, &err_set) && (nc->flags & NSF_CONNECTING)) { nc->last_io_time = current_time; ns_read_from_socket(nc); } if (FD_ISSET(nc->sock, &read_set)) { nc->last_io_time = current_time; if (nc->flags & NSF_LISTENING) { if (nc->flags & NSF_UDP) { ns_handle_udp(nc); } else { /* We're not looping here, and accepting just one connection at * a time. The reason is that eCos does not respect non-blocking * flag on a listening socket and hangs in a loop. */ accept_conn(nc); } } else { ns_read_from_socket(nc); } } if (FD_ISSET(nc->sock, &write_set)) { nc->last_io_time = current_time; if (nc->flags & NSF_CONNECTING) { ns_read_from_socket(nc); } else if (!(nc->flags & NSF_BUFFER_BUT_DONT_SEND) && !(nc->flags & NSF_CLOSE_IMMEDIATELY)) { ns_write_to_socket(nc); } } } } for (nc = mgr->active_connections; nc != NULL; nc = tmp) { tmp = nc->next; if ((nc->flags & NSF_CLOSE_IMMEDIATELY) || (nc->send_iobuf.len == 0 && (nc->flags & NSF_FINISHED_SENDING_DATA))) { ns_close_conn(nc); } } return current_time; }
static void ns_read_from_socket(struct ns_connection *conn) { char buf[NS_READ_BUFFER_SIZE]; int n = 0; if (conn->flags & NSF_CONNECTING) { int ok = 1, ret; socklen_t len = sizeof(ok); ret = getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &ok, &len); #ifdef NS_ENABLE_SSL if (ret == 0 && ok == 0 && conn->ssl != NULL) { int res = SSL_connect(conn->ssl); int ssl_err = ns_ssl_err(conn, res); if (res == 1) { conn->flags |= NSF_SSL_HANDSHAKE_DONE; } else if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) { return; /* Call us again */ } else { ok = 1; } } #endif conn->flags &= ~NSF_CONNECTING; DBG(("%p ok=%d", conn, ok)); if (ok != 0) { conn->flags |= NSF_CLOSE_IMMEDIATELY; } ns_call(conn, NS_CONNECT, &ok); return; } #ifdef NS_ENABLE_SSL if (conn->ssl != NULL) { if (conn->flags & NSF_SSL_HANDSHAKE_DONE) { /* SSL library may have more bytes ready to read then we ask to read. * Therefore, read in a loop until we read everything. Without the loop, * we skip to the next select() cycle which can just timeout. */ while ((n = SSL_read(conn->ssl, buf, sizeof(buf))) > 0) { DBG(("%p %lu <- %d bytes (SSL)", conn, conn->flags, n)); iobuf_append(&conn->recv_iobuf, buf, n); ns_call(conn, NS_RECV, &n); } ns_ssl_err(conn, n); } else { int res = SSL_accept(conn->ssl); int ssl_err = ns_ssl_err(conn, res); if (res == 1) { conn->flags |= NSF_SSL_HANDSHAKE_DONE; } else if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) { return; /* Call us again */ } else { conn->flags |= NSF_CLOSE_IMMEDIATELY; } return; } } else #endif { while ((n = (int) recv(conn->sock, buf, sizeof(buf), 0)) > 0) { DBG(("%p %lu <- %d bytes (PLAIN)", conn, conn->flags, n)); iobuf_append(&conn->recv_iobuf, buf, n); ns_call(conn, NS_RECV, &n); } } if (ns_is_error(n)) { conn->flags |= NSF_CLOSE_IMMEDIATELY; } }
static void ns_close_conn(struct ns_connection *conn) { DBG(("%p %lu", conn, conn->flags)); ns_call(conn, NS_CLOSE, NULL); ns_remove_conn(conn); ns_destroy_conn(conn); }