diff options
Diffstat (limited to 'src/net.c')
-rw-r--r-- | src/net.c | 500 |
1 files changed, 29 insertions, 471 deletions
@@ -23,25 +23,28 @@ #include "log.h" #include "opt.h" -#include "os.h" -#include "strerror.h" #include <errno.h> #include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> #include <string.h> -#include <unistd.h> -#include <errno.h> #include <poll.h> -#include <openssl/err.h> #include <netdb.h> #include <arpa/inet.h> -#define TCP_RECV_BUF_SIZE (16 * 1024) -#define TCP_SEND_BUF_SIZE (4 * 1024) +enum perf_net_mode perf_net_parsemode(const char* mode) +{ + if (!strcmp(mode, "udp")) { + return sock_udp; + } else if (!strcmp(mode, "tcp")) { + return sock_tcp; + } else if (!strcmp(mode, "tls") || !strcmp(mode, "dot")) { + return sock_dot; + } -static SSL_CTX* ssl_ctx = 0; + perf_log_warning("invalid socket mode"); + perf_opt_usage(); + exit(1); +} int perf_net_parsefamily(const char* family) { @@ -49,10 +52,8 @@ int perf_net_parsefamily(const char* family) return AF_UNSPEC; else if (strcmp(family, "inet") == 0) return AF_INET; -#ifdef AF_INET6 else if (strcmp(family, "inet6") == 0) return AF_INET6; -#endif else { fprintf(stderr, "invalid family %s\n", family); perf_opt_usage(); @@ -82,9 +83,9 @@ in_port_t perf_sockaddr_port(const perf_sockaddr_t* sockaddr) { switch (sockaddr->sa.sa.sa_family) { case AF_INET: - return sockaddr->sa.sin.sin_port; + return ntohs(sockaddr->sa.sin.sin_port); case AF_INET6: - return sockaddr->sa.sin6.sin6_port; + return ntohs(sockaddr->sa.sin6.sin6_port); default: break; } @@ -95,10 +96,10 @@ void perf_sockaddr_setport(perf_sockaddr_t* sockaddr, in_port_t port) { switch (sockaddr->sa.sa.sa_family) { case AF_INET: - sockaddr->sa.sin.sin_port = port; + sockaddr->sa.sin.sin_port = htons(port); break; case AF_INET6: - sockaddr->sa.sin6.sin6_port = port; + sockaddr->sa.sin6.sin6_port = htons(port); break; default: break; @@ -188,76 +189,15 @@ void perf_net_parselocal(int family, const char* name, unsigned int port, exit(1); } -struct perf_net_socket perf_net_opensocket(enum perf_net_mode mode, const perf_sockaddr_t* server, const perf_sockaddr_t* local, unsigned int offset, int bufsize) +struct perf_net_socket* perf_net_opensocket(enum perf_net_mode mode, const perf_sockaddr_t* server, const perf_sockaddr_t* local, unsigned int offset, size_t bufsize) { - int family; - perf_sockaddr_t tmp; - int port; - int ret; - int flags; - struct perf_net_socket sock = { .mode = mode, .is_ready = 1 }; - - family = server->sa.sa.sa_family; + int port; + perf_sockaddr_t tmp; - if (local->sa.sa.sa_family != family) { + if (server->sa.sa.sa_family != local->sa.sa.sa_family) { perf_log_fatal("server and local addresses have different families"); } - switch (mode) { - case sock_udp: - sock.fd = socket(family, SOCK_DGRAM, 0); - break; - case sock_tls: - if (pthread_mutex_init(&sock.lock, 0)) { - perf_log_fatal("pthread_mutex_init() failed"); - } - if ((sock.fd = socket(family, SOCK_STREAM, 0)) < 0) { - char __s[256]; - perf_log_fatal("socket: %s", perf_strerror_r(errno, __s, sizeof(__s))); - } - if (!ssl_ctx) { -#ifdef HAVE_TLS_METHOD - if (!(ssl_ctx = SSL_CTX_new(TLS_method()))) { - perf_log_fatal("SSL_CTX_new(): %s", ERR_error_string(ERR_get_error(), 0)); - } - if (!SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION)) { - perf_log_fatal("SSL_CTX_set_min_proto_version(TLS1_2_VERSION): %s", ERR_error_string(ERR_get_error(), 0)); - } -#else - if (!(ssl_ctx = SSL_CTX_new(SSLv23_client_method()))) { - perf_log_fatal("SSL_CTX_new(): %s", ERR_error_string(ERR_get_error(), 0)); - } -#endif - } - if (!(sock.ssl = SSL_new(ssl_ctx))) { - perf_log_fatal("SSL_new(): %s", ERR_error_string(ERR_get_error(), 0)); - } - if (!(ret = SSL_set_fd(sock.ssl, sock.fd))) { - perf_log_fatal("SSL_set_fd(): %s", ERR_error_string(SSL_get_error(sock.ssl, ret), 0)); - } - break; - case sock_tcp: - sock.fd = socket(family, SOCK_STREAM, 0); - break; - default: - perf_log_fatal("perf_net_opensocket(): invalid mode"); - } - - if (sock.fd == -1) { - char __s[256]; - perf_log_fatal("socket: %s", perf_strerror_r(errno, __s, sizeof(__s))); - } - -#if defined(AF_INET6) && defined(IPV6_V6ONLY) - if (family == AF_INET6) { - int on = 1; - - if (setsockopt(sock.fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) { - perf_log_warning("setsockopt(IPV6_V6ONLY) failed"); - } - } -#endif - tmp = *local; port = perf_sockaddr_port(&tmp); if (port != 0 && offset != 0) { @@ -267,398 +207,16 @@ struct perf_net_socket perf_net_opensocket(enum perf_net_mode mode, const perf_s perf_sockaddr_setport(&tmp, port); } - if (bind(sock.fd, &tmp.sa.sa, tmp.length) == -1) { - char __s[256]; - perf_log_fatal("bind: %s", perf_strerror_r(errno, __s, sizeof(__s))); - } - - if (bufsize > 0) { - bufsize *= 1024; - - ret = setsockopt(sock.fd, SOL_SOCKET, SO_RCVBUF, - &bufsize, sizeof(bufsize)); - if (ret < 0) - perf_log_warning("setsockbuf(SO_RCVBUF) failed"); - - ret = setsockopt(sock.fd, SOL_SOCKET, SO_SNDBUF, - &bufsize, sizeof(bufsize)); - if (ret < 0) - perf_log_warning("setsockbuf(SO_SNDBUF) failed"); - } - - flags = fcntl(sock.fd, F_GETFL, 0); - if (flags < 0) - perf_log_fatal("fcntl(F_GETFL)"); - ret = fcntl(sock.fd, F_SETFL, flags | O_NONBLOCK); - if (ret < 0) - perf_log_fatal("fcntl(F_SETFL)"); - - if (mode == sock_tcp || mode == sock_tls) { - if (connect(sock.fd, &server->sa.sa, server->length)) { - if (errno == EINPROGRESS) { - sock.is_ready = 0; - } else { - char __s[256]; - perf_log_fatal("connect() failed: %s", perf_strerror_r(errno, __s, sizeof(__s))); - } - } - sock.recvbuf = malloc(TCP_RECV_BUF_SIZE); - sock.at = 0; - sock.have_more = 0; - sock.sendbuf = malloc(TCP_SEND_BUF_SIZE); - if (!sock.recvbuf || !sock.sendbuf) { - perf_log_fatal("perf_net_opensocket() failed: unable to allocate buffers"); - } - } - - return sock; -} - -ssize_t perf_net_recv(struct perf_net_socket* sock, void* buf, size_t len, int flags) -{ - switch (sock->mode) { - case sock_tls: { - ssize_t n; - uint16_t dnslen, dnslen2; - - if (!sock->have_more) { - if (pthread_mutex_lock(&sock->lock)) { - perf_log_fatal("pthread_mutex_lock() failed"); - } - if (!sock->is_ready) { - if (pthread_mutex_unlock(&sock->lock)) { - perf_log_fatal("pthread_mutex_unlock() failed"); - } - errno = EAGAIN; - return -1; - } - - n = SSL_read(sock->ssl, sock->recvbuf + sock->at, TCP_RECV_BUF_SIZE - sock->at); - if (n < 0) { - int err = SSL_get_error(sock->ssl, n); - if (pthread_mutex_unlock(&sock->lock)) { - perf_log_fatal("pthread_mutex_unlock() failed"); - } - if (err == SSL_ERROR_WANT_READ) { - errno = EAGAIN; - } else { - errno = EBADF; - } - return -1; - } - if (pthread_mutex_unlock(&sock->lock)) { - perf_log_fatal("pthread_mutex_unlock() failed"); - } - - sock->at += n; - if (sock->at < 3) { - errno = EAGAIN; - return -1; - } - } - - memcpy(&dnslen, sock->recvbuf, 2); - dnslen = ntohs(dnslen); - if (sock->at < dnslen + 2) { - errno = EAGAIN; - return -1; - } - memcpy(buf, sock->recvbuf + 2, len < dnslen ? len : dnslen); - memmove(sock->recvbuf, sock->recvbuf + 2 + dnslen, sock->at - 2 - dnslen); - sock->at -= 2 + dnslen; - - if (sock->at > 2) { - memcpy(&dnslen2, sock->recvbuf, 2); - dnslen2 = ntohs(dnslen2); - if (sock->at >= dnslen2 + 2) { - sock->have_more = 1; - return dnslen; - } - } - - sock->have_more = 0; - return dnslen; - } - case sock_tcp: { - ssize_t n; - uint16_t dnslen, dnslen2; - - if (!sock->have_more) { - n = recv(sock->fd, sock->recvbuf + sock->at, TCP_RECV_BUF_SIZE - sock->at, flags); - if (n < 0) { - if (errno == ECONNRESET) { - // Treat connection reset like try again until reconnection features are in - errno = EAGAIN; - } - return n; - } - sock->at += n; - if (sock->at < 3) { - errno = EAGAIN; - return -1; - } - } - - memcpy(&dnslen, sock->recvbuf, 2); - dnslen = ntohs(dnslen); - if (sock->at < dnslen + 2) { - errno = EAGAIN; - return -1; - } - memcpy(buf, sock->recvbuf + 2, len < dnslen ? len : dnslen); - memmove(sock->recvbuf, sock->recvbuf + 2 + dnslen, sock->at - 2 - dnslen); - sock->at -= 2 + dnslen; - - if (sock->at > 2) { - memcpy(&dnslen2, sock->recvbuf, 2); - dnslen2 = ntohs(dnslen2); - if (sock->at >= dnslen2 + 2) { - sock->have_more = 1; - return dnslen; - } - } - - sock->have_more = 0; - return dnslen; - } - default: - break; - } - - return recv(sock->fd, buf, len, flags); -} - -ssize_t perf_net_sendto(struct perf_net_socket* sock, const void* buf, size_t len, int flags, - const struct sockaddr* dest_addr, socklen_t addrlen) -{ - switch (sock->mode) { - case sock_tls: { - size_t send = len < TCP_SEND_BUF_SIZE - 2 ? len : (TCP_SEND_BUF_SIZE - 2); - // TODO: We only send what we can send, because we can't continue sending - uint16_t dnslen = htons(send); - ssize_t n; - - memcpy(sock->sendbuf, &dnslen, 2); - memcpy(sock->sendbuf + 2, buf, send); - if (pthread_mutex_lock(&sock->lock)) { - perf_log_fatal("pthread_mutex_lock() failed"); - } - n = SSL_write(sock->ssl, sock->sendbuf, send + 2); - if (n < 0) { - perf_log_warning("SSL_write(): %s", ERR_error_string(SSL_get_error(sock->ssl, n), 0)); - errno = EBADF; - } - if (pthread_mutex_unlock(&sock->lock)) { - perf_log_fatal("pthread_mutex_unlock() failed"); - } - - if (n > 0 && n < send + 2) { - sock->sending = n; - sock->flags = flags; - memcpy(&sock->dest_addr, dest_addr, addrlen); - sock->addrlen = addrlen; - sock->is_ready = 0; - errno = EINPROGRESS; - return -1; - } - - return n > 0 ? n - 2 : n; - } - case sock_tcp: { - size_t send = len < TCP_SEND_BUF_SIZE - 2 ? len : (TCP_SEND_BUF_SIZE - 2); - // TODO: We only send what we can send, because we can't continue sending - uint16_t dnslen = htons(send); - ssize_t n; - - memcpy(sock->sendbuf, &dnslen, 2); - memcpy(sock->sendbuf + 2, buf, send); - n = sendto(sock->fd, sock->sendbuf, send + 2, flags, dest_addr, addrlen); - - if (n > 0 && n < send + 2) { - sock->sending = n; - sock->flags = flags; - memcpy(&sock->dest_addr, dest_addr, addrlen); - sock->addrlen = addrlen; - sock->is_ready = 0; - errno = EINPROGRESS; - return -1; - } - - return n > 0 ? n - 2 : n; - } - default: - break; - } - return sendto(sock->fd, buf, len, flags, dest_addr, addrlen); -} - -int perf_net_close(struct perf_net_socket* sock) -{ - return close(sock->fd); -} - -int perf_net_sockeq(struct perf_net_socket* sock_a, struct perf_net_socket* sock_b) -{ - return sock_a->fd == sock_b->fd; -} - -enum perf_net_mode perf_net_parsemode(const char* mode) -{ - if (!strcmp(mode, "udp")) { - return sock_udp; - } else if (!strcmp(mode, "tcp")) { - return sock_tcp; - } else if (!strcmp(mode, "tls") || !strcmp(mode, "dot")) { - return sock_tls; - } - - perf_log_warning("invalid socket mode"); - perf_opt_usage(); - exit(1); -} - -int perf_net_sockready(struct perf_net_socket* sock, int pipe_fd, int64_t timeout) -{ - if (sock->is_ready) { - return 1; - } - - switch (sock->mode) { - case sock_tls: { - int ret; - - if (sock->sending) { - uint16_t dnslen; - ssize_t n; - - memcpy(&dnslen, sock->sendbuf, 2); - dnslen = ntohs(dnslen); - if (pthread_mutex_lock(&sock->lock)) { - perf_log_fatal("pthread_mutex_lock() failed"); - } - n = SSL_write(sock->ssl, sock->sendbuf + sock->sending, dnslen + 2 - sock->sending); - if (n < 1) { - if (n < 0) { - perf_log_warning("SSL_write(): %s", ERR_error_string(SSL_get_error(sock->ssl, n), 0)); - errno = EBADF; - } - if (pthread_mutex_unlock(&sock->lock)) { - perf_log_fatal("pthread_mutex_unlock() failed"); - } - return -1; - } - if (pthread_mutex_unlock(&sock->lock)) { - perf_log_fatal("pthread_mutex_unlock() failed"); - } - sock->sending += n; - if (sock->sending < dnslen + 2) { - errno = EINPROGRESS; - return -1; - } - sock->sending = 0; - sock->is_ready = 1; - return 1; - } - - if (!sock->is_ssl_ready) { - switch (perf_os_waituntilanywritable(sock, 1, pipe_fd, timeout)) { - case PERF_R_TIMEDOUT: - return -1; - case PERF_R_SUCCESS: { - int error = 0; - socklen_t len = (socklen_t)sizeof(error); - - getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &len); - if (error != 0) { - if (error == EINPROGRESS -#if EWOULDBLOCK != EAGAIN - || error == EWOULDBLOCK -#endif - || error == EAGAIN) { - return 0; - } - return -1; - } - } - } - sock->is_ssl_ready = 1; - } - - if (pthread_mutex_lock(&sock->lock)) { - perf_log_fatal("pthread_mutex_lock() failed"); - } - ret = SSL_connect(sock->ssl); - if (!ret) { - perf_log_warning("SSL_connect(): %s", ERR_error_string(SSL_get_error(sock->ssl, ret), 0)); - if (pthread_mutex_unlock(&sock->lock)) { - perf_log_fatal("pthread_mutex_unlock() failed"); - } - return -1; - } - if (ret < 0) { - int err = SSL_get_error(sock->ssl, ret); - if (pthread_mutex_unlock(&sock->lock)) { - perf_log_fatal("pthread_mutex_unlock() failed"); - } - if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { - return 0; - } - perf_log_warning("SSL_connect(): %s", ERR_error_string(err, 0)); - return -1; - } - sock->is_ready = 1; - if (pthread_mutex_unlock(&sock->lock)) { - perf_log_fatal("pthread_mutex_unlock() failed"); - } - return 1; - } + switch (mode) { + case sock_udp: + return perf_net_udp_opensocket(server, &tmp, bufsize); case sock_tcp: - if (sock->sending) { - uint16_t dnslen; - ssize_t n; - - memcpy(&dnslen, sock->sendbuf, 2); - dnslen = ntohs(dnslen); - n = sendto(sock->fd, sock->sendbuf + sock->sending, dnslen + 2 - sock->sending, sock->flags, (struct sockaddr*)&sock->dest_addr, sock->addrlen); - if (n < 1) { - return -1; - } - sock->sending += n; - if (sock->sending < dnslen + 2) { - errno = EINPROGRESS; - return -1; - } - sock->sending = 0; - sock->is_ready = 1; - return 1; - } - - switch (perf_os_waituntilanywritable(sock, 1, pipe_fd, timeout)) { - case PERF_R_TIMEDOUT: - return -1; - case PERF_R_SUCCESS: { - int error = 0; - socklen_t len = (socklen_t)sizeof(error); - - getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &len); - if (error != 0) { - if (error == EINPROGRESS -#if EWOULDBLOCK != EAGAIN - || error == EWOULDBLOCK -#endif - || error == EAGAIN) { - return 0; - } - return -1; - } - sock->is_ready = 1; - return 1; - } - } - break; + return perf_net_tcp_opensocket(server, &tmp, bufsize); + case sock_dot: + return perf_net_dot_opensocket(server, &tmp, bufsize); default: - break; + perf_log_fatal("perf_net_opensocket(): invalid mode"); } - return -1; + return 0; } |