Imported Upstream version 0.3.3
[debian/uanytun.git] / src / udp.c
index 7f539a8..3e1def6 100644 (file)
--- a/src/udp.c
+++ b/src/udp.c
@@ -15,7 +15,7 @@
  *  tunneling and relaying of packets of any protocol.
  *  
  *
- *  Copyright (C) 2007-2008 Christian Pointner <equinox@anytun.org>
+ *  Copyright (C) 2007-2010 Christian Pointner <equinox@anytun.org>
  *
  *  This file is part of uAnytun.
  *
 #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)
+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->fd_ = 0;
-  memset(&(sock->local_end_), 0, sizeof(sock->local_end_));
-  memset(&(sock->remote_end_), 0, sizeof(sock->local_end_));
+
+  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;
@@ -64,12 +64,12 @@ int udp_init(udp_socket_t* sock, const char* local_addr, const char* port, resol
   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,46 +78,86 @@ 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 = NULL;
+  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_), 0, sizeof(new_sock->local_end_));
+    new_sock->next_ = NULL;
+
+    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_), r->ai_addr, r->ai_addrlen);
+    new_sock->fd_ = socket(r->ai_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_, r->ai_addr, r->ai_addrlen);
+    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, "listening on: %s", local_string);
+      free(local_string);
+    }
 
-  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);
-    return -1;
+    r = r->ai_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;
-  }
-  
-/* 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_init_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_;
+  }
+
+  return max_fd;
+}
+
+int udp_set_remote(udp_t* sock, const char* remote_addr, const char* port, resolv_addr_type_t resolv_type)
 {
   if(!sock || !remote_addr || !port) 
     return -1;
@@ -145,18 +185,53 @@ int udp_set_remote(udp_socket_t* sock, const char* remote_addr, const char* port
   }
   memcpy(&(sock->remote_end_), res->ai_addr, res->ai_addrlen);
   sock->remote_end_set_ = 1;
+
+  if(!sock->active_sock_) {
+    udp_socket_t* s = sock->socks_;
+    while(s) {
+      if((((struct sockaddr *)&s->local_end_)->sa_family) == res->ai_family) {
+        sock->active_sock_ = s;
+        break;
+      }
+      s = s->next_;
+    }
+  }
+
   freeaddrinfo(res);
 
   return 0;
 }
 
-void udp_close(udp_socket_t* sock)
+void udp_set_active_sock(udp_t* sock, int fd)
+{
+  if(!sock || (sock->active_sock_ && sock->active_sock_->fd_ == fd))
+    return;
+
+  udp_socket_t* s = sock->socks_;
+  while(s) {
+    if(s->fd_ == fd) {
+      sock->active_sock_ = s;
+      return;
+    }
+    s = s->next_;
+  }
+}
+
+void udp_close(udp_t* sock)
 {
   if(!sock)
     return;
 
-  if(sock->fd_ > 0)
-    close(sock->fd_);
+  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;
 }
 
 char* udp_endpoint_to_string(udp_endpoint_t e)
@@ -194,15 +269,7 @@ char* udp_endpoint_to_string(udp_endpoint_t e)
   return ret;
 }
 
-char* udp_get_local_end_string(udp_socket_t* sock)
-{
-  if(!sock)
-    return NULL;
-
-  return udp_endpoint_to_string(sock->local_end_);
-}
-
-char* udp_get_remote_end_string(udp_socket_t* sock)
+char* udp_get_remote_end_string(udp_t* sock)
 {
   if(!sock || !sock->remote_end_set_)
     return NULL;
@@ -210,27 +277,26 @@ char* udp_get_remote_end_string(udp_socket_t* sock)
   return udp_endpoint_to_string(sock->remote_end_);
 }
  
-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)
     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, &socklen);
 }
 
-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 || !sock->remote_end_set_ || !sock->active_sock_)
+    return 0;
 
   socklen_t socklen = sizeof(sock->remote_end_);
-#ifdef NO_V4MAPPED
-  if((((struct sockaddr *)&sock->local_end_)->sa_family) == AF_INET)
+  if((((struct sockaddr *)&sock->active_sock_->local_end_)->sa_family) == AF_INET)
     socklen = sizeof(struct sockaddr_in);
-  else if ((((struct sockaddr *)&sock->local_end_)->sa_family) == AF_INET6)
+  else if ((((struct sockaddr *)&sock->active_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->remote_end_), socklen);
 }