X-Git-Url: https://git.syn-net.org/?p=debian%2Fuanytun.git;a=blobdiff_plain;f=src%2Fudp.c;h=9217688105718aef3d463b3c1b82649ceda8954c;hp=96e2e23add27deea539e8b147fcd3caf7fe73347;hb=71f6f666a3d69c6e1e7a77e238362c5bbe288e66;hpb=b81b2c1954fe3e3317e6fa260c5ffdc9eeee966c diff --git a/src/udp.c b/src/udp.c index 96e2e23..9217688 100644 --- a/src/udp.c +++ b/src/udp.c @@ -10,18 +10,19 @@ * tunnel endpoints. It has less protocol overhead than IPSec in Tunnel * mode and allows tunneling of every ETHER TYPE protocol (e.g. * ethernet, ip, arp ...). satp directly includes cryptography and - * message authentication based on the methodes used by SRTP. It is + * message authentication based on the methods used by SRTP. It is * intended to deliver a generic, scaleable and secure solution for * tunneling and relaying of packets of any protocol. - * * - * Copyright (C) 2007-2008 Christian Pointner + * + * Copyright (C) 2007-2014 Christian Pointner * * This file is part of uAnytun. * * uAnytun is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. * * uAnytun is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -30,8 +31,24 @@ * * You should have received a copy of the GNU General Public License * along with uAnytun. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. */ +#define _GNU_SOURCE +#include + #include "datatypes.h" #include "udp.h" @@ -48,27 +65,19 @@ #include #include -int udp_init(udp_socket_t* sock, const char* local_addr, const char* port, resolv_addr_type_t resolv_type) +static int udp_resolv_local(udp_t* sock, const char* local_addr, const char* port, resolv_addr_type_t resolv_type, unsigned int* idx) { - if(!sock || !port) - return -1; - - sock->fd_ = 0; - memset(&(sock->local_end_), 0, sizeof(sock->local_end_)); - memset(&(sock->remote_end_), 0, sizeof(sock->local_end_)); - sock->remote_end_set_ = 0; - struct addrinfo hints, *res; res = NULL; memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_DGRAM; - hints.ai_flags |= AI_PASSIVE; + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; switch(resolv_type) { - case IPV4_ONLY: hints.ai_family = PF_INET; break; - case IPV6_ONLY: hints.ai_family = PF_INET6; break; - default: hints.ai_family = PF_UNSPEC; break; + case IPV4_ONLY: hints.ai_family = AF_INET; break; + case IPV6_ONLY: hints.ai_family = AF_INET6; break; + default: hints.ai_family = AF_UNSPEC; break; } int errcode = getaddrinfo(local_addr, port, &hints, &res); @@ -77,52 +86,129 @@ int udp_init(udp_socket_t* sock, const char* local_addr, const char* port, resol udp_close(sock); return -1; } - if(!res) { udp_close(sock); log_printf(ERROR, "getaddrinfo returned no address for %s:%s", local_addr, port); return -1; } - memcpy(&(sock->local_end_), res->ai_addr, res->ai_addrlen); + struct addrinfo* r = res; + udp_socket_t* prev_sock = sock->socks_; + while(prev_sock && prev_sock->next_) prev_sock = prev_sock->next_; + while(r) { + udp_socket_t* new_sock = malloc(sizeof(udp_socket_t)); + if(!new_sock) { + log_printf(ERROR, "memory error at udp_init"); + freeaddrinfo(res); + udp_close(sock); + return -2; + } + memset(&(new_sock->local_end_.addr_), 0, sizeof(new_sock->local_end_.addr_)); + new_sock->local_end_.len_ = sizeof(new_sock->local_end_.addr_); + memset(&(new_sock->remote_end_.addr_), 0, sizeof(new_sock->remote_end_.addr_)); + new_sock->remote_end_.len_ = sizeof(new_sock->remote_end_.addr_); + new_sock->remote_end_set_ = 0; + new_sock->next_ = NULL; + new_sock->idx_ = (*idx)++; + + if(!sock->socks_) { + sock->socks_ = new_sock; + prev_sock = new_sock; + } + else { + prev_sock->next_ = new_sock; + prev_sock = new_sock; + } + + memcpy(&(new_sock->local_end_.addr_), r->ai_addr, r->ai_addrlen); + new_sock->local_end_.len_ = r->ai_addrlen; + new_sock->fd_ = socket(new_sock->local_end_.addr_.ss_family, SOCK_DGRAM, 0); + if(new_sock->fd_ < 0) { + log_printf(ERROR, "Error on opening udp socket: %s", strerror(errno)); + freeaddrinfo(res); + udp_close(sock); + return -1; + } + + if(r->ai_family == AF_INET6) { + int on = 1; + if(setsockopt(new_sock->fd_, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) + log_printf(ERROR, "Error on setting IPV6_V6ONLY socket option: %s", strerror(errno)); + } + + errcode = bind(new_sock->fd_, (struct sockaddr*)&(new_sock->local_end_.addr_), new_sock->local_end_.len_); + if(errcode) { + log_printf(ERROR, "Error on binding udp socket: %s", strerror(errno)); + freeaddrinfo(res); + udp_close(sock); + return -1; + } + + char* local_string = udp_endpoint_to_string(&(new_sock->local_end_)); + if(local_string) { + log_printf(NOTICE, "socket[%d] listening on: %s", new_sock->idx_, local_string); + free(local_string); + } + + r = r->ai_next; + } - sock->fd_ = socket(res->ai_family, SOCK_DGRAM, 0); - if(sock->fd_ < 0) { - log_printf(ERROR, "Error on opening udp socket: %s", strerror(errno)); - freeaddrinfo(res); - udp_close(sock); + freeaddrinfo(res); + return 0; +} + +int udp_init(udp_t* sock, const char* local_addr, const char* port, resolv_addr_type_t resolv_type) +{ + if(!sock || !port) return -1; + + sock->socks_ = NULL; + sock->active_sock_ = NULL; + + unsigned int idx = 0; + int ret = udp_resolv_local(sock, local_addr, port, resolv_type, &idx); + if(ret) + return ret; + + return 0; +} + +int udp_fill_fd_set(udp_t* sock, fd_set* set) +{ + int max_fd = 0; + + udp_socket_t* s = sock->socks_; + while(s) { + FD_SET(s->fd_, set); + max_fd = s->fd_ > max_fd ? s->fd_ : max_fd; + s = s->next_; } - errcode = bind(sock->fd_, res->ai_addr, res->ai_addrlen); - if(errcode) { - log_printf(ERROR, "Error on binding udp socket: %s", strerror(errno)); - freeaddrinfo(res); - udp_close(sock); - return -1; + return max_fd; +} + +int udp_has_remote(udp_t* sock) +{ + if(!sock->active_sock_ || !sock->active_sock_->remote_end_set_) + return 0; + + udp_socket_t* s = sock->socks_; + while(s) { + if(s->remote_end_set_) + return 1; + s = s->next_; } - -/* this doesn't work on linux ?? */ -/* #ifdef NO_V4MAPPED */ -/* if(res->ai_family == AF_INET6) { */ -/* log_printf(NOTICE, "disabling V4-Mapped addresses"); */ -/* int on = 1; */ -/* if(setsockopt(sock->fd_, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) */ -/* log_printf(ERROR, "Error on setting IPV6_V6ONLY socket option: %s", strerror(errno)); */ -/* } */ -/* #endif */ - freeaddrinfo(res); return 0; } -int udp_set_remote(udp_socket_t* sock, const char* remote_addr, const char* port, resolv_addr_type_t resolv_type) +int udp_resolv_remote(udp_t* sock, const char* remote_addr, const char* port, resolv_addr_type_t resolv_type) { - if(!sock || !remote_addr || !port) - return -1; - struct addrinfo hints, *res; + if(!sock || !remote_addr || !port) + return -1; + res = NULL; memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_DGRAM; @@ -142,94 +228,129 @@ int udp_set_remote(udp_socket_t* sock, const char* remote_addr, const char* port log_printf(ERROR, "getaddrinfo returned no address for %s:%s", remote_addr, port); return -1; } - memcpy(&(sock->remote_end_), res->ai_addr, res->ai_addrlen); - sock->remote_end_set_ = 1; + + int found = 0; + struct addrinfo* r = res; + while(r) { + udp_socket_t* s = sock->socks_; + while(s) { + if(s->local_end_.addr_.ss_family == r->ai_family && !(s->remote_end_set_)) { + sock->active_sock_ = s; + break; + } + s = s->next_; + } + + if(s) { + memcpy(&(s->remote_end_.addr_), r->ai_addr, r->ai_addrlen); + s->remote_end_.len_ = r->ai_addrlen; + s->remote_end_set_ = 1; + found = 1; + char* remote_string = udp_endpoint_to_string(&(s->remote_end_)); + if(remote_string) { + log_printf(NOTICE, "socket[%d] set remote end to: %s", s->idx_, remote_string); + free(remote_string); + } + break; + } + + r = r->ai_next; + } freeaddrinfo(res); + if(!found) + log_printf(WARNING, "no remote address for '%s' found that fits any of the local address families", remote_addr); + return 0; } -void udp_close(udp_socket_t* sock) +void udp_update_remote(udp_t* sock, int fd, udp_endpoint_t* remote) { if(!sock) return; - if(sock->fd_ > 0) - close(sock->fd_); -} + if(!(sock->active_sock_) || sock->active_sock_->fd_ != fd) { + udp_socket_t* s = sock->socks_; + while(s) { + if(s->fd_ == fd) { + sock->active_sock_ = s; + break; + } + s = s->next_; + } + } -char* udp_endpoint_to_string(udp_endpoint_t e) -{ - void* ptr; - u_int16_t port; - size_t addrstr_len = 0; - char* addrstr, *ret; - char addrport_sep = ':'; + if(!remote) + return; - switch (((struct sockaddr *)&e)->sa_family) - { - case AF_INET: - ptr = &((struct sockaddr_in *)&e)->sin_addr; - port = ntohs(((struct sockaddr_in *)&e)->sin_port); - addrstr_len = INET_ADDRSTRLEN + 1; - addrport_sep = ':'; - break; - case AF_INET6: - ptr = &((struct sockaddr_in6 *)&e)->sin6_addr; - port = ntohs(((struct sockaddr_in6 *)&e)->sin6_port); - addrstr_len = INET6_ADDRSTRLEN + 1; - addrport_sep = '.'; - break; - default: - asprintf(&ret, "unknown address type"); - return ; + if(sock->active_sock_) { + if(remote->len_ != sock->active_sock_->remote_end_.len_ || + memcmp(&(remote->addr_), &(sock->active_sock_->remote_end_.addr_), remote->len_)) { + memcpy(&(sock->active_sock_->remote_end_.addr_), &(remote->addr_), remote->len_); + sock->active_sock_->remote_end_.len_ = remote->len_; + sock->active_sock_->remote_end_set_ = 1; + char* addrstring = udp_endpoint_to_string(remote); + log_printf(NOTICE, "socket[%d] autodetected remote host changed %s", sock->active_sock_->idx_, addrstring); + free(addrstring); + } } - addrstr = malloc(addrstr_len); - if(!addrstr) - return NULL; - inet_ntop (((struct sockaddr *)&e)->sa_family, ptr, addrstr, addrstr_len); - asprintf(&ret, "%s%c%d", addrstr, addrport_sep ,port); - free(addrstr); - return ret; } -char* udp_get_local_end_string(udp_socket_t* sock) +void udp_close(udp_t* sock) { if(!sock) - return NULL; + return; - return udp_endpoint_to_string(sock->local_end_); + while(sock->socks_) { + if(sock->socks_->fd_ > 0) + close(sock->socks_->fd_); + + udp_socket_t*s = sock->socks_; + sock->socks_ = sock->socks_->next_; + + free(s); + } + sock->socks_ = NULL; + sock->active_sock_ = NULL; } -char* udp_get_remote_end_string(udp_socket_t* sock) +char* udp_endpoint_to_string(udp_endpoint_t* e) { - if(!sock || !sock->remote_end_set_) - return NULL; + if(!e) + return strdup(""); + + char addrstr[INET6_ADDRSTRLEN + 1], portstr[6], *ret; + char addrport_sep = ':'; + + switch(e->addr_.ss_family) + { + case AF_INET: addrport_sep = ':'; break; + case AF_INET6: addrport_sep = '.'; break; + case AF_UNSPEC: return NULL; + default: return strdup(""); + } - return udp_endpoint_to_string(sock->remote_end_); + int errcode = getnameinfo((struct sockaddr *)&(e->addr_), e->len_, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + if (errcode != 0) return NULL; + int len = asprintf(&ret, "%s%c%s", addrstr, addrport_sep ,portstr); + if(len == -1) return NULL; + return ret; } - -int udp_read(udp_socket_t* sock, u_int8_t* buf, u_int32_t len, udp_endpoint_t* remote_end) + + +int udp_read(udp_t* sock, int fd, u_int8_t* buf, u_int32_t len, udp_endpoint_t* remote_end) { - if(!sock || !remote_end) + if(!sock || !buf || !remote_end) return -1; - socklen_t socklen = sizeof(*remote_end); - return recvfrom(sock->fd_, buf, len, 0, (struct sockaddr *)remote_end, &socklen); + return recvfrom(fd, buf, len, 0, (struct sockaddr *)&(remote_end->addr_), &(remote_end->len_)); } -int udp_write(udp_socket_t* sock, u_int8_t* buf, u_int32_t len) + +int udp_write(udp_t* sock, u_int8_t* buf, u_int32_t len) { - if(!sock || !sock->remote_end_set_) - return -1; + if(!sock || !buf || !sock->active_sock_ || !sock->active_sock_->remote_end_set_) + return 0; - socklen_t socklen = sizeof(sock->remote_end_); -#ifdef NO_V4MAPPED - if((((struct sockaddr *)&sock->local_end_)->sa_family) == AF_INET) - socklen = sizeof(struct sockaddr_in); - else if ((((struct sockaddr *)&sock->local_end_)->sa_family) == AF_INET6) - socklen = sizeof(struct sockaddr_in6); -#endif - return sendto(sock->fd_, buf, len, 0, (struct sockaddr *)&(sock->remote_end_), socklen);; + return sendto(sock->active_sock_->fd_, buf, len, 0, (struct sockaddr *)&(sock->active_sock_->remote_end_.addr_), sock->active_sock_->remote_end_.len_); } -