Imported Upstream version 0.3.5
[debian/uanytun.git] / src / udp.c
index 3e1def6..9217688 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-2010 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_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->socks_ = NULL;
-  sock->active_sock_ = NULL;
-  memset(&(sock->remote_end_), 0, sizeof(sock->remote_end_));
-  sock->remote_end_set_ = 0;
-
   struct addrinfo hints, *res;
 
   res = NULL;
@@ -85,7 +93,8 @@ int udp_init(udp_t* sock, const char* local_addr, const char* port, resolv_addr_
   }
 
   struct addrinfo* r = res;
-  udp_socket_t* prev_sock = NULL;
+  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) {
@@ -94,8 +103,13 @@ int udp_init(udp_t* sock, const char* local_addr, const char* port, resolv_addr_
       udp_close(sock);
       return -2;
     }
-    memset(&(new_sock->local_end_), 0, sizeof(new_sock->local_end_));
+    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;
@@ -105,9 +119,10 @@ int udp_init(udp_t* sock, const char* local_addr, const char* port, resolv_addr_
       prev_sock->next_ = new_sock;
       prev_sock = new_sock;
     }
-    
-    memcpy(&(new_sock->local_end_), r->ai_addr, r->ai_addrlen);
-    new_sock->fd_ = socket(r->ai_family, SOCK_DGRAM, 0);
+
+    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);
@@ -121,17 +136,17 @@ int udp_init(udp_t* sock, const char* local_addr, const char* port, resolv_addr_
         log_printf(ERROR, "Error on setting IPV6_V6ONLY socket option: %s", strerror(errno));
     }
 
-    errcode = bind(new_sock->fd_, r->ai_addr, r->ai_addrlen);
+    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_);
+
+    char* local_string = udp_endpoint_to_string(&(new_sock->local_end_));
     if(local_string) {
-      log_printf(NOTICE, "listening on: %s", local_string);
+      log_printf(NOTICE, "socket[%d] listening on: %s", new_sock->idx_, local_string);
       free(local_string);
     }
 
@@ -139,11 +154,26 @@ int udp_init(udp_t* sock, const char* local_addr, const char* port, resolv_addr_
   }
 
   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_init_fd_set(udp_t* sock, fd_set* set)
+int udp_fill_fd_set(udp_t* sock, fd_set* set)
 {
   int max_fd = 0;
 
@@ -157,13 +187,28 @@ int udp_init_fd_set(udp_t* sock, fd_set* set)
   return max_fd;
 }
 
-int udp_set_remote(udp_t* sock, const char* remote_addr, const char* port, resolv_addr_type_t resolv_type)
+int udp_has_remote(udp_t* sock)
 {
-  if(!sock || !remote_addr || !port) 
-    return -1;
+  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_;
+  }
+
+  return 0;
+}
+
+int udp_resolv_remote(udp_t* sock, const char* remote_addr, const char* port, resolv_addr_type_t resolv_type)
+{
   struct addrinfo hints, *res;
 
+  if(!sock || !remote_addr || !port)
+    return -1;
+
   res = NULL;
   memset (&hints, 0, sizeof (hints));
   hints.ai_socktype = SOCK_DGRAM;
@@ -183,37 +228,71 @@ int udp_set_remote(udp_t* sock, const char* remote_addr, const char* port, resol
     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;
 
-  if(!sock->active_sock_) {
+  int found = 0;
+  struct addrinfo* r = res;
+  while(r) {
     udp_socket_t* s = sock->socks_;
     while(s) {
-      if((((struct sockaddr *)&s->local_end_)->sa_family) == res->ai_family) {
+      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_set_active_sock(udp_t* sock, int fd)
+void udp_update_remote(udp_t* sock, int fd, udp_endpoint_t* remote)
 {
-  if(!sock || (sock->active_sock_ && sock->active_sock_->fd_ == fd))
+  if(!sock)
     return;
 
-  udp_socket_t* s = sock->socks_;
-  while(s) {
-    if(s->fd_ == fd) {
-      sock->active_sock_ = s;
-      return;
+  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_;
+    }
+  }
+
+  if(!remote)
+    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);
     }
-    s = s->next_;
   }
 }
 
@@ -225,78 +304,53 @@ void udp_close(udp_t* sock)
   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_endpoint_to_string(udp_endpoint_t e)
+char* udp_endpoint_to_string(udp_endpoint_t* e)
 {
-  void* ptr;
-  u_int16_t port;
-  size_t addrstr_len = 0;
-  char* addrstr, *ret;
+  if(!e)
+    return strdup("<null>");
+
+  char addrstr[INET6_ADDRSTRLEN + 1], portstr[6], *ret;
   char addrport_sep = ':';
 
-  switch (((struct sockaddr *)&e)->sa_family)
+  switch(e->addr_.ss_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 ;
+  case AF_INET: addrport_sep = ':'; break;
+  case AF_INET6: addrport_sep = '.'; break;
+  case AF_UNSPEC: return NULL;
+  default: return strdup("<unknown address type>");
   }
-  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);
+
+  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;
 }
 
-char* udp_get_remote_end_string(udp_t* sock)
-{
-  if(!sock || !sock->remote_end_set_)
-    return NULL;
 
-  return udp_endpoint_to_string(sock->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(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_t* sock, u_int8_t* buf, u_int32_t len)
 {
-  if(!sock || !sock->remote_end_set_ || !sock->active_sock_)
+  if(!sock || !buf || !sock->active_sock_ || !sock->active_sock_->remote_end_set_)
     return 0;
 
-  socklen_t socklen = sizeof(sock->remote_end_);
-  if((((struct sockaddr *)&sock->active_sock_->local_end_)->sa_family) == AF_INET)
-    socklen = sizeof(struct sockaddr_in);
-  else if ((((struct sockaddr *)&sock->active_sock_->local_end_)->sa_family) == AF_INET6)
-    socklen = sizeof(struct sockaddr_in6);
-
-  return sendto(sock->active_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_);
 }
-