diff options
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/fastd.c | 169 | ||||
-rw-r--r-- | src/fastd.h | 18 | ||||
-rw-r--r-- | src/receive.c | 185 | ||||
-rw-r--r-- | src/socket.c | 23 |
5 files changed, 216 insertions, 180 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 92ad954..d527006 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,6 +25,7 @@ add_executable(fastd printf.c queue.c random.c + receive.c resolve.c send.c shell.c diff --git a/src/fastd.c b/src/fastd.c index 9bd534d..37e841f 100644 --- a/src/fastd.c +++ b/src/fastd.c @@ -561,160 +561,6 @@ static void handle_tun(fastd_context_t *ctx) { fastd_send_all(ctx, NULL, buffer); } -static inline void handle_socket_control(fastd_context_t *ctx, struct msghdr *message, const fastd_socket_t *sock, fastd_peer_address_t *local_addr) { - memset(local_addr, 0, sizeof(fastd_peer_address_t)); - - const char *end = (char*)message->msg_control + message->msg_controllen; - - struct cmsghdr *cmsg; - for (cmsg = CMSG_FIRSTHDR(message); cmsg; cmsg = CMSG_NXTHDR(message, cmsg)) { - if ((char*)cmsg + sizeof(*cmsg) > end) - return; - - if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { - struct in_pktinfo *pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg); - if ((char*)pktinfo + sizeof(*pktinfo) > end) - return; - - local_addr->in.sin_family = AF_INET; - local_addr->in.sin_addr = pktinfo->ipi_addr; - local_addr->in.sin_port = fastd_peer_address_get_port(sock->bound_addr); - - return; - } - - if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { - struct in6_pktinfo *pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg); - if ((char*)pktinfo + sizeof(*pktinfo) > end) - return; - - local_addr->in6.sin6_family = AF_INET6; - local_addr->in6.sin6_addr = pktinfo->ipi6_addr; - local_addr->in6.sin6_port = fastd_peer_address_get_port(sock->bound_addr); - - if (IN6_IS_ADDR_LINKLOCAL(&local_addr->in6.sin6_addr)) - local_addr->in6.sin6_scope_id = pktinfo->ipi6_ifindex; - - return; - } - } -} - -static inline void handle_socket_receive_known(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_peer_t *peer, fastd_buffer_t buffer) { - if (!fastd_peer_may_connect(ctx, peer)) { - fastd_buffer_free(buffer); - return; - } - - const uint8_t *packet_type = buffer.data; - fastd_buffer_push_head(ctx, &buffer, 1); - - switch (*packet_type) { - case PACKET_DATA: - if (!fastd_peer_is_established(peer) || !fastd_peer_address_equal(&peer->local_address, local_addr)) { - fastd_buffer_free(buffer); - ctx->conf->protocol->handshake_init(ctx, sock, local_addr, remote_addr, NULL); - return; - } - - ctx->conf->protocol->handle_recv(ctx, peer, buffer); - break; - - case PACKET_HANDSHAKE: - fastd_handshake_handle(ctx, sock, local_addr, remote_addr, peer, buffer); - } -} - -static inline bool is_unknown_peer_valid(fastd_context_t *ctx, const fastd_peer_address_t *remote_addr) { - return ctx->conf->has_floating || ctx->conf->on_verify; -} - -static inline void handle_socket_receive_unknown(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_buffer_t buffer) { - const uint8_t *packet_type = buffer.data; - fastd_buffer_push_head(ctx, &buffer, 1); - - switch (*packet_type) { - case PACKET_DATA: - fastd_buffer_free(buffer); - ctx->conf->protocol->handshake_init(ctx, sock, local_addr, remote_addr, NULL); - break; - - case PACKET_HANDSHAKE: - fastd_handshake_handle(ctx, sock, local_addr, remote_addr, NULL, buffer); - } -} - -static inline void handle_socket_receive(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_buffer_t buffer) { - fastd_peer_t *peer = NULL; - - if (sock->peer) { - if (!fastd_peer_address_equal(&sock->peer->address, remote_addr)) { - fastd_buffer_free(buffer); - return; - } - - peer = sock->peer; - } - else { - for (peer = ctx->peers; peer; peer = peer->next) { - if (fastd_peer_address_equal(&peer->address, remote_addr)) - break; - } - } - - if (peer) { - handle_socket_receive_known(ctx, sock, local_addr, remote_addr, peer, buffer); - } - else if(is_unknown_peer_valid(ctx, remote_addr)) { - handle_socket_receive_unknown(ctx, sock, local_addr, remote_addr, buffer); - } - else { - pr_debug(ctx, "received packet from unknown peer %I", remote_addr); - fastd_buffer_free(buffer); - } -} - -static void handle_socket(fastd_context_t *ctx, fastd_socket_t *sock) { - size_t max_len = PACKET_TYPE_LEN + ctx->conf->max_packet_size; - fastd_buffer_t buffer = fastd_buffer_alloc(ctx, max_len, ctx->conf->min_decrypt_head_space, ctx->conf->min_decrypt_tail_space); - fastd_peer_address_t local_addr; - fastd_peer_address_t recvaddr; - struct iovec buffer_vec = { .iov_base = buffer.data, .iov_len = buffer.len }; - char cbuf[1024]; - - struct msghdr message = { - .msg_name = &recvaddr, - .msg_namelen = sizeof(recvaddr), - .msg_iov = &buffer_vec, - .msg_iovlen = 1, - .msg_control = cbuf, - .msg_controllen = sizeof(cbuf), - }; - - ssize_t len = recvmsg(sock->fd, &message, 0); - if (len <= 0) { - if (len < 0 && errno != EINTR) - pr_warn_errno(ctx, "recvmsg"); - - fastd_buffer_free(buffer); - return; - } - - buffer.len = len; - - handle_socket_control(ctx, &message, sock, &local_addr); - - if (!local_addr.sa.sa_family) { - pr_error(ctx, "received packet without packet info"); - fastd_buffer_free(buffer); - return; - } - - fastd_peer_address_simplify(&recvaddr); - - handle_socket_receive(ctx, sock, &local_addr, &recvaddr, buffer); -} - static void handle_resolve_returns(fastd_context_t *ctx) { fastd_resolve_return_t resolve_return; @@ -745,15 +591,6 @@ static void handle_resolve_returns(fastd_context_t *ctx) { fastd_remote_unref(resolve_return.remote); } -static inline void handle_socket_error(fastd_context_t *ctx, fastd_socket_t *sock) { - if (sock->addr->bindtodev) - pr_warn(ctx, "socket bind %I on `%s' lost", &sock->addr->addr, sock->addr->bindtodev); - else - pr_warn(ctx, "socket bind %I lost", &sock->addr->addr); - - fastd_socket_close(ctx, sock); -} - static void handle_input(fastd_context_t *ctx) { const size_t n_fds = 2 + ctx->n_socks + ctx->n_peers; struct pollfd fds[n_fds]; @@ -805,16 +642,16 @@ static void handle_input(fastd_context_t *ctx) { for (i = 2; i < ctx->n_socks+2; i++) { if (fds[i].revents & (POLLERR|POLLHUP|POLLNVAL)) - handle_socket_error(ctx, &ctx->socks[i-2]); + fastd_socket_error(ctx, &ctx->socks[i-2]); else if (fds[i].revents & POLLIN) - handle_socket(ctx, &ctx->socks[i-2]); + fastd_receive(ctx, &ctx->socks[i-2]); } for (peer = ctx->peers; peer; peer = peer->next) { if (fds[i].revents & (POLLERR|POLLHUP|POLLNVAL)) fastd_peer_reset_socket(ctx, peer); else if (fds[i].revents & POLLIN) - handle_socket(ctx, peer->sock); + fastd_receive(ctx, peer->sock); i++; } diff --git a/src/fastd.h b/src/fastd.h index 1bbda28..ba74330 100644 --- a/src/fastd.h +++ b/src/fastd.h @@ -311,10 +311,14 @@ struct fastd_string_stack { void fastd_send(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_buffer_t buffer); void fastd_send_all(fastd_context_t *ctx, fastd_peer_t *source_peer, fastd_buffer_t buffer); void fastd_send_handshake(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_buffer_t buffer); +void fastd_receive(fastd_context_t *ctx, fastd_socket_t *sock); + void fastd_handle_receive(fastd_context_t *ctx, fastd_peer_t *peer, fastd_buffer_t buffer); bool fastd_socket_handle_binds(fastd_context_t *ctx); fastd_socket_t* fastd_socket_open(fastd_context_t *ctx, fastd_peer_t *peer, int af); +void fastd_socket_close(fastd_context_t *ctx, fastd_socket_t *sock); +void fastd_socket_error(fastd_context_t *ctx, fastd_socket_t *sock); void fastd_setfd(const fastd_context_t *ctx, int fd, int set, int unset); void fastd_setfl(const fastd_context_t *ctx, int fd, int set, int unset); @@ -460,20 +464,6 @@ static inline void fastd_string_stack_free(fastd_string_stack_t *str) { } } -static inline void fastd_socket_close(fastd_context_t *ctx, fastd_socket_t *sock) { - if (sock->fd >= 0) { - if(close(sock->fd)) - pr_error_errno(ctx, "closing socket: close"); - - sock->fd = -2; - } - - if (sock->bound_addr) { - free(sock->bound_addr); - sock->bound_addr = NULL; - } -} - static inline bool timespec_after(const struct timespec *tp1, const struct timespec *tp2) { return (tp1->tv_sec > tp2->tv_sec || (tp1->tv_sec == tp2->tv_sec && tp1->tv_nsec > tp2->tv_nsec)); diff --git a/src/receive.c b/src/receive.c new file mode 100644 index 0000000..fc835eb --- /dev/null +++ b/src/receive.c @@ -0,0 +1,185 @@ +/* + Copyright (c) 2012-2013, Matthias Schiffer <mschiffer@universe-factory.net> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "fastd.h" +#include "handshake.h" +#include "packet.h" +#include "peer.h" + + +static inline void handle_socket_control(fastd_context_t *ctx, struct msghdr *message, const fastd_socket_t *sock, fastd_peer_address_t *local_addr) { + memset(local_addr, 0, sizeof(fastd_peer_address_t)); + + const char *end = (char*)message->msg_control + message->msg_controllen; + + struct cmsghdr *cmsg; + for (cmsg = CMSG_FIRSTHDR(message); cmsg; cmsg = CMSG_NXTHDR(message, cmsg)) { + if ((char*)cmsg + sizeof(*cmsg) > end) + return; + + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg); + if ((char*)pktinfo + sizeof(*pktinfo) > end) + return; + + local_addr->in.sin_family = AF_INET; + local_addr->in.sin_addr = pktinfo->ipi_addr; + local_addr->in.sin_port = fastd_peer_address_get_port(sock->bound_addr); + + return; + } + + if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg); + if ((char*)pktinfo + sizeof(*pktinfo) > end) + return; + + local_addr->in6.sin6_family = AF_INET6; + local_addr->in6.sin6_addr = pktinfo->ipi6_addr; + local_addr->in6.sin6_port = fastd_peer_address_get_port(sock->bound_addr); + + if (IN6_IS_ADDR_LINKLOCAL(&local_addr->in6.sin6_addr)) + local_addr->in6.sin6_scope_id = pktinfo->ipi6_ifindex; + + return; + } + } +} + +static inline void handle_socket_receive_known(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_peer_t *peer, fastd_buffer_t buffer) { + if (!fastd_peer_may_connect(ctx, peer)) { + fastd_buffer_free(buffer); + return; + } + + const uint8_t *packet_type = buffer.data; + fastd_buffer_push_head(ctx, &buffer, 1); + + switch (*packet_type) { + case PACKET_DATA: + if (!fastd_peer_is_established(peer) || !fastd_peer_address_equal(&peer->local_address, local_addr)) { + fastd_buffer_free(buffer); + ctx->conf->protocol->handshake_init(ctx, sock, local_addr, remote_addr, NULL); + return; + } + + ctx->conf->protocol->handle_recv(ctx, peer, buffer); + break; + + case PACKET_HANDSHAKE: + fastd_handshake_handle(ctx, sock, local_addr, remote_addr, peer, buffer); + } +} + +static inline bool is_unknown_peer_valid(fastd_context_t *ctx, const fastd_peer_address_t *remote_addr) { + return ctx->conf->has_floating || ctx->conf->on_verify; +} + +static inline void handle_socket_receive_unknown(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_buffer_t buffer) { + const uint8_t *packet_type = buffer.data; + fastd_buffer_push_head(ctx, &buffer, 1); + + switch (*packet_type) { + case PACKET_DATA: + fastd_buffer_free(buffer); + ctx->conf->protocol->handshake_init(ctx, sock, local_addr, remote_addr, NULL); + break; + + case PACKET_HANDSHAKE: + fastd_handshake_handle(ctx, sock, local_addr, remote_addr, NULL, buffer); + } +} + +static inline void handle_socket_receive(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_buffer_t buffer) { + fastd_peer_t *peer = NULL; + + if (sock->peer) { + if (!fastd_peer_address_equal(&sock->peer->address, remote_addr)) { + fastd_buffer_free(buffer); + return; + } + + peer = sock->peer; + } + else { + for (peer = ctx->peers; peer; peer = peer->next) { + if (fastd_peer_address_equal(&peer->address, remote_addr)) + break; + } + } + + if (peer) { + handle_socket_receive_known(ctx, sock, local_addr, remote_addr, peer, buffer); + } + else if(is_unknown_peer_valid(ctx, remote_addr)) { + handle_socket_receive_unknown(ctx, sock, local_addr, remote_addr, buffer); + } + else { + pr_debug(ctx, "received packet from unknown peer %I", remote_addr); + fastd_buffer_free(buffer); + } +} + +void fastd_receive(fastd_context_t *ctx, fastd_socket_t *sock) { + size_t max_len = PACKET_TYPE_LEN + ctx->conf->max_packet_size; + fastd_buffer_t buffer = fastd_buffer_alloc(ctx, max_len, ctx->conf->min_decrypt_head_space, ctx->conf->min_decrypt_tail_space); + fastd_peer_address_t local_addr; + fastd_peer_address_t recvaddr; + struct iovec buffer_vec = { .iov_base = buffer.data, .iov_len = buffer.len }; + char cbuf[1024]; + + struct msghdr message = { + .msg_name = &recvaddr, + .msg_namelen = sizeof(recvaddr), + .msg_iov = &buffer_vec, + .msg_iovlen = 1, + .msg_control = cbuf, + .msg_controllen = sizeof(cbuf), + }; + + ssize_t len = recvmsg(sock->fd, &message, 0); + if (len <= 0) { + if (len < 0 && errno != EINTR) + pr_warn_errno(ctx, "recvmsg"); + + fastd_buffer_free(buffer); + return; + } + + buffer.len = len; + + handle_socket_control(ctx, &message, sock, &local_addr); + + if (!local_addr.sa.sa_family) { + pr_error(ctx, "received packet without packet info"); + fastd_buffer_free(buffer); + return; + } + + fastd_peer_address_simplify(&recvaddr); + + handle_socket_receive(ctx, sock, &local_addr, &recvaddr, buffer); +} diff --git a/src/socket.c b/src/socket.c index ca6f588..c53695b 100644 --- a/src/socket.c +++ b/src/socket.c @@ -199,3 +199,26 @@ fastd_socket_t* fastd_socket_open(fastd_context_t *ctx, fastd_peer_t *peer, int return sock; } + +void fastd_socket_close(fastd_context_t *ctx, fastd_socket_t *sock) { + if (sock->fd >= 0) { + if(close(sock->fd)) + pr_error_errno(ctx, "closing socket: close"); + + sock->fd = -2; + } + + if (sock->bound_addr) { + free(sock->bound_addr); + sock->bound_addr = NULL; + } +} + +void fastd_socket_error(fastd_context_t *ctx, fastd_socket_t *sock) { + if (sock->addr->bindtodev) + pr_warn(ctx, "socket bind %I on `%s' lost", &sock->addr->addr, sock->addr->bindtodev); + else + pr_warn(ctx, "socket bind %I lost", &sock->addr->addr); + + fastd_socket_close(ctx, sock); +} |