/* XXX: hacktastic but should return a valid file from pacman's cache */ static void handle_file_request(const char *path, int client_fd) { struct iobuf buf; struct stat st; int ret; iobuf_init(&buf, 10); iobuf_append(&buf, "HTTP/1.1 ", 9); iobuf_append(&buf, "200 ", 4); iobuf_append(&buf, "OK\r\n", 4); _cleanup_free_ char *filename = joinpath(".", path, NULL); _cleanup_free_ char *content_length = NULL; _cleanup_close_ int fd = open(filename, O_RDONLY); if (fd < 0) err(EXIT_FAILURE, "couldn't access %s", filename); fstat(fd, &st); ssize_t nbytes_w = asprintf(&content_length, "%s: %zd\r\n", "Content-Length", st.st_size); if (nbytes_w < 0) err(1, "failed to allocate memory for Content-Length"); iobuf_append(&buf, content_length, nbytes_w); iobuf_append(&buf, "\r\n", 2); /* write out header */ iobuf_write(&buf, client_fd); ret = sendfile(client_fd, fd, NULL, st.st_size); if (ret < 0) err(EXIT_FAILURE, "failed to send file %s across socket", filename); }
/* TODO: this doesn't need any arguments */ static void http_request_done(void *data, const char _unused_ *at, size_t _unused_ len) { struct http_data *header = data; /* TODO: redo how callbacks are set */ if (header->request_type != REQUEST_URI) return; iobuf_append(&header->p.request, "Connection: Close\r\n", 19); iobuf_append(&header->p.request, "\r\n", 2); }
static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) { if (nc->flags & NSF_UDP) { long n = sendto(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); DBG(("%p %d send %ld (%d %s)", nc, nc->sock, n, errno, strerror(errno))); return n < 0 ? 0 : n; } else { return iobuf_append(&nc->send_iobuf, buf, len); } }
static void http_request_version(void *data, const char *at, size_t len) { /* XXX: do we really need to pass this on verbatum? Lets just * automatically upgrade to HTTP 1.1 */ struct http_data *header = data; /* TODO: redo how callbacks are set */ if (header->request_type != REQUEST_URI) return; iobuf_append(&header->p.request, at, len + 2); }
static void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen) { struct http_data *header = data; /* TODO: redo how callbacks are set */ if (header->request_type != REQUEST_URI) return; if (strncmp("Host", field, flen) == 0) { iobuf_append(&header->p.request, "Host: ", 6); iobuf_append(&header->p.request, header->p.hostname, strlen(header->p.hostname)); iobuf_append(&header->p.request, "\r\n", 2); return; } else if (strncmp("If-Modified-Since", field, flen) == 0) { header->modified = strndup(value, vlen); } else if (strncmp("Proxy-Connection", field, flen) == 0) { return; } iobuf_append(&header->p.request, field, flen + 2); iobuf_append(&header->p.request, value, vlen + 2); }
static void http_request_uri(void *data, const char *at, size_t len) { struct http_data *header = data; /* *header->p.v++ = (struct iovec){ (void *)at + 2, len - 2 + 1 }; */ if (strncmp(at, "//", 2) == 0) { header->request_type = REQUEST_URI; } else { header->request_type = REQUEST_PATH; } switch (header->request_type) { case REQUEST_URI: /* TODO: properly init proxy response */ iobuf_init(&header->p.request, 10); header->p.hostname = strndup(at + 2, len - 3); /** -3 to avoid the /, parser broken? */ header->p.fd = connect_to(header->p.hostname, "http"); iobuf_append(&header->p.request, header->method, header->method_len + 1); iobuf_append(&header->p.request, "/ ", 2); /* TODO: store the lenght to avoid the strlen later */ header->path = strndup(at, len); /* TODO: check if uri or path, this should affect below */ /* header->http_request_version = http_request_version; */ /* header->http_field = http_field; */ break; case REQUEST_PATH: header->path = strndup(at, len); printf("requesting PATH=%s\n", header->path); break; default: break; } }
static const char *test_hexdump_file(void) { const char *path = "test_hexdump"; const char *want = "0xbeef :0 -> :0 3\n" "0000 66 6f 6f " " foo\n\n"; char *data, *got; size_t size; struct ns_connection *nc = (struct ns_connection *) calloc(1, sizeof(*nc)); /* "In the GNU system, non-null pointers are printed as unsigned integers, * as if a `%#x' conversion were used. Null pointers print as `(nil)'. * (Pointers might print differently in other systems.)" * indeed it prints 0x0 on apple. */ nc->user_data = (void *)0xbeef; truncate(path, 0); iobuf_append(&nc->send_iobuf, "foo", 3); iobuf_append(&nc->recv_iobuf, "bar", 3); ns_hexdump_connection(nc, path, 3, NS_SEND); iobuf_free(&nc->send_iobuf); iobuf_free(&nc->recv_iobuf); free(nc); ASSERT((data = read_file(path, &size)) != NULL); unlink(path); got = data; while(got-data < (int)size && *got++ != ' '); size -= got-data; ASSERT(strncmp(got, want, size) == 0); free(data); return NULL; }
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; } }
void uv_custom_poll_cb(uv_poll_t *req, int status, int events) { /* * Make sure we execute in the main thread */ const uv_thread_t pth_cur_id = uv_thread_self(); assert(uv_thread_equal(&pth_main_id, &pth_cur_id)); struct uv_custom_poll_t *custom_poll_data = NULL; struct iobuf_t *send_io = NULL; char buffer[BUFFER_SIZE]; uv_os_fd_t fd = 0; long int fromlen = 0; int r = 0, n = 0; custom_poll_data = req->data; if(custom_poll_data == NULL) { uv_poll_stop(req); return; } /* * Status == -9: Socket is unreachable * Events == 0: Client-end got disconnected */ r = uv_fileno((uv_handle_t *)req, &fd); if(status < 0 || events == 0) { if(status == -9) { logprintf(LOG_ERR, "uv_custom_poll_cb: socket not responding"); } else { logprintf(LOG_ERR, "uv_custom_poll_cb: %s", uv_strerror(status)); } if(custom_poll_data->close_cb != NULL) { custom_poll_data->close_cb(req); } if(!uv_is_closing((uv_handle_t *)req)) { uv_poll_stop(req); } if(fd > 0) { close(fd); } return; } custom_poll_data->started = 1; send_io = &custom_poll_data->send_iobuf; memset(&buffer, 0, BUFFER_SIZE); if(uv_is_closing((uv_handle_t *)req)) { return; } r = uv_fileno((uv_handle_t *)req, &fd); if(r != 0) { logprintf(LOG_ERR, "uv_fileno: %s", uv_strerror(r)); return; } if(custom_poll_data->is_ssl == 1 && custom_poll_data->ssl.init == 0) { custom_poll_data->ssl.init = 1; struct mbedtls_ssl_config *ssl_conf = &ssl_client_conf; if(custom_poll_data->is_server == 1) { custom_poll_data->ssl.handshake = 1; ssl_conf = &ssl_server_conf; } if((r = mbedtls_ssl_setup(&custom_poll_data->ssl.ctx, ssl_conf)) < 0) { mbedtls_strerror(r, (char *)&buffer, BUFFER_SIZE); logprintf(LOG_ERR, "mbedtls_ssl_setup: %s", buffer); FREE(req); return; } if((r = mbedtls_ssl_session_reset(&custom_poll_data->ssl.ctx)) < 0) { mbedtls_strerror(r, (char *)&buffer, BUFFER_SIZE); logprintf(LOG_ERR, "mbedtls_ssl_session_reset: %s", buffer); FREE(req); return; } // mbedtls_debug_set_threshold(2); mbedtls_ssl_set_bio(&custom_poll_data->ssl.ctx, &fd, mbedtls_net_send, mbedtls_net_recv, NULL); mbedtls_ssl_conf_dbg(ssl_conf, my_debug, stdout); if(custom_poll_data->host != NULL) { mbedtls_ssl_set_hostname(&custom_poll_data->ssl.ctx, custom_poll_data->host); } } if(custom_poll_data->is_ssl == 1 && custom_poll_data->ssl.handshake == 0) { n = mbedtls_ssl_handshake(&custom_poll_data->ssl.ctx); if(n == MBEDTLS_ERR_SSL_WANT_READ) { custom_poll_data->doread = 1; custom_poll_data->dowrite = 0; goto end; } else if(n == MBEDTLS_ERR_SSL_WANT_WRITE) { /*LCOV_EXCL_START*/ custom_poll_data->dowrite = 1; custom_poll_data->doread = 0; goto end; /*LCOV_EXCL_STOP*/ } if(n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) { } else if(n < 0) { /*LCOV_EXCL_START*/ mbedtls_strerror(n, (char *)&buffer, BUFFER_SIZE); logprintf(LOG_NOTICE, "mbedtls_ssl_handshake: %s", buffer); uv_poll_stop(req); return; /*LCOV_EXCL_STOP*/ } else { custom_poll_data->ssl.handshake = 1; } custom_poll_data->dowrite = 1; goto end; } if(events & UV_WRITABLE) { if(send_io->len > 0) { if(custom_poll_data->is_ssl == 1) { n = mbedtls_ssl_write(&custom_poll_data->ssl.ctx, (unsigned char *)send_io->buf, send_io->len); if(n == MBEDTLS_ERR_SSL_WANT_READ) { /*LCOV_EXCL_START*/ custom_poll_data->doread = 1; custom_poll_data->dowrite = 0; goto end; /*LCOV_EXCL_STOP*/ } else if(n == MBEDTLS_ERR_SSL_WANT_WRITE) { /*LCOV_EXCL_START*/ custom_poll_data->dowrite = 1; custom_poll_data->doread = 0; goto end; /*LCOV_EXCL_STOP*/ } if(n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) { } else if(n < 0) { /*LCOV_EXCL_START*/ mbedtls_strerror(n, (char *)&buffer, BUFFER_SIZE); logprintf(LOG_NOTICE, "mbedtls_ssl_handshake: %s", buffer); uv_poll_stop(req); return; /*LCOV_EXCL_STOP*/ } } else { n = (int)send((unsigned int)fd, send_io->buf, send_io->len, 0); } if(n > 0) { iobuf_remove(send_io, n); if(send_io->len > 0) { custom_poll_data->dowrite = 1; } else { custom_poll_data->dowrite = 0; if(custom_poll_data->doclose == 1 && send_io->len == 0) { custom_poll_data->doread = 0; goto end; } else { custom_poll_data->dowrite = 0; if(custom_poll_data->write_cb != NULL) { custom_poll_data->write_cb(req); } } } } else if(n == 0) { } else if(custom_poll_data->is_ssl == 0 && n < 0 && errno != EAGAIN && errno != EINTR) { if(errno == ECONNRESET) { uv_poll_stop(req); return; } else { uv_poll_stop(req); return; } } } else { custom_poll_data->dowrite = 0; if(custom_poll_data->doclose == 1 && send_io->len == 0) { custom_poll_data->doread = 0; goto end; } else { custom_poll_data->dowrite = 0; if(custom_poll_data->write_cb != NULL) { custom_poll_data->write_cb(req); } } } } if(send_io->len > 0) { custom_poll_data->dowrite = 1; } if(events & UV_READABLE) { if(custom_poll_data->is_ssl == 1) { n = mbedtls_ssl_read(&custom_poll_data->ssl.ctx, (unsigned char *)buffer, BUFFER_SIZE); if(n == MBEDTLS_ERR_SSL_WANT_READ) { custom_poll_data->doread = 1; custom_poll_data->dowrite = 0; goto end; } else if(n == MBEDTLS_ERR_SSL_WANT_WRITE) { /*LCOV_EXCL_START*/ custom_poll_data->dowrite = 1; custom_poll_data->doread = 0; goto end; /*LCOV_EXCL_STOP*/ } else if(n == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { custom_poll_data->doread = 0; if(custom_poll_data->read_cb != NULL) { custom_poll_data->read_cb(req, &custom_poll_data->recv_iobuf.len, custom_poll_data->recv_iobuf.buf); } } if(n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) { } else if(n < 0) { if(n == MBEDTLS_ERR_NET_RECV_FAILED) { /* * FIXME: New client not yet accepted */ if(custom_poll_data->read_cb != NULL) { one = 1; custom_poll_data->read_cb(req, &one, NULL); } } else if(n != MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { mbedtls_strerror(n, (char *)&buffer, BUFFER_SIZE); logprintf(LOG_NOTICE, "mbedtls_ssl_handshake: %s", buffer); uv_poll_stop(req); } return; } } else { if(custom_poll_data->custom_recv == 0) { if(custom_poll_data->is_udp == 1) { n = (int)recv((unsigned int)fd, buffer, BUFFER_SIZE, 0); } else { #ifdef _WIN32 n = recvfrom((SOCKET)fd, buffer, BUFFER_SIZE, 0, NULL, (socklen_t *)&fromlen); #else n = recvfrom(fd, buffer, BUFFER_SIZE, 0, NULL, (socklen_t *)&fromlen); #endif } } } if(custom_poll_data->custom_recv == 0) { if(n > 0) { iobuf_append(&custom_poll_data->recv_iobuf, buffer, n); custom_poll_data->doread = 0; if(custom_poll_data->read_cb != NULL) { custom_poll_data->read_cb(req, &custom_poll_data->recv_iobuf.len, custom_poll_data->recv_iobuf.buf); } } else if(n < 0 && errno != EINTR) { #ifdef _WIN32 switch(WSAGetLastError()) { case WSAENOTCONN: if(custom_poll_data->read_cb != NULL) { one = 1; custom_poll_data->read_cb(req, &one, NULL); } break; case WSAEWOULDBLOCK: #else switch(errno) { case ENOTCONN: if(custom_poll_data->read_cb != NULL) { one = 1; custom_poll_data->read_cb(req, &one, NULL); } break; #if defined EAGAIN case EAGAIN: #endif #if defined EWOULDBLOCK && EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif #endif custom_poll_data->doread = 1; break; default: break; } /* * Client was disconnected */ } else if(n == 0) { custom_poll_data->doclose = 1; custom_poll_data->doread = 0; goto end; } } else { custom_poll_data->doread = 0; if(custom_poll_data->read_cb != NULL) { zero = 0; custom_poll_data->read_cb(req, &zero, NULL); } } }