Merge tag 'upstream/0.3.7'
[debian/uanytun.git] / src / udp.c
index 7f539a8..239e074 100644 (file)
--- a/src/udp.c
+++ b/src/udp.c
  *  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 <equinox@anytun.org>
+ *
+ *  Copyright (C) 2007-2014 Christian Pointner <equinox@anytun.org>
  *
  *  This file is part of uAnytun.
  *
  *
  *  You should have received a copy of the GNU General Public License
  *  along with uAnytun. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  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 <stdio.h>
+
 #include "datatypes.h"
 
 #include "udp.h"
 #include <arpa/inet.h>
 #include <netinet/in.h>
 
-int udp_init(udp_socket_t* sock, const char* local_addr, const char* port, resolv_addr_type_t resolv_type)
-{
-  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;
+#ifndef AI_ADDRCONFIG 
+#define AI_ADDRCONFIG 0 
+#endif
 
+static int udp_resolv_local(udp_t* sock, const char* local_addr, const char* port, resolv_addr_type_t resolv_type, unsigned int* idx)
+{
   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);
@@ -78,52 +90,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;
@@ -143,94 +232,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("<null>");
+
+  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("<unknown address type>");
+  }
 
-  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_);
 }
-