IPv6 dual-stack support
authorFelix Morgner <felix.morgner@gmail.com>
Fri, 6 Jun 2014 08:21:59 +0000 (10:21 +0200)
committerFelix Morgner <felix.morgner@gmail.com>
Fri, 6 Jun 2014 08:22:13 +0000 (10:22 +0200)
This patch implements full IPv4-IPv6-dual-stack support for umurmur. It
introduces some new configuration and command line options:

  - The '-A' and '-B' options which work similar to the old '-a' and
    '-b' options but for IPv6.
  - The 'bindaddr6' and 'bindport6' options, which too, work like their
    IPv4 counterparts.

By default, the IPv6 implementation listens on all addresses and the
default port 64738, just like the IPv4 part. If your system does
support IPv6 and you haven't setup a proper IPv6 firewall, now is the
time to do it. In the meantime you could simply listen on the loopback
device (::1), if you'd like to 'disable' IPv6 functionality.

Note that since the implementation is fully dual-stack, umurmur will
simply warn you if either IPv4 or IPv6 is unavailable but it will still
run fine with no further action is required (unless of course you want
to use one of the unavailable protocols, which would mean you'd have to
enable it in your kernel configuration).

21 files changed:
CMakeLists.txt
configure.ac
src/CMakeLists.txt
src/Makefile.am
src/ban.c
src/ban.h
src/byteorder.h
src/client.c
src/client.h
src/conf.c
src/conf.h
src/log.c
src/main.c
src/messagehandler.c
src/server.c
src/ssl.c [deleted file]
src/ssli_gnutls.c [new file with mode: 0644]
src/ssli_openssl.c [new file with mode: 0644]
src/ssli_polarssl.c [new file with mode: 0644]
src/util.c [new file with mode: 0644]
src/util.h [new file with mode: 0644]

index 6e81c3c12906a2bd8cd14367e9455b155c32a73c..7ff95a019e777695b7bd49f50a8d33234cdc593f 100644 (file)
@@ -9,9 +9,9 @@ option(USE_POLARSSL_TESTCERT "Link to the PolarSSL test certificate and key." OF
 option(USE_POLARSSL_HAVEGE "Use the PolarSSL HAVEGE random generator key." OFF)
 
 if(USE_POLARSSL_TESTCERT OR USE_POLARSSL_HAVEGE)
-  if(SSL MATCHES "openssl")
+  if(SSL MATCHES "openssl" OR SSL MATCHES "gnutls")
     message(FATAL_ERROR "Selecting USE_POLARSSL_TESTCERT or USE_POLARSSL_HAVEGE implies SSL=polarssl")
-  endif(SSL MATCHES "openssl")
+  endif(SSL MATCHES "openssl" OR SSL MATCHES "gnutls")
 endif(USE_POLARSSL_TESTCERT OR USE_POLARSSL_HAVEGE)
 
 find_package(Libconfig REQUIRED)
@@ -19,14 +19,18 @@ find_package(ProtobufC REQUIRED)
 include(CheckFunctionExists)
 include(CheckLibraryExists)
 
-if(SSL MATCHES "openssl")
+if("${SSL}" STREQUAL "")
+  set(SSL "polarssl")
+endif("${SSL}" STREQUAL "")
+
+if("${SSL}" STREQUAL "openssl")
   find_package(OpenSSL REQUIRED)
   if(OPENSSL_FOUND)
     set(SSLIMP_LIBRARIES ${OPENSSL_LIBRARIES})
     set(SSLIMP_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR})
     set(SSLIMP_LIBRARY_DIR ${OPENSSL_LIB_DIR})
   endif(OPENSSL_FOUND)
-else(SSL MATCHES "openssl")
+elseif("${SSL}" STREQUAL "polarssl")
   find_package(PolarSSL REQUIRED)
   if(POLARSSL_FOUND)
     set(USE_POLARSSL ON)
@@ -34,7 +38,14 @@ else(SSL MATCHES "openssl")
     set(SSLIMP_INCLUDE_DIR ${POLARSSL_INCLUDE_DIR})
     set(SSLIMP_LIBRARY_DIR ${POLARSSL_LIB_DIR})
   endif(POLARSSL_FOUND)
-endif(SSL MATCHES "openssl")
+elseif("${SSL}" STREQUAL "gnutls")
+  find_package(GnuTLS REQUIRED)
+  if(GNUTLS_FOUND)
+    set(SSLIMP_LIBRARIES ${GNUTLS_LIBRARIES})
+    set(SSLIMP_INCLUDE_DIR ${GNUTLS_INCLUDE_DIR})
+    set(SSLIMP_LIBRARY_DIR ${GNUTLS_LIB_DIR})
+  endif(GNUTLS_FOUND)
+endif("${SSL}" STREQUAL "openssl")
 
 check_library_exists(rt clock_gettime "time.h" HAVE_CLOCK_GETTIME_RT)
 
index ff2ddd526d4ab27cad48e28805ac4c10a4b424b6..3fb9757641441aa7106392cd9f90186d295672f2 100644 (file)
@@ -32,7 +32,7 @@
 AC_PREREQ([2.63])
 AC_INIT([umurmur], [0.2.14], [http://code.google.com/p/umurmur/issues/entry], [umurmur], [http://code.google.com/p/umurmur])
 AC_CONFIG_SRCDIR([src/client.h])
-AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_HEADERS([src/config.h])
 AM_INIT_AUTOMAKE
 AC_CANONICAL_HOST
 
@@ -86,6 +86,9 @@ AS_IF([test "x$with_ssl" = xopenssl], [
        AC_CHECK_LIB([ssl], [SSL_library_init], [], [AC_MSG_ERROR([could not find libssl])])
 ])
 
+AM_CONDITIONAL(USE_OPENSSL, test x$with_ssl = xopenssl)
+AM_CONDITIONAL(USE_GNUTLS, test x$with_ssl = xgnutls)
+
 AC_DEFINE([DEFAULT_CONFIG], ["/etc/umurmur.conf"], [Default config])
 
 # Checks for header files.
index 58288f5cd74774a667802d820611b12a51ebfa9b..a73a5a2261b6105d9a5afdbbe451603725991709 100644 (file)
@@ -1,20 +1,28 @@
-set(SOURCE
-   ${SOURCE}
-   ${CMAKE_CURRENT_SOURCE_DIR}/Mumble.pb-c.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/ban.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/channel.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/client.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/conf.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/crypt.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/log.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/main.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/messagehandler.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/messages.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/pds.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/server.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/ssl.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/timer.c
-   ${CMAKE_CURRENT_SOURCE_DIR}/voicetarget.c
+set(SOURCE_FILES
+  ${CMAKE_CURRENT_SOURCE_DIR}/Mumble.pb-c.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/ban.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/channel.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/client.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/conf.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/crypt.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/log.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/main.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/messagehandler.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/messages.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/pds.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/server.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/timer.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/util.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/voicetarget.c
+  )
+
+if(SSL MATCHES "openssl")
+  list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/ssli_openssl.c)
+elseif(SSL MATCHES "polarssl")
+  list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/ssli_polarssl.c)
+elseif(SSL MATCHES "gnutls")
+  list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/ssli_gnutls.c)
+endif(SSL MATCHES "openssl")
+
+set(SOURCE ${SOURCE} ${SOURCE_FILES} PARENT_SCOPE)
 
-   PARENT_SCOPE
-)
index 3118dc59cd49042b853336993cf14e14a2c9aae4..912232ef57f6f8e1442e0fbaa761f138ed5b9de6 100644 (file)
 #  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 #  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 bin_PROGRAMS=umurmurd
-umurmurd_SOURCES=client.c main.c messages.c pds.c server.c ssl.c log.c conf.c crypt.c timer.c messagehandler.c channel.c Mumble.pb-c.c voicetarget.c ban.c
+umurmurd_SOURCES=client.c main.c messages.c pds.c server.c log.c conf.c crypt.c timer.c messagehandler.c channel.c Mumble.pb-c.c voicetarget.c ban.c
 
+if USE_OPENSSL
+umurmurd_SOURCES+=ssli_openssl.c
+else
+if USE_GNUTLS
+umurmurd_SOURCES+=ssli_gnutls.c
+else
+umurmurd_SOURCES+=ssli_polarssl.c
+endif
+endif
index e1855de2cb830be0c4d9ff12ab15c8031c90d6a9..3c7c4ffa656ea26c2ab285192b03f9466d4f08d5 100644 (file)
--- a/src/ban.c
+++ b/src/ban.c
@@ -36,6 +36,7 @@
 #include "ban.h"
 #include "conf.h"
 #include "ssl.h"
+#include "util.h"
 
 static void Ban_saveBanFile(void);
 static void Ban_readBanFile(void);
@@ -74,8 +75,13 @@ void Ban_UserBan(client_t *client, char *reason)
        memset(ban, 0, sizeof(ban_t));
 
        memcpy(ban->hash, client->hash, 20);
-       memcpy(&ban->address, &client->remote_tcp.sin_addr, sizeof(in_addr_t));
-       ban->mask = 128;
+       if (client->remote_tcp.ss_family == AF_INET) {
+               memcpy(&ban->address, &(((struct sockaddr_in*)&client->remote_tcp)->sin_addr), sizeof(in_addr_t));
+               ban->mask = sizeof(in_addr_t);
+       } else {
+               memcpy(&ban->address, &(((struct sockaddr_in6*)&client->remote_tcp)->sin6_addr), 4 * sizeof(in_addr_t));
+               ban->mask = 4 * sizeof(in_addr_t);
+       }
        ban->reason = strdup(reason);
        ban->name = strdup(client->username);
        ban->time = time(NULL);
@@ -88,8 +94,9 @@ void Ban_UserBan(client_t *client, char *reason)
                Ban_saveBanFile();
 
        SSLi_hash2hex(ban->hash, hexhash);
+
        Log_info_client(client, "User kickbanned. Reason: '%s' Hash: %s IP: %s Banned for: %d seconds",
-                       ban->reason, hexhash, inet_ntoa(*((struct in_addr *)&ban->address)), ban->duration);
+               ban->reason, hexhash, Util_clientAddressToString(client), ban->duration);
 }
 
 
@@ -106,8 +113,8 @@ void Ban_pruneBanned()
 #ifdef DEBUG
                SSLi_hash2hex(ban->hash, hexhash);
                Log_debug("BL: User %s Reason: '%s' Hash: %s IP: %s Time left: %d",
-                         ban->name, ban->reason, hexhash, inet_ntoa(*((struct in_addr *)&ban->address)),
-                         bantime_long / 1000000LL - Timer_elapsed(&ban->startTime) / 1000000LL);
+                       ban->name, ban->reason, hexhash, inet_ntoa(*((struct in_addr *)&ban->address)),
+                       bantime_long / 1000000LL - Timer_elapsed(&ban->startTime) / 1000000LL);
 #endif
                /* Duration of 0 = forever */
                if (ban->duration != 0 && Timer_isElapsed(&ban->startTime, bantime_long)) {
@@ -136,24 +143,23 @@ bool_t Ban_isBanned(client_t *client)
 
 }
 
-bool_t Ban_isBannedAddr(in_addr_t *addr)
+bool_t Ban_isBannedAddr(struct sockaddr_storage *address)
 {
        struct dlist *itr;
        ban_t *ban;
-       int mask;
        in_addr_t tempaddr1, tempaddr2;
 
        list_iterate(itr, &banlist) {
                ban = list_get_entry(itr, ban_t, node);
-               mask = ban->mask - 96;
-               if (mask < 32) { /* XXX - only ipv4 support */
-                       memcpy(&tempaddr1, addr, sizeof(in_addr_t));
-                       memcpy(&tempaddr2, &ban->address, sizeof(in_addr_t));
-                       tempaddr1 &= (2 ^ mask) - 1;
-                       tempaddr2 &= (2 ^ mask) - 1;
+
+               if(ban->mask == sizeof(in_addr_t)) {
+                       if(memcmp(ban->address, &((struct sockaddr_in *)address)->sin_addr, ban->mask) == 0)
+                               return true;
+               }
+               else {
+                       if(memcmp(ban->address, &((struct sockaddr_in6 *)address)->sin6_addr, ban->mask) == 0)
+                               return true;
                }
-               if (memcmp(&tempaddr1, &tempaddr2, sizeof(in_addr_t)) == 0)
-                       return true;
        }
        return false;
 }
@@ -185,7 +191,7 @@ message_t *Ban_getBanList(void)
                memcpy(&address[12], &ban->address, 4);
                memset(&address[10], 0xff, 2); /* IPv4 */
                Msg_banList_addEntry(msg, i++, address, ban->mask, ban->name,
-                                    hexhash, ban->reason, timestr, ban->duration);
+                       hexhash, ban->reason, timestr, ban->duration);
        }
        return msg;
 }
@@ -254,7 +260,7 @@ static void Ban_saveBanFile(void)
                ban = list_get_entry(itr, ban_t, node);
                SSLi_hash2hex(ban->hash, hexhash);
                fprintf(file, "%s,%s,%d,%ld,%d,%s,%s\n", hexhash, inet_ntoa(*((struct in_addr *)&ban->address)),
-                       ban->mask, (long int)ban->time, ban->duration, ban->name, ban->reason);
+                       ban->mask, (long int)ban->time, ban->duration, ban->name, ban->reason);
        }
        fclose(file);
        banlist_changed = false;
index 89cd14099c5924ec705ce271acf3ce12189feb5e..dccf26fdac1cac76256169e587b79834b03df18d 100644 (file)
--- a/src/ban.h
+++ b/src/ban.h
@@ -38,7 +38,7 @@
 
 typedef struct {
        uint8_t hash[20];
-       in_addr_t address;
+       uint8_t address[16];
        uint32_t mask;
        char *reason;
        char *name;
@@ -51,7 +51,7 @@ typedef struct {
 void Ban_UserBan(client_t *client, char *reason);
 void Ban_pruneBanned();
 bool_t Ban_isBanned(client_t *client);
-bool_t Ban_isBannedAddr(in_addr_t *addr);
+bool_t Ban_isBannedAddr(struct sockaddr_storage *address);
 int Ban_getBanCount(void);
 message_t *Ban_getBanList(void);
 void Ban_putBanList(message_t *msg, int n_bans);
index c4e78a91c1aea89147ac25174f5e1b579b73c2e0..d44db26bd75667e3b9ae048506433a4d83090add 100644 (file)
@@ -35,7 +35,7 @@
 
 #include <stdint.h>
 
-#if defined(NETBSD) || defined(FREEBSD) || defined(OPENBSD) || defined(MACOSX)
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
 #include <machine/endian.h>
 #if BYTE_ORDER == BIG_ENDIAN
 #define BYTE_ORDER_BIG_ENDIAN
@@ -45,7 +45,7 @@
 #if __BYTE_ORDER == __BIG_ENDIAN
 #define BYTE_ORDER_BIG_ENDIAN
 #endif // __BYTE_ORDER == __BIG_ENDIAN
-#endif // defined(NETBSD) || defined(FREEBSD) || defined(OPENBSD)
+#endif // defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
 
 #if defined(__LP64__)
 #define BLOCKSIZE 2
index f7d2b709d35255a996d21541f20620f16ea6a7f7..526e1f4bf5f2fc35cee0b33e10dd5ecfba2e3d90 100644 (file)
@@ -8,13 +8,13 @@
    are met:
 
    - Redistributions of source code must retain the above copyright notice,
-     this list of conditions and the following disclaimer.
+   this list of conditions and the following disclaimer.
    - 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 list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
    - Neither the name of the Developers nor the names of its contributors may
-     be used to endorse or promote products derived from this software without
-     specific prior written permission.
+   be used to endorse or promote products derived from this software without
+   specific prior written permission.
 
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -27,7 +27,7 @@
    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 <sys/poll.h>
 #include <sys/socket.h>
 #include <fcntl.h>
@@ -46,6 +46,7 @@
 #include "version.h"
 #include "voicetarget.h"
 #include "ban.h"
+#include "util.h"
 
 extern char system_string[], version_string[];
 
@@ -62,7 +63,8 @@ bool_t bOpus = true;
 int iCodecAlpha, iCodecBeta;
 bool_t bPreferAlpha;
 
-extern int udpsock;
+extern int* udpsocks;
+extern bool_t hasv4;
 
 void Client_init()
 {
@@ -290,7 +292,7 @@ void recheckCodecVersions(client_t *connectingClient)
                client_itr = NULL;
                while (Client_iterate(&client_itr) != NULL) {
                        if ((client_itr->authenticated || client_itr == connectingClient) &&
-                           !client_itr->bOpus) {
+                               !client_itr->bOpus) {
                                Client_textmessage(client_itr, OPUS_WARN_SWITCHING);
                        }
                }
@@ -317,26 +319,24 @@ static int findFreeSessionId()
        return -1;
 }
 
-int Client_add(int fd, struct sockaddr_in *remote)
+int Client_add(int fd, struct sockaddr_storage *remote)
 {
-       client_t *newclient;
+       client_tnewclient;
        message_t *sendmsg;
 
-       if (Ban_isBannedAddr((in_addr_t *)&remote->sin_addr)) {
-               Log_info("Address %s banned. Disconnecting", inet_ntoa(remote->sin_addr));
+       if (Ban_isBannedAddr(remote)) {
+               Log_info("Address %s banned. Disconnecting", Util_addressToString(remote));
                return -1;
        }
-       newclient = malloc(sizeof(client_t));
-       if (newclient == NULL)
-               Log_fatal("Out of memory");
-       memset(newclient, 0, sizeof(client_t));
+
+       if ((newclient = calloc(1, sizeof(client_t))) == NULL)
+               Log_fatal("(%s:%s): Out of memory while allocating %d bytes.", __FILE__, __LINE__, sizeof(client_t));
 
        newclient->tcpfd = fd;
-       memcpy(&newclient->remote_tcp, remote, sizeof(struct sockaddr_in));
+       memcpy(&newclient->remote_tcp, remote, sizeof(struct sockaddr_storage));
        newclient->ssl = SSLi_newconnection(&newclient->tcpfd, &newclient->SSLready);
        if (newclient->ssl == NULL) {
-               Log_warn("SSL negotiation failed with %s:%d", inet_ntoa(remote->sin_addr),
-                                ntohs(remote->sin_port));
+               Log_warn("SSL negotiation failed with %s on port %d", Util_addressToString(remote), Util_addressToPort(remote));
                free(newclient);
                return -1;
        }
@@ -493,8 +493,8 @@ int Client_read(client_t *client)
                                 * 1. A valid size. The only message that is this big is UserState message with a big texture
                                 * 2. An invalid size = protocol error, e.g. connecting with a 1.1.x client
                                 */
-                               Log_warn("Too big message received (%d bytes). Playing safe and disconnecting client %s:%d",
-                                                client->msgsize, inet_ntoa(client->remote_tcp.sin_addr), ntohs(client->remote_tcp.sin_port));
+                               //                Log_warn("Too big message received (%d bytes). Playing safe and disconnecting client %s:%d",
+                               //                         client->msgsize, inet_ntoa(client->remote_tcp.sin_addr), ntohs(client->remote_tcp.sin_port));
                                Client_free(client);
                                return -1;
                                /* client->rxcount = client->msgsize = 0; */
@@ -515,7 +515,7 @@ int Client_read(client_t *client)
                                return 0;
                        }
                        else if (SSLi_get_error(client->ssl, rc) == SSLI_ERROR_ZERO_RETURN ||
-                                SSLi_get_error(client->ssl, rc) == 0) {
+                               SSLi_get_error(client->ssl, rc) == 0) {
                                Log_info_client(client, "Connection closed by peer");
                                Client_close(client);
                        }
@@ -525,7 +525,7 @@ int Client_read(client_t *client)
                                                Log_info_client(client, "Connection closed by peer");
                                        else
                                                Log_info_client(client,"Error: %s  - Closing connection (code %d)",
-                                                               strerror(errno));
+                                                       strerror(errno));
                                }
                                else if (SSLi_get_error(client->ssl, rc) == SSLI_ERROR_CONNRESET) {
                                        Log_info_client(client, "Connection reset by peer");
@@ -584,7 +584,7 @@ int Client_write(client_t *client)
                }
                else {
                        if (SSLi_get_error(client->ssl, rc) == SSLI_ERROR_SYSCALL) {
-                               Log_info_client(client, "Error: %s  - Closing connection", strerror(errno));
+                               Log_info_client(client, "Error: %s      - Closing connection", strerror(errno));
                        }
                        else if (SSLi_get_error(client->ssl, rc) == SSLI_ERROR_CONNRESET) {
                                Log_info_client(client, "Connection reset by peer");
@@ -613,6 +613,7 @@ int Client_send_message_ver(client_t *client, message_t *msg, uint32_t version)
                return Client_send_message(client, msg);
        else
                Msg_free(msg);
+       return -1;
 }
 
 int Client_send_message(client_t *client, message_t *msg)
@@ -751,14 +752,16 @@ static bool_t checkDecrypt(client_t *client, const uint8_t *encrypted, uint8_t *
 }
 
 #define UDP_PACKET_SIZE 1024
-int Client_read_udp()
+int Client_read_udp(int udpsock)
 {
        int len;
-       struct sockaddr_in from;
-       socklen_t fromlen = sizeof(struct sockaddr_in);
-       uint64_t key;
+       struct sockaddr_storage from;
+       socklen_t fromlen = sizeof(struct sockaddr_storage);
+       uint8_t key[KEY_LENGTH];
        client_t *itr;
        UDPMessageType_t msgType;
+       uint8_t fromaddress[4 * sizeof(in_addr_t)];
+       uint16_t fromport;
 
 #if defined(__LP64__)
        uint8_t encbuff[UDP_PACKET_SIZE + 8];
@@ -769,22 +772,33 @@ int Client_read_udp()
        uint8_t buffer[UDP_PACKET_SIZE];
 
        len = recvfrom(udpsock, encrypted, UDP_PACKET_SIZE, MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
-       if (len == 0) {
-               return -1;
-       } else if (len < 0) {
+
+       memset(key, 0, KEY_LENGTH);
+
+       fromport = Util_addressToPort(&from);
+
+       if(from.ss_family == AF_INET) {
+               memcpy(fromaddress, &((struct sockaddr_in*)&from)->sin_addr, sizeof(in_addr_t));
+               memcpy(&key[0], &fromport, 2);
+               memcpy(&key[2], fromaddress, sizeof(in_addr_t));
+       } else {
+               memcpy(fromaddress, &((struct sockaddr_in6*)&from)->sin6_addr, 4 * sizeof(in_addr_t));
+               memcpy(&key[0], &fromport, 2);
+               memcpy(&key[2], fromaddress, 4 * sizeof(in_addr_t));
+       }
+
+       if (len <= 0)
                return -1;
-       } else if (len < 5) {
-               // 4 bytes crypt header + type + session
-               return 0;
-       } else if (len > UDP_PACKET_SIZE) {
+       else if (len < 5 || len > UDP_PACKET_SIZE) /* 4 bytes crypt header + type + session */
                return 0;
-       }
 
-       /* Ping packet */
+       /*
+        * Reply to ping packet
+        * The second and third uint32_t are the timestamp, which will be returned unmodified
+        */
        if (len == 12 && *encrypted == 0) {
                uint32_t *ping = (uint32_t *)encrypted;
                ping[0] = htonl((uint32_t)PROTOCOL_VERSION);
-               // 1 and 2 will be the timestamp, which we return unmodified.
                ping[3] = htonl((uint32_t)clientcount);
                ping[4] = htonl((uint32_t)getIntConf(MAX_CLIENTS));
                ping[5] = htonl((uint32_t)getIntConf(MAX_BANDWIDTH));
@@ -793,23 +807,35 @@ int Client_read_udp()
                return 0;
        }
 
-       key = (((uint64_t)from.sin_addr.s_addr) << 16) ^ from.sin_port;
        itr = NULL;
 
        while (Client_iterate(&itr) != NULL) {
-               if (itr->key == key) {
+               if (memcmp(itr->key, key, KEY_LENGTH) == 0) {
                        if (!checkDecrypt(itr, encrypted, buffer, len))
                                goto out;
                        break;
                }
        }
        if (itr == NULL) { /* Unknown peer */
+               struct sockaddr_storage itraddressstorage;
+               uint8_t itraddress[4 * sizeof(in_addr_t)];
+               int addresslength;
+
                while (Client_iterate(&itr) != NULL) {
-                       if (itr->remote_tcp.sin_addr.s_addr == from.sin_addr.s_addr) {
+                       itraddressstorage = itr->remote_tcp;
+                       if(itraddressstorage.ss_family == AF_INET) {
+                               memcpy(itraddress, &((struct sockaddr_in*)&from)->sin_addr, sizeof(in_addr_t));
+                               addresslength = sizeof(in_addr_t);
+                       } else {
+                               memcpy(itraddress, &((struct sockaddr_in6*)&from)->sin6_addr, 4 * sizeof(in_addr_t));
+                               addresslength = 4 * sizeof(in_addr_t);
+                       }
+
+                       if (memcmp(itraddress, fromaddress, addresslength) == 0) {
                                if (checkDecrypt(itr, encrypted, buffer, len)) {
-                                       itr->key = key;
-                                       Log_info_client(itr, "New UDP connection port %d", ntohs(from.sin_port));
-                                       memcpy(&itr->remote_udp, &from, sizeof(struct sockaddr_in));
+                                       memcpy(itr->key, key, KEY_LENGTH);
+                                       Log_info_client(itr, "New UDP connection from %s on port %d", Util_clientAddressToString(itr), fromport);
+                                       memcpy(&itr->remote_udp, &from, sizeof(struct sockaddr_storage));
                                        break;
                                }
                        }
@@ -823,21 +849,21 @@ int Client_read_udp()
        len -= 4; /* Adjust for crypt header */
        msgType = (UDPMessageType_t)((buffer[0] >> 5) & 0x7);
        switch (msgType) {
-       case UDPVoiceSpeex:
-       case UDPVoiceCELTAlpha:
-       case UDPVoiceCELTBeta:
-               if (bOpus)
+               case UDPVoiceSpeex:
+               case UDPVoiceCELTAlpha:
+               case UDPVoiceCELTBeta:
+                       if (bOpus)
+                               break;
+               case UDPVoiceOpus:
+                       Client_voiceMsg(itr, buffer, len);
+                       break;
+               case UDPPing:
+                       Log_debug("UDP Ping reply len %d", len);
+                       Client_send_udp(itr, buffer, len);
+                       break;
+               default:
+                       Log_debug("Unknown UDP message type from %s port %d", Util_clientAddressToString(itr), fromport);
                        break;
-       case UDPVoiceOpus:
-               Client_voiceMsg(itr, buffer, len);
-               break;
-       case UDPPing:
-               Log_debug("UDP Ping reply len %d", len);
-               Client_send_udp(itr, buffer, len);
-               break;
-       default:
-               Log_debug("Unknown UDP message type from %s port %d", inet_ntoa(from.sin_addr), ntohs(from.sin_port));
-               break;
        }
 
 out:
@@ -913,7 +939,7 @@ int Client_voiceMsg(client_t *client, uint8_t *data, int len)
                        c = list_get_entry(itr, client_t, chan_node);
                        Client_send_voice(client, c, buffer, pds->offset + 1, poslen);
                }
-       } else if ((vt = Voicetarget_get_id(client, target)) != NULL) { /* Targeted whisper */
+       } else if ((vt = Voicetarget_get_id(client, target)) != NULL) { /* Targeted whisper */
                int i;
                channel_t *ch;
                /* Channels */
@@ -980,12 +1006,13 @@ out:
        return 0;
 }
 
-
 static int Client_send_udp(client_t *client, uint8_t *data, int len)
 {
        uint8_t *buf, *mbuf;
 
-       if (client->remote_udp.sin_port != 0 && CryptState_isValid(&client->cryptState) &&
+       int udpsock = (client->remote_udp.ss_family == AF_INET) ? udpsocks[0] : udpsocks[(hasv4) ? 1 : 0];
+
+       if (Util_clientAddressToPortUDP(client) != 0 && CryptState_isValid(&client->cryptState) &&
                client->bUDP) {
 #if defined(__LP64__)
                buf = mbuf = malloc(len + 4 + 16);
@@ -998,7 +1025,11 @@ static int Client_send_udp(client_t *client, uint8_t *data, int len)
 
                CryptState_encrypt(&client->cryptState, data, buf, len);
 
-               sendto(udpsock, buf, len + 4, 0, (struct sockaddr *)&client->remote_udp, sizeof(struct sockaddr_in));
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+                       sendto(udpsock, buf, len + 4, 0, (struct sockaddr *)&client->remote_udp, client->remote_tcp.ss_len);
+#else
+                       sendto(udpsock, buf, len + 4, 0, (struct sockaddr *)&client->remote_udp, sizeof(struct sockaddr_storage));
+#endif
 
                free(mbuf);
        } else {
index c6085d298e6563779f4763f60d4d6f9b0ab284dd..a710f5c8a63d93eafbec883b8807c7a14984ed5a 100644 (file)
@@ -55,6 +55,7 @@
 #define MAX_CODECS 10
 #define MAX_TOKENSIZE 64
 #define MAX_TOKENS 32
+#define KEY_LENGTH sizeof(uint16_t) + 4 * sizeof(in_addr_t)
 
 #define IS_AUTH(_a_) ((_a_)->authenticated)
 
@@ -65,13 +66,12 @@ typedef struct {
        bool_t shutdown_wait;
        cryptState_t cryptState;
        bool_t readBlockedOnWrite, writeBlockedOnRead;
-
-       struct sockaddr_in remote_tcp;
-       struct sockaddr_in remote_udp;
+       struct sockaddr_storage remote_tcp;
+       struct sockaddr_storage remote_udp;
        uint8_t rxbuf[BUFSIZE], txbuf[BUFSIZE];
        uint32_t rxcount, msgsize, drainleft, txcount, txsize;
        int sessionId;
-       uint64_t key;
+       uint8_t key[KEY_LENGTH];
        char *username;
        bool_t bUDP, authenticated, deaf, mute, self_deaf, self_mute, recording, bOpus;
        char *os, *release, *os_version;
@@ -109,7 +109,7 @@ typedef struct {
 void Client_init();
 int Client_getfds(struct pollfd *pollfds);
 void Client_janitor();
-int Client_add(int fd, struct sockaddr_in *remote);
+int Client_add(int fd, struct sockaddr_storage *remote);
 int Client_read_fd(int fd);
 int Client_write_fd(int fd);
 int Client_send_message(client_t *client, message_t *msg);
@@ -119,7 +119,7 @@ int Client_count(void);
 void Client_close(client_t *client);
 client_t *Client_iterate(client_t **client);
 int Client_send_message_except(client_t *client, message_t *msg);
-int Client_read_udp(void);
+int Client_read_udp(int udpsock);
 void Client_disconnect_all();
 int Client_voiceMsg(client_t *client, uint8_t *data, int len);
 void recheckCodecVersions(client_t *connectingClient);
index 2c6f56f9ffa5fd404fd2f10618a241cf42488d6c..bf21d99bb84cbe41f00caad5d82d34b07a789505 100644 (file)
@@ -56,7 +56,7 @@ void Conf_init(const char *conffile)
                conffile = defaultconfig;
        if (config_read_file(&configuration, conffile) != CONFIG_TRUE) {
                Log_fatal("Error in config file %s line %d: %s", conffile,
-                                 config_error_line(&configuration), config_error_text(&configuration));
+                       config_error_line(&configuration), config_error_text(&configuration));
        }
 }
 
@@ -68,7 +68,7 @@ bool_t Conf_ok(const char *conffile)
                conffile = defaultconfig;
        if (config_read_file(&configuration, conffile) != CONFIG_TRUE) {
                fprintf(stderr, "Error in config file %s line %d: %s\n", conffile,
-                       config_error_line(&configuration), config_error_text(&configuration));
+                       config_error_line(&configuration), config_error_text(&configuration));
                rc = false;
        }
        config_destroy(&configuration);
@@ -86,141 +86,152 @@ const char *getStrConf(param_t param)
        const char *strsetting = NULL;
 
        switch (param) {
-       case CERTIFICATE:
-               setting = config_lookup(&configuration, "certificate");
-               if (!setting)
-                       return "/etc/umurmur/certificate.crt";
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
+               case CERTIFICATE:
+                       setting = config_lookup(&configuration, "certificate");
+                       if (!setting)
                                return "/etc/umurmur/certificate.crt";
-               }
-               break;
-       case KEY:
-               setting = config_lookup(&configuration, "private_key");
-               if (!setting)
-                       return "/etc/umurmur/private_key.key";
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return "/etc/umurmur/certificate.crt";
+                       }
+                       break;
+               case KEY:
+                       setting = config_lookup(&configuration, "private_key");
+                       if (!setting)
                                return "/etc/umurmur/private_key.key";
-               }
-               break;
-       case CAPATH:
-               setting = config_lookup(&configuration, "ca_path");
-               if (!setting)
-                 return NULL;
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return "/etc/umurmur/private_key.key";
+                       }
+                       break;
+               case CAPATH:
+                       setting = config_lookup(&configuration, "ca_path");
+                       if (!setting)
                                return NULL;
-               }
-               break;
-       case PASSPHRASE:
-               setting = config_lookup(&configuration, "password");
-               if (!setting)
-                       return "";
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return NULL;
+                       }
+                       break;
+               case PASSPHRASE:
+                       setting = config_lookup(&configuration, "password");
+                       if (!setting)
                                return "";
-               }
-               break;
-       case ADMIN_PASSPHRASE:
-               setting = config_lookup(&configuration, "admin_password");
-               if (!setting)
-                       return "";
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return "";
+                       }
+                       break;
+               case ADMIN_PASSPHRASE:
+                       setting = config_lookup(&configuration, "admin_password");
+                       if (!setting)
                                return "";
-               }
-               break;
-       case BINDADDR:
-               setting = config_lookup(&configuration, "bindaddr");
-               if (!setting)
-                       return "";
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return "";
+                       }
+                       break;
+               case BINDADDR:
+                       setting = config_lookup(&configuration, "bindaddr");
+                       if (!setting)
+                               return NULL;
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return NULL;
+                       }
+                       break;
+               case BINDADDR6:
+                       setting = config_lookup(&configuration, "bindaddr6");
+                       if (!setting)
+                               return NULL;
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return NULL;
+                       }
+                       break;
+               case WELCOMETEXT:
+                       setting = config_lookup(&configuration, "welcometext");
+                       if (!setting)
+                               return DEFAULT_WELCOME;
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return DEFAULT_WELCOME;
+                       }
+                       break;
+               case DEFAULT_CHANNEL:
+                       setting = config_lookup(&configuration, "default_channel");
+                       if (!setting)
                                return "";
-               }
-               break;
-       case WELCOMETEXT:
-               setting = config_lookup(&configuration, "welcometext");
-               if (!setting)
-                       return DEFAULT_WELCOME;
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
-                       return DEFAULT_WELCOME;
-               }
-               break;
-       case DEFAULT_CHANNEL:
-               setting = config_lookup(&configuration, "default_channel");
-               if (!setting)
-                       return "";
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
-                       return "";
-               }
-               break;
-       case USERNAME:
-               setting = config_lookup(&configuration, "username");
-               if (!setting)
-                       return "";
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
-                       return "";
-               }
-               break;
-       case GROUPNAME:
-               setting = config_lookup(&configuration, "groupname");
-               if (!setting)
-                       return "";
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
-                       return "";
-               }
-               break;
-       case LOGFILE:
-               setting = config_lookup(&configuration, "logfile");
-               if (!setting)
-                       return NULL;
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
-                       return NULL;
-               }
-               break;
-       case BANFILE:
-               setting = config_lookup(&configuration, "banfile");
-               if (!setting)
-                       return NULL;
-               else {
-                       if ((strsetting = config_setting_get_string(setting)) != NULL)
-                               return strsetting;
-                       else
-                       return NULL;
-               }
-               break;
-       default:
-               doAssert(false);
-               break;
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return "";
+                       }
+                       break;
+               case USERNAME:
+                       setting = config_lookup(&configuration, "username");
+                       if (!setting)
+                               return "";
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return "";
+                       }
+                       break;
+               case GROUPNAME:
+                       setting = config_lookup(&configuration, "groupname");
+                       if (!setting)
+                               return "";
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return "";
+                       }
+                       break;
+               case LOGFILE:
+                       setting = config_lookup(&configuration, "logfile");
+                       if (!setting)
+                               return NULL;
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return NULL;
+                       }
+                       break;
+               case BANFILE:
+                       setting = config_lookup(&configuration, "banfile");
+                       if (!setting)
+                               return NULL;
+                       else {
+                               if ((strsetting = config_setting_get_string(setting)) != NULL)
+                                       return strsetting;
+                               else
+                                       return NULL;
+                       }
+                       break;
+               default:
+                       doAssert(false);
+                       break;
        }
        return NULL;
 }
@@ -230,48 +241,56 @@ int getIntConf(param_t param)
        config_setting_t *setting = NULL;
 
        switch (param) {
-       case BINDPORT:
-               setting = config_lookup(&configuration, "bindport");
-               if (!setting)
-                       return DEFAULT_BINDPORT;
-               else {
-                       return config_setting_get_int(setting);
-               }
-               break;
-       case BAN_LENGTH:
-               setting = config_lookup(&configuration, "ban_length");
-               if (!setting)
-                       return DEFAULT_BAN_LENGTH;
-               else {
-                       return config_setting_get_int(setting);
-               }
-               break;
-       case MAX_BANDWIDTH:
-               setting = config_lookup(&configuration, "max_bandwidth");
-               if (!setting)
-                       return DEFAULT_MAX_BANDWIDTH;
-               else {
-                       return config_setting_get_int(setting);
-               }
-               break;
-       case MAX_CLIENTS:
-               setting = config_lookup(&configuration, "max_users");
-               if (!setting)
-                       return DEFAULT_MAX_CLIENTS;
-               else {
-                       return config_setting_get_int(setting);
-               }
-               break;
-       case OPUS_THRESHOLD:
-               setting = config_lookup(&configuration, "opus_threshold");
-               if (!setting)
-                       return DEFAULT_OPUS_THRESHOLD;
-               else {
-                       return config_setting_get_int(setting);
-               }
-               break;
-       default:
-               doAssert(false);
+               case BINDPORT:
+                       setting = config_lookup(&configuration, "bindport");
+                       if (!setting)
+                               return DEFAULT_BINDPORT;
+                       else {
+                               return config_setting_get_int(setting);
+                       }
+                       break;
+               case BINDPORT6:
+                       setting = config_lookup(&configuration, "bindport6");
+                       if (!setting)
+                               return DEFAULT_BINDPORT;
+                       else {
+                               return config_setting_get_int(setting);
+                       }
+                       break;
+               case BAN_LENGTH:
+                       setting = config_lookup(&configuration, "ban_length");
+                       if (!setting)
+                               return DEFAULT_BAN_LENGTH;
+                       else {
+                               return config_setting_get_int(setting);
+                       }
+                       break;
+               case MAX_BANDWIDTH:
+                       setting = config_lookup(&configuration, "max_bandwidth");
+                       if (!setting)
+                               return DEFAULT_MAX_BANDWIDTH;
+                       else {
+                               return config_setting_get_int(setting);
+                       }
+                       break;
+               case MAX_CLIENTS:
+                       setting = config_lookup(&configuration, "max_users");
+                       if (!setting)
+                               return DEFAULT_MAX_CLIENTS;
+                       else {
+                               return config_setting_get_int(setting);
+                       }
+                       break;
+               case OPUS_THRESHOLD:
+                       setting = config_lookup(&configuration, "opus_threshold");
+                       if (!setting)
+                               return DEFAULT_OPUS_THRESHOLD;
+                       else {
+                               return config_setting_get_int(setting);
+                       }
+                       break;
+               default:
+                       doAssert(false);
        }
 }
 
@@ -280,29 +299,29 @@ bool_t getBoolConf(param_t param)
        config_setting_t *setting = NULL;
 
        switch (param) {
-       case ALLOW_TEXTMESSAGE:
-               setting = config_lookup(&configuration, "allow_textmessage");
-               if (!setting)
-                       return true;
-               else
-                       return config_setting_get_bool(setting);
-               break;
-       case ENABLE_BAN:
-               setting = config_lookup(&configuration, "enable_ban");
-               if (!setting)
-                       return false;
-               else
-                       return config_setting_get_bool(setting);
-               break;
-       case SYNC_BANFILE:
-               setting = config_lookup(&configuration, "sync_banfile");
-               if (!setting)
-                       return false;
-               else
-                       return config_setting_get_bool(setting);
-               break;
-       default:
-               doAssert(false);
+               case ALLOW_TEXTMESSAGE:
+                       setting = config_lookup(&configuration, "allow_textmessage");
+                       if (!setting)
+                               return true;
+                       else
+                               return config_setting_get_bool(setting);
+                       break;
+               case ENABLE_BAN:
+                       setting = config_lookup(&configuration, "enable_ban");
+                       if (!setting)
+                               return false;
+                       else
+                               return config_setting_get_bool(setting);
+                       break;
+               case SYNC_BANFILE:
+                       setting = config_lookup(&configuration, "sync_banfile");
+                       if (!setting)
+                               return false;
+                       else
+                               return config_setting_get_bool(setting);
+                       break;
+               default:
+                       doAssert(false);
        }
 }
 
index 8253fb330ba3c190f0d6331e2b07671c83bdd81f..9a9591c22f2c3ff553265699506b24a98adfa539 100644 (file)
@@ -40,7 +40,9 @@ typedef enum param {
        PASSPHRASE,
        CAPATH,
        BINDPORT,
+       BINDPORT6,
        BINDADDR,
+       BINDADDR6,
        WELCOMETEXT,
        MAX_BANDWIDTH,
        MAX_CLIENTS,
index ebb5cd1c0ae1e4b4965da971df41a3484f538928..61a92d24994109f34c06f23a1b165a9440ebc096 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -39,6 +39,7 @@
 
 #include "log.h"
 #include "conf.h"
+#include "util.h"
 
 #define STRSIZE 254
 
@@ -191,10 +192,11 @@ void Log_info_client(client_t *client, const char *logstring, ...)
        va_end(argp);
 
        offset += snprintf(&buf[offset], STRSIZE - offset, " - [%d] %s@%s:%d",
-                                          client->sessionId,
-                                          client->username == NULL ? "" : client->username,
-                                          inet_ntoa(client->remote_tcp.sin_addr),
-                                          ntohs(client->remote_tcp.sin_port));
+               client->sessionId,
+               client->username == NULL ? "" : client->username,
+               Util_clientAddressToString(client),
+               Util_clientAddressToPortTCP(client));
+
        if (termprint)
                fprintf(stderr, "%s\n", buf);
        else if (logfile)
index bfff28ab0b4696d2d36138aea05796b11c7d736f..c1649b6fb20b6ff2e4c24aa8fc431348c2aead51 100644 (file)
@@ -58,7 +58,9 @@
 
 char system_string[64], version_string[64];
 int bindport;
+int bindport6;
 char *bindaddr;
+char *bindaddr6;
 
 void lockfile(const char *pidfile)
 {
@@ -165,14 +167,14 @@ static void switch_user(void)
 void signal_handler(int sig)
 {
        switch(sig) {
-       case SIGHUP:
-               Log_info("HUP signal received.");
-               Log_reset();
-               break;
-       case SIGTERM:
-               Log_info("TERM signal. Shutting down.");
-               Server_shutdown();
-               break;
+               case SIGHUP:
+                       Log_info("HUP signal received.");
+                       Log_reset();
+                       break;
+               case SIGTERM:
+                       Log_info("TERM signal. Shutting down.");
+                       Server_shutdown();
+                       break;
        }
 }
 
@@ -221,7 +223,7 @@ void setscheduler()
 void printhelp()
 {
        printf("uMurmur version %s ('%s'). Mumble protocol %d.%d.%d\n", UMURMUR_VERSION,
-              UMURMUR_CODENAME, PROTVER_MAJOR, PROTVER_MINOR, PROTVER_PATCH);
+               UMURMUR_CODENAME, PROTVER_MAJOR, PROTVER_MINOR, PROTVER_PATCH);
        printf("Usage: umurmurd [-d] [-r] [-h] [-p <pidfile>] [-t] [-c <conf file>] [-a <addr>] [-b <port>]\n");
        printf("       -d             - Do not daemonize - run in foreground.\n");
 #ifdef POSIX_PRIORITY_SCHEDULING
@@ -231,7 +233,9 @@ void printhelp()
        printf("       -c <conf file> - Specify configuration file (default %s)\n", DEFAULT_CONFIG);
        printf("       -t             - Test config. Error message to stderr + non-zero exit code on error\n");
        printf("       -a <address>   - Bind to IP address\n");
+       printf("       -A <address>   - Bind to IPv6 address\n");
        printf("       -b <port>      - Bind to port\n");
+       printf("       -B <port>      - Bind to port (IPv6)\n");
        printf("       -h             - Print this help\n");
        exit(0);
 }
@@ -249,112 +253,118 @@ int main(int argc, char **argv)
 
        /* Arguments */
 #ifdef POSIX_PRIORITY_SCHEDULING
-       while ((c = getopt(argc, argv, "drp:c:a:b:ht")) != EOF) {
+       while ((c = getopt(argc, argv, "drp:c:a:A:b:B:ht")) != EOF) {
 #else
-       while ((c = getopt(argc, argv, "dp:c:a:b:ht")) != EOF) {
+               while ((c = getopt(argc, argv, "dp:c:a:A:b:B:ht")) != EOF) {
 #endif
-               switch(c) {
-               case 'c':
-                       conffile = optarg;
-                       break;
-               case 'p':
-                       pidfile = optarg;
-                       break;
-               case 'a':
-                       bindaddr = optarg;
-                       break;
-               case 'b':
-                       bindport = atoi(optarg);
-                       break;
-               case 'd':
-                       nodaemon = true;
-                       break;
-               case 'h':
-                       printhelp();
-                       break;
-               case 't':
-                       testconfig = true;
-                       break;
+                       switch(c) {
+                               case 'c':
+                                       conffile = optarg;
+                                       break;
+                               case 'p':
+                                       pidfile = optarg;
+                                       break;
+                               case 'a':
+                                       bindaddr = optarg;
+                                       break;
+                               case 'A':
+                                       bindaddr6 = optarg;
+                                       break;
+                               case 'b':
+                                       bindport = atoi(optarg);
+                                       break;
+                               case 'B':
+                                       bindport6 = atoi(optarg);
+                                       break;
+                               case 'd':
+                                       nodaemon = true;
+                                       break;
+                               case 'h':
+                                       printhelp();
+                                       break;
+                               case 't':
+                                       testconfig = true;
+                                       break;
 #ifdef POSIX_PRIORITY_SCHEDULING
-               case 'r':
-                       realtime = true;
-                       break;
+                               case 'r':
+                                       realtime = true;
+                                       break;
 #endif
-               default:
-                       fprintf(stderr, "Unrecognized option\n");
-                       printhelp();
-                       break;
+                               default:
+                                       fprintf(stderr, "Unrecognized option\n");
+                                       printhelp();
+                                       break;
+                       }
                }
-       }
-
-       if (testconfig) {
-               if (!Conf_ok(conffile))
-                       exit(1);
-               else
-                       exit(0);
-       }
-
-       /* Initialize the config subsystem early;
-        * switch_user() will need to read some config variables as well as logging.
-        */
-       Conf_init(conffile);
-
-       /* Logging to terminal if not daemonizing, otherwise to syslog or log file.
-        */
-       if (!nodaemon) {
-               daemonize();
-               Log_init(false);
-               if (pidfile != NULL)
-                       lockfile(pidfile);
 
-               switch_user();
+               if (testconfig) {
+                       if (!Conf_ok(conffile))
+                               exit(1);
+                       else
+                               exit(0);
+               }
 
-               /* Reopen log file. If user switch results in access denied, we catch
-                * it early.
+               /* Initialize the config subsystem early;
+                * switch_user() will need to read some config variables as well as logging.
                 */
-               Log_reset();
-       }
-       else Log_init(true);
-
-       signal(SIGCHLD, SIG_IGN); /* ignore child */
-       signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
-       signal(SIGTTOU, SIG_IGN);
-       signal(SIGTTIN, SIG_IGN);
-       signal(SIGPIPE, SIG_IGN);
-       signal(SIGHUP, signal_handler); /* catch hangup signal */
-       signal(SIGTERM, signal_handler); /* catch kill signal */
-
-       /* Build system string */
-       if (uname(&utsbuf) == 0) {
-               snprintf(system_string, 64, "%s %s", utsbuf.sysname, utsbuf.machine);
-               snprintf(version_string, 64, "%s", utsbuf.release);
-       }
-       else {
-               snprintf(system_string, 64, "unknown unknown");
-               snprintf(version_string, 64, "unknown");
-       }
+               Conf_init(conffile);
+
+               /* Logging to terminal if not daemonizing, otherwise to syslog or log file.
+               */
+               if (!nodaemon) {
+                       daemonize();
+                       Log_init(false);
+                       if (pidfile != NULL)
+                               lockfile(pidfile);
+
+                       switch_user();
+
+                       /* Reopen log file. If user switch results in access denied, we catch
+                        * it early.
+                        */
+                       Log_reset();
+               }
+               else Log_init(true);
+
+               signal(SIGCHLD, SIG_IGN); /* ignore child */
+               signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
+               signal(SIGTTOU, SIG_IGN);
+               signal(SIGTTIN, SIG_IGN);
+               signal(SIGPIPE, SIG_IGN);
+               signal(SIGHUP, signal_handler); /* catch hangup signal */
+               signal(SIGTERM, signal_handler); /* catch kill signal */
+
+               /* Build system string */
+               if (uname(&utsbuf) == 0) {
+                       snprintf(system_string, 64, "%s %s", utsbuf.sysname, utsbuf.machine);
+                       snprintf(version_string, 64, "%s", utsbuf.release);
+               }
+               else {
+                       snprintf(system_string, 64, "unknown unknown");
+                       snprintf(version_string, 64, "unknown");
+               }
 
-       /* Initializing */
-       SSLi_init();
-       Chan_init();
-       Client_init();
-       Ban_init();
+               /* Initializing */
+               SSLi_init();
+               Chan_init();
+               Client_init();
+               Ban_init();
 
 #ifdef POSIX_PRIORITY_SCHEDULING
-       if (realtime)
-               setscheduler();
+               if (realtime)
+                       setscheduler();
 #endif
 
-       Server_run();
+               Server_run();
 
-       Ban_deinit();
-       SSLi_deinit();
-       Chan_free();
-       Log_free();
-       Conf_deinit();
+               Ban_deinit();
+               SSLi_deinit();
+               Chan_free();
+               Log_free();
+               Conf_deinit();
 
-       if (pidfile != NULL)
-               unlink(pidfile);
+               if (pidfile != NULL)
+                       unlink(pidfile);
 
-       return 0;
-}
+               return 0;
+       }
index f1a753f9e81495764d20f120f9e371ea51959b34..696cad3c6d750f90f642ee2c5aa734b07b9f0be1 100644 (file)
@@ -878,8 +878,11 @@ void Mh_handle_message(client_t *client, message_t *msg)
                                Log_fatal("Out of memory");
                        memset(sendmsg->payload.userStats->address.data, 0, 16);
                        /* ipv4 representation as ipv6 address. Supposedly correct. */
-                       memcpy(&sendmsg->payload.userStats->address.data[12], &target->remote_tcp.sin_addr, 4);
                        memset(&sendmsg->payload.userStats->address.data[10], 0xff, 2); /* IPv4 */
+      if(target->remote_tcp.ss_family == AF_INET)
+        memcpy(&sendmsg->payload.userStats->address.data[12], &((struct sockaddr_in*)&target->remote_tcp)->sin_addr, 4);
+      else
+        memcpy(&sendmsg->payload.userStats->address.data[0], &((struct sockaddr_in6*)&target->remote_tcp)->sin6_addr, 16);
                        sendmsg->payload.userStats->address.len = 16;
                }
                /* BW */
index 05f5213abc058575cd22b1591ecb68553188cf3d..09e05ddefaf0868fe799b5efe2565ca52c2c035c 100644 (file)
 #include "log.h"
 #include "timer.h"
 #include "version.h"
-
-#define LISTEN_SOCK 0
-#define TCP_SOCK 0
-#define UDP_SOCK 1
+#include "util.h"
 
 /* globals */
-int udpsock;
 bool_t shutdown_server;
 extern char *bindaddr;
+extern char *bindaddr6;
 extern int bindport;
+extern int bindport6;
+int* udpsocks;
+bool_t hasv4 = true, hasv6 = true;
 
-void Server_run()
+const int on = 1;
+int nofServerSocks = 4;
+
+/* Check which IP versions are supported by the system. */
+void checkIPversions()
 {
-       int timeout = 1000, rc;
-       struct pollfd *pollfds;
-       int tcpsock, sockopt = 1;
-       struct sockaddr_in sin;
-       int val, clientcount;
-       etimer_t janitorTimer;
-       unsigned short port;
-       in_addr_t inet_address;
+       int testsocket = -1;
 
-       /* max clients + listen sock + udp sock + client connecting that will be disconnected */
-       pollfds = malloc((getIntConf(MAX_CLIENTS) + 3) * sizeof(struct pollfd));
-       if (pollfds == NULL)
-               Log_fatal("out of memory");
+       testsocket = socket(PF_INET, SOCK_STREAM, 0);
+       hasv4 = (errno == EAFNOSUPPORT) ? false : true;
+       if (!(testsocket < 0)) close(testsocket);
 
-       /* Figure out bind address and port */
-       if (bindport != 0)
-               port = htons(bindport);
-       else
-               port = htons(getIntConf(BINDPORT));
-
-       if (bindaddr != NULL && inet_addr(bindaddr) != -1)
-               inet_address = inet_addr(bindaddr);
-       else if (inet_addr(getStrConf(BINDADDR)) !=  -1)
-               inet_address = inet_addr(getStrConf(BINDADDR));
-       else
-               inet_address = inet_addr("0.0.0.0");
-       Log_info("Bind to %s:%hu", inet_address == 0 ? "*" : inet_ntoa(*((struct in_addr *)&inet_address)), ntohs(port));
-
-       /* Prepare TCP socket */
-       memset(&sin, 0, sizeof(sin));
-       tcpsock = socket(PF_INET, SOCK_STREAM, 0);
-       if (tcpsock < 0)
-               Log_fatal("socket");
-       if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int)) != 0)
-               Log_fatal("setsockopt: %s", strerror(errno));
-       sin.sin_family = AF_INET;
-       sin.sin_port = port;
-       sin.sin_addr.s_addr = inet_address;
-
-       rc = bind(tcpsock, (struct sockaddr *) &sin, sizeof (struct sockaddr_in));
-       if (rc < 0) Log_fatal("bind: %s", strerror(errno));
-       rc = listen(tcpsock, 3);
-       if (rc < 0) Log_fatal("listen");
-       fcntl(tcpsock, F_SETFL, O_NONBLOCK);
-
-       pollfds[LISTEN_SOCK].fd = tcpsock;
-       pollfds[LISTEN_SOCK].events = POLLIN;
-
-       /* Prepare UDP socket */
-       memset(&sin, 0, sizeof(sin));
-       udpsock = socket(PF_INET, SOCK_DGRAM, 0);
-       sin.sin_family = AF_INET;
-       sin.sin_port = port;
-       sin.sin_addr.s_addr = inet_address;
-
-       rc = bind(udpsock, (struct sockaddr *) &sin, sizeof (struct sockaddr_in));
-       if (rc < 0)
-               Log_fatal("bind %d %s: %s", getIntConf(BINDPORT), getStrConf(BINDADDR), strerror(errno));
-       val = 0xe0;
-       rc = setsockopt(udpsock, IPPROTO_IP, IP_TOS, &val, sizeof(val));
-       if (rc < 0)
-               Log_warn("Server: Failed to set TOS for UDP Socket");
-       val = 0x80;
-       rc = setsockopt(udpsock, IPPROTO_IP, IP_TOS, &val, sizeof(val));
-       if (rc < 0)
-               Log_warn("Server: Failed to set TOS for UDP Socket");
-
-       fcntl(udpsock, F_SETFL, O_NONBLOCK);
-       pollfds[UDP_SOCK].fd = udpsock;
-       pollfds[UDP_SOCK].events = POLLIN | POLLHUP | POLLERR;
+       testsocket = socket(PF_INET6, SOCK_STREAM, 0);
+       hasv6 = (errno == EAFNOSUPPORT) ? false : true;
+       if (!(testsocket < 0)) close(testsocket);
 
-       Timer_init(&janitorTimer);
+       if(!hasv4)
+       {
+               Log_info("IPv4 is not supported by this system");
+               nofServerSocks -= 2;
+       }
 
-       Log_info("uMurmur version %s ('%s') protocol version %d.%d.%d",
-                UMURMUR_VERSION, UMURMUR_CODENAME, PROTVER_MAJOR, PROTVER_MINOR, PROTVER_PATCH);
-       Log_info("Visit http://code.google.com/p/umurmur/");
+       if(!hasv6)
+       {
+               Log_info("IPv6 is not supported by this system");
+               nofServerSocks -= 2;
+       }
+
+       if(nofServerSocks == 0)
+       {
+               Log_fatal("Neither IPv4 nor IPv6 are supported by this system");
+       }
+}
+
+/* Initialize the address structures for IPv4 and IPv6 */
+struct sockaddr_storage** Server_setupAddressesAndPorts()
+{
+       struct sockaddr_storage** addresses = calloc(2, sizeof(void*));
+
+       struct sockaddr_storage* v4address = calloc(1, sizeof(struct sockaddr_storage));
+       v4address->ss_family = AF_INET;
+       struct sockaddr_storage* v6address = calloc(1, sizeof(struct sockaddr_storage));
+       v6address->ss_family = AF_INET6;
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+       v4address->ss_len = sizeof(struct sockaddr_storage);
+       v6address->ss_len = sizeof(struct sockaddr_storage);
+#endif
+
+       int error = 0;
+
+       error = inet_pton(AF_INET, (!bindaddr) ? ((getStrConf(BINDADDR)) ? getStrConf(BINDADDR) : "0.0.0.0")
+               : bindaddr, &(((struct sockaddr_in*)v4address)->sin_addr));
+       if (error == 0)
+               Log_fatal("Invalid IPv4 address supplied!");
+       else if (error == -1)
+               Log_warn("Could not allocate IPv4 address");
+
+       error = inet_pton(AF_INET6, (!bindaddr6) ? ((getStrConf(BINDADDR6)) ? getStrConf(BINDADDR6) : "::")
+               : bindaddr6, &(((struct sockaddr_in6*)v6address)->sin6_addr));
+       if (error == 0)
+               Log_fatal("Invalid IPv6 address supplied!");
+       else if (error == -1)
+               Log_warn("Could not allocate IPv6 address");
+
+       ((struct sockaddr_in*)v4address)->sin_port = htons((bindport) ? bindport : getIntConf(BINDPORT));
+       ((struct sockaddr_in6*)v6address)->sin6_port = htons((bindport6) ? bindport6 : getIntConf(BINDPORT6));
+
+       addresses[0] = v4address;
+       addresses[1] = v6address;
+
+       return addresses;
+}
+
+void Server_runLoop(struct pollfd* pollfds)
+{
+       int timeout, rc, clientcount;
+
+       etimer_t janitorTimer;
+       Timer_init(&janitorTimer);
 
-       /* Main server loop */
        while (!shutdown_server) {
-               struct sockaddr_in remote;
+               struct sockaddr_storage remote;
                int i;
 
-               pollfds[UDP_SOCK].revents = 0;
-               pollfds[TCP_SOCK].revents = 0;
-               clientcount = Client_getfds(&pollfds[2]);
+               for(i = 0; i < nofServerSocks; i++) {
+                       pollfds[i].revents = 0;
+               }
+
+               clientcount = Client_getfds(&pollfds[nofServerSocks]);
 
                timeout = (int)(1000000LL - (int64_t)Timer_elapsed(&janitorTimer)) / 1000LL;
                if (timeout <= 0) {
@@ -151,9 +154,9 @@ void Server_run()
                        Timer_restart(&janitorTimer);
                        timeout = (int)(1000000LL - (int64_t)Timer_elapsed(&janitorTimer)) / 1000LL;
                }
-               rc = poll(pollfds, clientcount + 2, timeout);
-               if (rc == 0) { /* Timeout */
-                       /* Do maintenance */
+               rc = poll(pollfds, clientcount + nofServerSocks, timeout);
+               if (rc == 0) {
+                       /* Poll timed out, do maintenance */
                        Timer_restart(&janitorTimer);
                        Client_janitor();
                        continue;
@@ -162,37 +165,161 @@ void Server_run()
                        if (errno == EINTR) /* signal */
                                continue;
                        else
-                               Log_fatal("poll: error %d", errno);
+                               Log_fatal("poll: error %d (%s)", errno, strerror(errno));
                }
-               if (pollfds[LISTEN_SOCK].revents) { /* New tcp connection */
-                       int tcpfd, flag = 1;
-                       uint32_t addrlen;
-                       addrlen = sizeof(struct sockaddr_in);
-                       tcpfd = accept(pollfds[LISTEN_SOCK].fd, (struct sockaddr*)&remote, &addrlen);
-                       fcntl(tcpfd, F_SETFL, O_NONBLOCK);
-                       setsockopt(tcpfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
-                       Log_debug("Connection from %s port %d\n", inet_ntoa(remote.sin_addr),
-                                         ntohs(remote.sin_port));
-                       if (Client_add(tcpfd, &remote) < 0)
-                               close(tcpfd);
+
+               /* Check for new connection */
+               for (i = 0; i < nofServerSocks / 2; i++) {
+                       if (pollfds[i].revents) {
+                               static int tcpfd;
+                               static uint32_t addrlen = sizeof(struct sockaddr_storage);
+                               tcpfd = accept(pollfds[i].fd, (struct sockaddr *)&remote, &addrlen);
+                               fcntl(tcpfd, F_SETFL, O_NONBLOCK);
+                               setsockopt(tcpfd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(int));
+                               Log_debug("Connection from %s port %d\n", Util_addressToString(&remote), Util_addressToPort(&remote));
+                               if (Client_add(tcpfd, &remote) < 0)
+                                       close(tcpfd);
+                       }
                }
 
-               if (pollfds[UDP_SOCK].revents) {
-                       Client_read_udp();
+               for (i = nofServerSocks / 2; i < nofServerSocks; i++) {
+                       if (pollfds[i].revents)
+                               Client_read_udp(udpsocks[i - nofServerSocks / 2]);
                }
+
                for (i = 0; i < clientcount; i++) {
-                       if (pollfds[i + 2].revents & POLLIN) {
-                               Client_read_fd(pollfds[i + 2].fd);
-                       }
-                       if (pollfds[i + 2].revents & POLLOUT) {
-                               Client_write_fd(pollfds[i + 2].fd);
-                       }
+                       if (pollfds[nofServerSocks + i].revents & POLLIN)
+                               Client_read_fd(pollfds[nofServerSocks + i].fd);
+
+                       if (pollfds[nofServerSocks + i].revents & POLLOUT)
+                               Client_write_fd(pollfds[nofServerSocks + i].fd);
                }
        }
+}
+
+void Server_setupTCPSockets(struct sockaddr_storage* addresses[2], struct pollfd* pollfds)
+{
+       uint8_t yes = 1;
+       int sockets[2];
+
+       if (hasv4) {
+               /* IPv4 socket setup */
+               sockets[0] = socket(PF_INET, SOCK_STREAM, 0);
+               if (sockets[0] < 0)
+                       Log_fatal("socket IPv4");
+               if (setsockopt(sockets[0], SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0)
+                       Log_fatal("setsockopt IPv4: %s", strerror(errno));
+               if (bind(sockets[0], (struct sockaddr *)addresses[0], sizeof (struct sockaddr_in)) < 0)
+                       Log_fatal("bind %s %d: %s", Util_addressToString(addresses[0]), Util_addressToPort(addresses[0]), strerror(errno));
+               if (listen(sockets[0], 3) < 0)
+                       Log_fatal("listen IPv4");
+               fcntl(sockets[0], F_SETFL, O_NONBLOCK);
+
+               pollfds[0].fd = sockets[0];
+               pollfds[0].events = POLLIN;
+       }
+
+       if (hasv6) {
+               /* IPv6 socket setup */
+               sockets[1] = socket(PF_INET6, SOCK_STREAM, 0);
+               if (sockets[1] < 0)
+                       Log_fatal("socket IPv6: %s", strerror(errno));
+               if (setsockopt(sockets[1], SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0)
+                       Log_fatal("setsockopt IPv6: %s", strerror(errno));
+               if (setsockopt(sockets[1], IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(int)) != 0)
+                       Log_fatal("setsockopt IPv6: %s", strerror(errno));
+               if (bind(sockets[1], (struct sockaddr *)addresses[1], sizeof (struct sockaddr_in6)) < 0)
+                       Log_fatal("bind %s %d: %s", Util_addressToString(addresses[1]), Util_addressToPort(addresses[1]), strerror(errno));
+               if (listen(sockets[1], 3) < 0)
+                       Log_fatal("listen IPv6");
+               fcntl(sockets[1], F_SETFL, O_NONBLOCK);
+
+
+               /* If  there is an IPv4 address, then IPv6 will use the second socket, otherwise it uses the first */
+               pollfds[(hasv4) ? 1 : 0].fd = sockets[1];
+               pollfds[(hasv4) ? 1 : 0].events = POLLIN;
+       }
+}
+
+void Server_setupUDPSockets(struct sockaddr_storage* addresses[2], struct pollfd* pollfds)
+{
+       int val = 0;
+       int sockets[2] = {-1, -1};
+
+       if((udpsocks = calloc(nofServerSocks / 2, sizeof(int))) == NULL)
+               Log_fatal("Out of memory (%s:%s)", __FILE__, __LINE__);
+
+       if (hasv4) {
+               sockets[0] = socket(PF_INET, SOCK_DGRAM, 0);
+               if (bind(sockets[0], (struct sockaddr *) addresses[0], sizeof (struct sockaddr_in)) < 0)
+                       Log_fatal("bind %s %d: %s", Util_addressToString(addresses[0]), Util_addressToPort(addresses[0]), strerror(errno));
+               val = 0xe0;
+               if (setsockopt(sockets[0], IPPROTO_IP, IP_TOS, &val, sizeof(val)) < 0)
+                       Log_warn("Server: Failed to set TOS for UDP Socket");
+               val = 0x80;
+               if (setsockopt(sockets[0], IPPROTO_IP, IP_TOS, &val, sizeof(val)) < 0)
+                       Log_warn("Server: Failed to set TOS for UDP Socket");
+
+               fcntl(sockets[0], F_SETFL, O_NONBLOCK);
+               pollfds[(hasv6) ? 2 : 1].fd = sockets[0];
+               pollfds[(hasv6) ? 2 : 1].events = POLLIN | POLLHUP | POLLERR;
+               udpsocks[0] = sockets[0];
+       }
+
+       if (hasv6) {
+               sockets[1] = socket(PF_INET6, SOCK_DGRAM, 0);
+               if (setsockopt(sockets[1], IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(int)) != 0)
+                       Log_fatal("setsockopt IPv6: %s", strerror(errno));
+               if (bind(sockets[1], (struct sockaddr *) addresses[1], sizeof (struct sockaddr_in6)) < 0)
+                       Log_fatal("bind %s %d: %s", Util_addressToString(addresses[1]), Util_addressToPort(addresses[1]), strerror(errno));
+               val = 0xe0;
+               if (setsockopt(sockets[1], IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val)) < 0)
+                       Log_warn("Server: Failed to set TOS for UDP Socket");
+               val = 0x80;
+               if (setsockopt(sockets[1], IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val)) < 0)
+                       Log_warn("Server: Failed to set TOS for UDP Socket");
+
+               fcntl(sockets[1], F_SETFL, O_NONBLOCK);
+               pollfds[(hasv4) ? 3 : 1].fd = sockets[1];
+               pollfds[(hasv4) ? 3 : 1].events = POLLIN | POLLHUP | POLLERR;
+               udpsocks[(hasv4) ? 1 : 0] = sockets[1];
+       }
+
+}
+
+void Server_run()
+{
+       struct pollfd *pollfds;
+
+       checkIPversions();
+
+       /* max clients + server sokets + client connecting that will be disconnected */
+       if ((pollfds = calloc((getIntConf(MAX_CLIENTS) + nofServerSocks + 1) , sizeof(struct pollfd))) == NULL)
+               Log_fatal("out of memory");
+
+       /* Figure out bind address and port */
+       struct sockaddr_storage** addresses = Server_setupAddressesAndPorts();
+
+       /* Prepare TCP sockets */
+       Server_setupTCPSockets(addresses, pollfds);
+
+       /* Prepare UDP sockets */
+       Server_setupUDPSockets(addresses, pollfds);
+
+       Log_info("uMurmur version %s ('%s') protocol version %d.%d.%d",
+               UMURMUR_VERSION, UMURMUR_CODENAME, PROTVER_MAJOR, PROTVER_MINOR, PROTVER_PATCH);
+       Log_info("Visit http://code.google.com/p/umurmur/");
+
+       /* Main server loop */
+       Server_runLoop(pollfds);
 
-       /* Disconnect clients */
+       /* Disconnect clients and cleanup memory */
        Client_disconnect_all();
        free(pollfds);
+       free(addresses[0]);
+       free(addresses[1]);
+       free(addresses);
+       free(udpsocks);
 }
 
 void Server_shutdown()
diff --git a/src/ssl.c b/src/ssl.c
deleted file mode 100644 (file)
index 62257d0..0000000
--- a/src/ssl.c
+++ /dev/null
@@ -1,791 +0,0 @@
-/* Copyright (C) 2009-2014, Martin Johansson <martin@fatbob.nu>
-   Copyright (C) 2005-2014, Thorvald Natvig <thorvald@natvig.com>
-
-   All rights reserved.
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions
-   are met:
-
-   - Redistributions of source code must retain the above copyright notice,
-     this list of conditions and the following disclaimer.
-   - 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.
-   - Neither the name of the Developers nor the names of its contributors may
-     be used to endorse or promote products derived from this software without
-     specific prior written permission.
-
-   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 FOUNDATION 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 <string.h>
-#include <stdlib.h>
-#include <fcntl.h>
-
-#include "conf.h"
-#include "log.h"
-#include "ssl.h"
-
-#ifdef USE_POLARSSL
-/*
- * PolarSSL interface
- */
-
-#include <polarssl/config.h>
-#include <polarssl/havege.h>
-#include <polarssl/certs.h>
-#include <polarssl/x509.h>
-#include <polarssl/ssl.h>
-#include <polarssl/net.h>
-
-#ifdef POLARSSL_API_V1_2_ABOVE
-int ciphers[] =
-{
-    TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
-    TLS_RSA_WITH_AES_256_CBC_SHA,
-    TLS_RSA_WITH_AES_128_CBC_SHA,
-    0
-};
-#else
-int ciphers[] =
-{
-    SSL_EDH_RSA_AES_256_SHA,
-    SSL_RSA_AES_256_SHA,
-    SSL_RSA_AES_128_SHA,
-    0
-};
-#endif
-
-#ifdef POLARSSL_API_V1_3_ABOVE
-static x509_crt certificate;
-static inline int x509parse_keyfile(rsa_context *rsa, const char *path,
-                                    const char *pwd)
-{
-    int ret;
-    pk_context pk;
-
-    pk_init(&pk);
-    ret = pk_parse_keyfile(&pk, path, pwd);
-    if (ret == 0 && !pk_can_do( &pk, POLARSSL_PK_RSA))
-        ret = POLARSSL_ERR_PK_TYPE_MISMATCH;
-    if (ret == 0)
-        rsa_copy(rsa, pk_rsa(pk));
-    else
-        rsa_free(rsa);
-    pk_free(&pk);
-    return ret;
-}
-#else
-static x509_cert certificate;
-#endif
-
-static rsa_context key;
-bool_t builtInTestCertificate;
-
-#ifdef USE_POLARSSL_HAVEGE
-havege_state hs;
-#else
-int urandom_fd;
-#endif
-
-/* DH prime */
-char *my_dhm_P =
-       "9CE85640903BF123906947FEDE767261" \
-       "D9B4A973EB8F7D984A8C656E2BCC161C" \
-       "183D4CA471BA78225F940F16D1D99CA3" \
-       "E66152CC68EDCE1311A390F307741835" \
-       "44FF6AB553EC7073AD0CB608F2A3B480" \
-       "19E6C02BCED40BD30E91BB2469089670" \
-       "DEF409C08E8AC24D1732A6128D2220DC53";
-char *my_dhm_G = "4";
-
-#ifdef USE_POLARSSL_TESTCERT
-static void initTestCert()
-{
-       int rc;
-       builtInTestCertificate = true;
-#ifdef POLARSSL_API_V1_3_ABOVE
-       rc = x509_crt_parse_rsa(&certificate, (unsigned char *)test_srv_crt,
-               strlen(test_srv_crt));
-#else
-       rc = x509parse_crt(&certificate, (unsigned char *)test_srv_crt,
-               strlen(test_srv_crt));
-#endif
-       if (rc != 0)
-               Log_fatal("Could not parse built-in test certificate");
-}
-
-static void initTestKey()
-{
-       int rc;
-
-       rc = x509parse_key_rsa(&key, (unsigned char *)test_srv_key,
-                              strlen(test_srv_key), NULL, 0);
-       if (rc != 0)
-               Log_fatal("Could not parse built-in test RSA key");
-}
-#endif
-
-/*
- * How to generate a self-signed cert with openssl:
- * openssl genrsa 1024 > host.key
- * openssl req -new -x509 -nodes -sha1 -days 365 -key host.key > host.cert
- */
-static void initCert()
-{
-       int rc;
-       char *crtfile = (char *)getStrConf(CERTIFICATE);
-
-       if (crtfile == NULL) {
-#ifdef USE_POLARSSL_TESTCERT
-               Log_warn("No certificate file specified. Falling back to test certificate.");
-               initTestCert();
-#else
-               Log_fatal("No certificate file specified");
-#endif
-               return;
-       }
-#ifdef POLARSSL_API_V1_3_ABOVE
-       rc = x509_crt_parse_file(&certificate, crtfile);
-#else
-       rc = x509parse_crtfile(&certificate, crtfile);
-#endif
-       if (rc != 0) {
-#ifdef USE_POLARSSL_TESTCERT
-               Log_warn("Could not read certificate file '%s'. Falling back to test certificate.", crtfile);
-               initTestCert();
-#else
-               Log_fatal("Could not read certificate file '%s'", crtfile);
-#endif
-               return;
-       }
-}
-
-static void initKey()
-{
-       int rc;
-       char *keyfile = (char *)getStrConf(KEY);
-
-       if (keyfile == NULL)
-               Log_fatal("No key file specified");
-       rc = x509parse_keyfile(&key, keyfile, NULL);
-       if (rc != 0)
-               Log_fatal("Could not read RSA key file %s", keyfile);
-}
-
-#ifndef USE_POLARSSL_HAVEGE
-int urandom_bytes(void *ctx, unsigned char *dest, size_t len)
-{
-       int cur;
-
-       while (len) {
-               cur = read(urandom_fd, dest, len);
-               if (cur < 0)
-                       continue;
-               len -= cur;
-       }
-       return 0;
-}
-#endif
-
-#define DEBUG_LEVEL 0
-static void pssl_debug(void *ctx, int level, const char *str)
-{
-    if (level <= DEBUG_LEVEL)
-               Log_info("PolarSSL [level %d]: %s", level, str);
-}
-
-void SSLi_init(void)
-{
-       char verstring[12];
-
-       initCert();
-#ifdef USE_POLARSSL_TESTCERT
-       if (builtInTestCertificate) {
-               Log_warn("*** Using built-in test certificate and RSA key ***");
-               Log_warn("*** This is not secure! Please use a CA-signed certificate or create a key and self-signed certificate ***");
-               initTestKey();
-       }
-       else
-               initKey();
-#else
-       initKey();
-#endif
-
-       /* Initialize random number generator */
-#ifdef USE_POLARSSL_HAVEGE
-    havege_init(&hs);
-#else
-    urandom_fd = open("/dev/urandom", O_RDONLY);
-    if (urandom_fd < 0)
-           Log_fatal("Cannot open /dev/urandom");
-#endif
-
-    version_get_string(verstring);
-    Log_info("PolarSSL library version %s initialized", verstring);
-}
-
-void SSLi_deinit(void)
-{
-#ifdef POLARSSL_API_V1_3_ABOVE
-       x509_crt_free(&certificate);
-#else
-       x509_free(&certificate);
-#endif
-       rsa_free(&key);
-}
-
-/* Create SHA1 of last certificate in the peer's chain. */
-bool_t SSLi_getSHA1Hash(SSL_handle_t *ssl, uint8_t *hash)
-{
-#ifdef POLARSSL_API_V1_3_ABOVE
-       x509_crt const *cert;
-#else
-       x509_cert const *cert;
-#endif
-#ifdef POLARSSL_API_V1_2_ABOVE
-       cert = ssl_get_peer_cert(ssl);
-#else
-       cert = ssl->peer_cert;
-#endif
-       if (!cert) {
-               return false;
-       }
-       sha1(cert->raw.p, cert->raw.len, hash);
-       return true;
-}
-
-SSL_handle_t *SSLi_newconnection(int *fd, bool_t *SSLready)
-{
-       ssl_context *ssl;
-       ssl_session *ssn;
-       int rc;
-
-       ssl = malloc(sizeof(ssl_context));
-       ssn = malloc(sizeof(ssl_session));
-       if (!ssl || !ssn)
-               Log_fatal("Out of memory");
-       memset(ssl, 0, sizeof(ssl_context));
-       memset(ssn, 0, sizeof(ssl_session));
-
-       rc = ssl_init(ssl);
-       if (rc != 0 )
-               Log_fatal("Failed to initialize: %d", rc);
-
-       ssl_set_endpoint(ssl, SSL_IS_SERVER);
-       ssl_set_authmode(ssl, SSL_VERIFY_OPTIONAL);
-
-#ifdef USE_POLARSSL_HAVEGE
-       ssl_set_rng(ssl, HAVEGE_RAND, &hs);
-#else
-       ssl_set_rng(ssl, urandom_bytes, NULL);
-#endif
-
-       ssl_set_dbg(ssl, pssl_debug, NULL);
-       ssl_set_bio(ssl, net_recv, fd, net_send, fd);
-
-       ssl_set_ciphersuites(ssl, ciphers);
-
-#ifdef POLARSSL_API_V1_2_ABOVE
-    ssl_set_session(ssl, ssn);
-#else
-    ssl_set_session(ssl, 0, 0, ssn);
-#endif
-
-    ssl_set_ca_chain(ssl, &certificate, NULL, NULL);
-#ifdef POLARSSL_API_V1_3_ABOVE
-       ssl_set_own_cert_rsa(ssl, &certificate, &key);
-#else
-       ssl_set_own_cert(ssl, &certificate, &key);
-#endif
-       ssl_set_dh_param(ssl, my_dhm_P, my_dhm_G);
-
-       return ssl;
-}
-
-int SSLi_nonblockaccept(SSL_handle_t *ssl, bool_t *SSLready)
-{
-       int rc;
-
-       rc = ssl_handshake(ssl);
-       if (rc != 0) {
-               if (rc == POLARSSL_ERR_NET_WANT_READ || rc == POLARSSL_ERR_NET_WANT_WRITE) {
-                       return 0;
-               } else if (rc == POLARSSL_ERR_X509_CERT_VERIFY_FAILED) { /* Allow this (selfsigned etc) */
-                       return 0;
-               } else {
-                       Log_warn("SSL handshake failed: %d", rc);
-                       return -1;
-               }
-       }
-       *SSLready = true;
-       return 0;
-}
-
-int SSLi_read(SSL_handle_t *ssl, uint8_t *buf, int len)
-{
-       int rc;
-
-       rc = ssl_read(ssl, buf, len);
-       if (rc == POLARSSL_ERR_NET_WANT_READ)
-               return SSLI_ERROR_WANT_READ;
-       return rc;
-}
-
-int SSLi_write(SSL_handle_t *ssl, uint8_t *buf, int len)
-{
-       int rc;
-
-       rc = ssl_write(ssl, buf, len);
-       if (rc == POLARSSL_ERR_NET_WANT_WRITE)
-               return SSLI_ERROR_WANT_WRITE;
-       return rc;
-}
-
-int SSLi_get_error(SSL_handle_t *ssl, int code)
-{
-       return code;
-}
-
-bool_t SSLi_data_pending(SSL_handle_t *ssl)
-{
-       return ssl_get_bytes_avail(ssl) > 0;
-}
-
-void SSLi_shutdown(SSL_handle_t *ssl)
-{
-       ssl_close_notify(ssl);
-}
-
-void SSLi_free(SSL_handle_t *ssl)
-{
-       Log_debug("SSLi_free");
-#if (POLARSSL_VERSION_MINOR <= 2 && POLARSSL_VERSION_PATCH < 6)
-       free(ssl->session); /* Workaround for memory leak in PolarSSL < 1.2.6 */
-       ssl->session = NULL;
-#endif
-       ssl_free(ssl);
-       free(ssl);
-}
-
-#else
-/*
- * OpenSSL interface
- */
-
-#include <openssl/x509v3.h>
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#include <openssl/safestack.h>
-static X509 *x509;
-static RSA *rsa;
-static SSL_CTX *context;
-static EVP_PKEY *pkey;
-
-static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx);
-
-static int SSL_add_ext(X509 * crt, int nid, char *value) {
-       X509_EXTENSION *ex;
-       X509V3_CTX ctx;
-       X509V3_set_ctx_nodb(&ctx);
-       X509V3_set_ctx(&ctx, crt, crt, NULL, NULL, 0);
-       ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
-       if (!ex)
-               return 0;
-
-       X509_add_ext(crt, ex, -1);
-       X509_EXTENSION_free(ex);
-       return 1;
-}
-
-static X509 *SSL_readcert(char *certfile)
-{
-       FILE *fp;
-       X509 *x509;
-
-       /* open the certificate file */
-       fp = fopen(certfile, "r");
-       if (fp == NULL) {
-               Log_warn("Unable to open the X509 file %s for reading.", certfile);
-               return NULL;
-       }
-
-       /* allocate memory for the cert structure */
-       x509 = X509_new();
-
-       if (PEM_read_X509(fp, &x509, NULL, NULL) == 0) {
-               /* error reading the x509 information - check the error stack */
-               Log_warn("Error trying to read X509 info.");
-               fclose(fp);
-               X509_free(x509);
-               return NULL;
-       }
-       fclose(fp);
-       return x509;
-}
-
-static RSA *SSL_readprivatekey(char *keyfile)
-{
-       FILE *fp;
-       RSA *rsa;
-
-/* open the private key file for reading */
-       fp = fopen(keyfile, "r");
-       if (fp == NULL) {
-               Log_warn("Unable to open the private key file %s for reading.", keyfile);
-               return NULL;
-       }
-
-/* allocate memory for the RSA structure */
-       rsa = RSA_new();
-
-       /* assign a callback function for the password */
-
-       /* read a private key from file */
-       if (PEM_read_RSAPrivateKey(fp, &rsa, NULL, NULL) <= 0) {
-               /* error reading the key - check the error stack */
-               Log_warn("Error trying to read private key.");
-               RSA_free(rsa);
-               fclose(fp);
-               return NULL;
-       }
-       fclose(fp);
-       return rsa;
-}
-
-static void SSL_writecert(char *certfile, X509 *x509)
-{
-       FILE *fp;
-
-       /* open the private key file */
-       fp = fopen(certfile, "w");
-       if (fp == NULL) {
-               Log_warn("Unable to open the X509 file %s for writing", certfile);
-               return;
-       }
-       if (PEM_write_X509(fp, x509) == 0) {
-               Log_warn("Error trying to write X509 info.");
-       }
-       fclose(fp);
-}
-
-static void SSL_writekey(char *keyfile, RSA *rsa)
-{
-       FILE *fp;
-
-       /* open the private key file for reading */
-       fp = fopen(keyfile, "w");
-       if (fp == NULL) {
-               Log_warn("Unable to open the private key file %s for writing.", keyfile);
-               return;
-       }
-
-       if (PEM_write_RSAPrivateKey(fp, rsa, NULL, NULL, 0, NULL, NULL) == 0) {
-               Log_warn("Error trying to write private key");
-       }
-       fclose(fp);
-}
-
-static void SSL_initializeCert() {
-
-       char *crt, *key, *pass;
-
-       crt = (char *)getStrConf(CERTIFICATE);
-       key = (char *)getStrConf(KEY);
-       pass = (char *)getStrConf(PASSPHRASE);
-
-       x509 = SSL_readcert(crt);
-       rsa = SSL_readprivatekey(key);
-       if (rsa != NULL) {
-               pkey = EVP_PKEY_new();
-               EVP_PKEY_assign_RSA(pkey, rsa);
-       }
-
-
-#if 0
-       /* Later ... */
-       if (key && !x509) {
-               qscCert = QSslCertificate(key);
-               if (! qscCert.isNull()) {
-                       logthis("Using certificate from key.");
-               }
-       }
-
-       if (! qscCert.isNull()) {
-               QSsl::KeyAlgorithm alg = qscCert.publicKey().algorithm();
-               /* Fetch algorith from cert */
-               if (! key.isEmpty()) {
-                       /* get key */
-                       qskKey = QSslKey(key, alg, QSsl::Pem, QSsl::PrivateKey, pass);
-                       if (qskKey.isNull()) {
-                               logthis("Failed to parse key.");
-                       }
-               }
-
-               if (! crt.isEmpty() && qskKey.isNull()) {
-                       /* get key from certificate */
-                       qskKey = QSslKey(crt, alg, QSsl::Pem, QSsl::PrivateKey, pass);
-                       if (! qskKey.isNull()) {
-                               logthis("Using key from certificate.");
-                       }
-               }
-
-       }
-#endif
-
-       if (!rsa || !x509) {
-               Log_info("Generating new server certificate.");
-
-
-               CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
-
-               x509 = X509_new();
-               pkey = EVP_PKEY_new();
-               rsa = RSA_generate_key(1024,RSA_F4,NULL,NULL);
-               EVP_PKEY_assign_RSA(pkey, rsa);
-
-               X509_set_version(x509, 2);
-               ASN1_INTEGER_set(X509_get_serialNumber(x509),1);
-               X509_gmtime_adj(X509_get_notBefore(x509),0);
-               X509_gmtime_adj(X509_get_notAfter(x509),60*60*24*365);
-               X509_set_pubkey(x509, pkey);
-
-               X509_NAME *name=X509_get_subject_name(x509);
-
-               X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (const uint8_t *)"Murmur Autogenerated Certificate v2", -1, -1, 0);
-               X509_set_issuer_name(x509, name);
-               SSL_add_ext(x509, NID_basic_constraints, "critical,CA:FALSE");
-               SSL_add_ext(x509, NID_ext_key_usage, "serverAuth,clientAuth");
-               SSL_add_ext(x509, NID_subject_key_identifier, "hash");
-               SSL_add_ext(x509, NID_netscape_comment, "Generated from umurmur");
-
-               X509_sign(x509, pkey, EVP_md5());
-
-               SSL_writecert(crt, x509);
-               SSL_writekey(key, rsa);
-       }
-
-}
-
-void SSLi_init(void)
-{
-       const SSL_METHOD *method;
-       SSL *ssl;
-       int i, offset = 0, cipherstringlen = 0;
-       STACK_OF(SSL_CIPHER) *cipherlist = NULL, *cipherlist_new = NULL;
-       SSL_CIPHER *cipher;
-       char *cipherstring, tempstring[128];
-
-       SSL_library_init();
-       OpenSSL_add_all_algorithms();           /* load & register all cryptos, etc. */
-       SSL_load_error_strings();                       /* load all error messages */
-       ERR_load_crypto_strings();                      /* load all error messages */
-       method = SSLv23_server_method();                /* create new server-method instance */
-       context = SSL_CTX_new(method);                  /* create new context from method */
-       if (context == NULL)
-       {
-               ERR_print_errors_fp(stderr);
-               abort();
-       }
-
-       char* sslCAPath = getStrConf(CAPATH);
-       if(sslCAPath != NULL)
-       {
-               SSL_CTX_load_verify_locations(context, NULL, sslCAPath);
-       }
-
-       SSL_initializeCert();
-       if (SSL_CTX_use_certificate(context, x509) <= 0)
-               Log_fatal("Failed to initialize cert");
-       if (SSL_CTX_use_PrivateKey(context, pkey) <= 0) {
-               ERR_print_errors_fp(stderr);
-               Log_fatal("Failed to initialize private key");
-       }
-
-       /* Set cipher list */
-       ssl = SSL_new(context);
-       cipherlist = (STACK_OF(SSL_CIPHER) *) SSL_get_ciphers(ssl);
-       cipherlist_new = (STACK_OF(SSL_CIPHER) *) sk_SSL_CIPHER_new_null();
-
-       for ( i = 0; (cipher = sk_SSL_CIPHER_value(cipherlist, i)) != NULL; i++) {
-               if (SSL_CIPHER_get_bits(cipher, NULL) >= 128) {
-                       sk_SSL_CIPHER_push(cipherlist_new, cipher);
-               }
-       }
-       Log_debug("List of ciphers:");
-       if (cipherlist_new) {
-               for (i = 0; (cipher = sk_SSL_CIPHER_value(cipherlist_new, i)) != NULL; i++) {
-                       Log_debug("%s", SSL_CIPHER_get_name(cipher));
-                       cipherstringlen += strlen(SSL_CIPHER_get_name(cipher)) + 1;
-               }
-               cipherstring = malloc(cipherstringlen + 1);
-               if (cipherstring == NULL)
-                       Log_fatal("Out of memory");
-               for (i = 0; (cipher = sk_SSL_CIPHER_value(cipherlist_new, i)) != NULL; i++) {
-                       offset += sprintf(cipherstring + offset, "%s:", SSL_CIPHER_get_name(cipher));
-               }
-       }
-
-       if (cipherlist_new)
-               sk_SSL_CIPHER_free(cipherlist_new);
-
-       if (strlen(cipherstring) == 0)
-               Log_fatal("No suitable ciphers found!");
-
-       if (SSL_CTX_set_cipher_list(context, cipherstring) == 0)
-               Log_fatal("Failed to set cipher list!");
-
-       free(cipherstring);
-
-       SSL_CTX_set_verify(context, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
-                          verify_callback);
-
-       SSL_free(ssl);
-       Log_info("OpenSSL library initialized");
-
-}
-
-void SSLi_deinit(void)
-{
-       SSL_CTX_free(context);
-       EVP_cleanup();
-}
-
-int SSLi_nonblockaccept(SSL_handle_t *ssl, bool_t *SSLready)
-{
-       int rc;
-       rc = SSL_accept(ssl);
-       if (rc < 0) {
-               if (SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ ||
-                       SSL_get_error(ssl, rc) == SSL_ERROR_WANT_WRITE) {
-                       Log_debug("SSL not ready");
-                       return 0;
-               } else {
-                       Log_warn("SSL error: %s", ERR_error_string(SSL_get_error(ssl, rc), NULL));
-                       return -1;
-               }
-       }
-       *SSLready = true;
-       return 0;
-}
-
-SSL_handle_t *SSLi_newconnection(int *fd, bool_t *SSLready)
-{
-       SSL *ssl;
-
-       *SSLready = false;
-       ssl = SSL_new(context);
-       SSL_set_fd(ssl, *fd);
-       if (SSLi_nonblockaccept(ssl, SSLready) < 0) {
-               SSL_free(ssl);
-               return NULL;
-       }
-       return ssl;
-}
-
-/* Create SHA1 of last certificate in the peer's chain. */
-bool_t SSLi_getSHA1Hash(SSL_handle_t *ssl, uint8_t *hash)
-{
-       X509 *x509;
-       uint8_t *buf, *p;
-       int len;
-
-       x509 = SSL_get_peer_certificate(ssl);
-       if (!x509) {
-               return false;
-       }
-
-       len = i2d_X509(x509, NULL);
-       buf = malloc(len);
-       if (buf == NULL) {
-               Log_fatal("malloc");
-       }
-
-       p = buf;
-       i2d_X509(x509, &p);
-
-       SHA1(buf, len, hash);
-       free(buf);
-       return true;
-}
-
-void SSLi_closeconnection(SSL_handle_t *ssl)
-{
-       SSL_free(ssl);
-}
-
-void SSLi_shutdown(SSL_handle_t *ssl)
-{
-       SSL_shutdown(ssl);
-}
-
-int SSLi_read(SSL_handle_t *ssl, uint8_t *buf, int len)
-{
-       return SSL_read(ssl, buf, len);
-}
-
-int SSLi_write(SSL_handle_t *ssl, uint8_t *buf, int len)
-{
-       return SSL_write(ssl, buf, len);
-}
-
-int SSLi_get_error(SSL_handle_t *ssl, int code)
-{
-       return SSL_get_error(ssl, code);
-}
-
-bool_t SSLi_data_pending(SSL_handle_t *ssl)
-{
-       return SSL_pending(ssl);
-}
-
-void SSLi_free(SSL_handle_t *ssl)
-{
-       SSL_free(ssl);
-}
-
-static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
-{
-       char    buf[256];
-       X509   *err_cert;
-       int     err, depth;
-       SSL    *ssl;
-
-    err_cert = X509_STORE_CTX_get_current_cert(ctx);
-    err = X509_STORE_CTX_get_error(ctx);
-    depth = X509_STORE_CTX_get_error_depth(ctx);
-
-    ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
-    X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
-
-    if (depth > 5) {
-        preverify_ok = 0;
-        err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
-        X509_STORE_CTX_set_error(ctx, err);
-    }
-    if (!preverify_ok) {
-           Log_warn("SSL: verify error:num=%d:%s:depth=%d:%s\n", err,
-                    X509_verify_cert_error_string(err), depth, buf);
-    }
-    /*
-     * At this point, err contains the last verification error. We can use
-     * it for something special
-     */
-    if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT)) {
-           X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
-           Log_warn("issuer= %s", buf);
-    }
-    return 1;
-}
-
-#endif
diff --git a/src/ssli_gnutls.c b/src/ssli_gnutls.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/ssli_openssl.c b/src/ssli_openssl.c
new file mode 100644 (file)
index 0000000..470332c
--- /dev/null
@@ -0,0 +1,446 @@
+/* Copyright (C) 2009-2014, Martin Johansson <martin@fatbob.nu>
+   Copyright (C) 2005-2014, Thorvald Natvig <thorvald@natvig.com>
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+   - 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.
+   - Neither the name of the Developers nor the names of its contributors may
+     be used to endorse or promote products derived from this software without
+     specific prior written permission.
+
+   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 FOUNDATION 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 <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "conf.h"
+#include "log.h"
+#include "ssl.h"
+
+/*
+ * OpenSSL interface
+ */
+
+#include <openssl/x509v3.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/safestack.h>
+static X509 *x509;
+static RSA *rsa;
+static SSL_CTX *context;
+static EVP_PKEY *pkey;
+
+static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx);
+
+static int SSL_add_ext(X509 * crt, int nid, char *value) {
+       X509_EXTENSION *ex;
+       X509V3_CTX ctx;
+       X509V3_set_ctx_nodb(&ctx);
+       X509V3_set_ctx(&ctx, crt, crt, NULL, NULL, 0);
+       ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
+       if (!ex)
+               return 0;
+
+       X509_add_ext(crt, ex, -1);
+       X509_EXTENSION_free(ex);
+       return 1;
+}
+
+static X509 *SSL_readcert(char *certfile)
+{
+       FILE *fp;
+       X509 *x509;
+
+       /* open the certificate file */
+       fp = fopen(certfile, "r");
+       if (fp == NULL) {
+               Log_warn("Unable to open the X509 file %s for reading.", certfile);
+               return NULL;
+       }
+
+       /* allocate memory for the cert structure */
+       x509 = X509_new();
+
+       if (PEM_read_X509(fp, &x509, NULL, NULL) == 0) {
+               /* error reading the x509 information - check the error stack */
+               Log_warn("Error trying to read X509 info.");
+               fclose(fp);
+               X509_free(x509);
+               return NULL;
+       }
+       fclose(fp);
+       return x509;
+}
+
+static RSA *SSL_readprivatekey(char *keyfile)
+{
+       FILE *fp;
+       RSA *rsa;
+
+/* open the private key file for reading */
+       fp = fopen(keyfile, "r");
+       if (fp == NULL) {
+               Log_warn("Unable to open the private key file %s for reading.", keyfile);
+               return NULL;
+       }
+
+/* allocate memory for the RSA structure */
+       rsa = RSA_new();
+
+       /* assign a callback function for the password */
+
+       /* read a private key from file */
+       if (PEM_read_RSAPrivateKey(fp, &rsa, NULL, NULL) <= 0) {
+               /* error reading the key - check the error stack */
+               Log_warn("Error trying to read private key.");
+               RSA_free(rsa);
+               fclose(fp);
+               return NULL;
+       }
+       fclose(fp);
+       return rsa;
+}
+
+static void SSL_writecert(char *certfile, X509 *x509)
+{
+       FILE *fp;
+
+       /* open the private key file */
+       fp = fopen(certfile, "w");
+       if (fp == NULL) {
+               Log_warn("Unable to open the X509 file %s for writing", certfile);
+               return;
+       }
+       if (PEM_write_X509(fp, x509) == 0) {
+               Log_warn("Error trying to write X509 info.");
+       }
+       fclose(fp);
+}
+
+static void SSL_writekey(char *keyfile, RSA *rsa)
+{
+       FILE *fp;
+
+       /* open the private key file for reading */
+       fp = fopen(keyfile, "w");
+       if (fp == NULL) {
+               Log_warn("Unable to open the private key file %s for writing.", keyfile);
+               return;
+       }
+
+       if (PEM_write_RSAPrivateKey(fp, rsa, NULL, NULL, 0, NULL, NULL) == 0) {
+               Log_warn("Error trying to write private key");
+       }
+       fclose(fp);
+}
+
+static void SSL_initializeCert() {
+
+       char *crt, *key, *pass;
+
+       crt = (char *)getStrConf(CERTIFICATE);
+       key = (char *)getStrConf(KEY);
+       pass = (char *)getStrConf(PASSPHRASE);
+
+       x509 = SSL_readcert(crt);
+       rsa = SSL_readprivatekey(key);
+       if (rsa != NULL) {
+               pkey = EVP_PKEY_new();
+               EVP_PKEY_assign_RSA(pkey, rsa);
+       }
+
+
+#if 0
+       /* Later ... */
+       if (key && !x509) {
+               qscCert = QSslCertificate(key);
+               if (! qscCert.isNull()) {
+                       logthis("Using certificate from key.");
+               }
+       }
+
+       if (! qscCert.isNull()) {
+               QSsl::KeyAlgorithm alg = qscCert.publicKey().algorithm();
+               /* Fetch algorith from cert */
+               if (! key.isEmpty()) {
+                       /* get key */
+                       qskKey = QSslKey(key, alg, QSsl::Pem, QSsl::PrivateKey, pass);
+                       if (qskKey.isNull()) {
+                               logthis("Failed to parse key.");
+                       }
+               }
+
+               if (! crt.isEmpty() && qskKey.isNull()) {
+                       /* get key from certificate */
+                       qskKey = QSslKey(crt, alg, QSsl::Pem, QSsl::PrivateKey, pass);
+                       if (! qskKey.isNull()) {
+                               logthis("Using key from certificate.");
+                       }
+               }
+
+       }
+#endif
+
+       if (!rsa || !x509) {
+               Log_info("Generating new server certificate.");
+
+
+               CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
+
+               x509 = X509_new();
+               pkey = EVP_PKEY_new();
+               rsa = RSA_generate_key(1024,RSA_F4,NULL,NULL);
+               EVP_PKEY_assign_RSA(pkey, rsa);
+
+               X509_set_version(x509, 2);
+               ASN1_INTEGER_set(X509_get_serialNumber(x509),1);
+               X509_gmtime_adj(X509_get_notBefore(x509),0);
+               X509_gmtime_adj(X509_get_notAfter(x509),60*60*24*365);
+               X509_set_pubkey(x509, pkey);
+
+               X509_NAME *name=X509_get_subject_name(x509);
+
+               X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (const uint8_t *)"Murmur Autogenerated Certificate v2", -1, -1, 0);
+               X509_set_issuer_name(x509, name);
+               SSL_add_ext(x509, NID_basic_constraints, "critical,CA:FALSE");
+               SSL_add_ext(x509, NID_ext_key_usage, "serverAuth,clientAuth");
+               SSL_add_ext(x509, NID_subject_key_identifier, "hash");
+               SSL_add_ext(x509, NID_netscape_comment, "Generated from umurmur");
+
+               X509_sign(x509, pkey, EVP_md5());
+
+               SSL_writecert(crt, x509);
+               SSL_writekey(key, rsa);
+       }
+
+}
+
+void SSLi_init(void)
+{
+       const SSL_METHOD *method;
+       SSL *ssl;
+       int i, offset = 0, cipherstringlen = 0;
+       STACK_OF(SSL_CIPHER) *cipherlist = NULL, *cipherlist_new = NULL;
+       SSL_CIPHER *cipher;
+       char *cipherstring, tempstring[128];
+
+       SSL_library_init();
+       OpenSSL_add_all_algorithms();           /* load & register all cryptos, etc. */
+       SSL_load_error_strings();                       /* load all error messages */
+       ERR_load_crypto_strings();                      /* load all error messages */
+       method = SSLv23_server_method();                /* create new server-method instance */
+       context = SSL_CTX_new(method);                  /* create new context from method */
+       if (context == NULL)
+       {
+               ERR_print_errors_fp(stderr);
+               abort();
+       }
+
+       char* sslCAPath = getStrConf(CAPATH);
+       if(sslCAPath != NULL)
+       {
+               SSL_CTX_load_verify_locations(context, NULL, sslCAPath);
+       }
+
+       SSL_initializeCert();
+       if (SSL_CTX_use_certificate(context, x509) <= 0)
+               Log_fatal("Failed to initialize cert");
+       if (SSL_CTX_use_PrivateKey(context, pkey) <= 0) {
+               ERR_print_errors_fp(stderr);
+               Log_fatal("Failed to initialize private key");
+       }
+
+       /* Set cipher list */
+       ssl = SSL_new(context);
+       cipherlist = (STACK_OF(SSL_CIPHER) *) SSL_get_ciphers(ssl);
+       cipherlist_new = (STACK_OF(SSL_CIPHER) *) sk_SSL_CIPHER_new_null();
+
+       for ( i = 0; (cipher = sk_SSL_CIPHER_value(cipherlist, i)) != NULL; i++) {
+               if (SSL_CIPHER_get_bits(cipher, NULL) >= 128) {
+                       sk_SSL_CIPHER_push(cipherlist_new, cipher);
+               }
+       }
+       Log_debug("List of ciphers:");
+       if (cipherlist_new) {
+               for (i = 0; (cipher = sk_SSL_CIPHER_value(cipherlist_new, i)) != NULL; i++) {
+                       Log_debug("%s", SSL_CIPHER_get_name(cipher));
+                       cipherstringlen += strlen(SSL_CIPHER_get_name(cipher)) + 1;
+               }
+               cipherstring = malloc(cipherstringlen + 1);
+               if (cipherstring == NULL)
+                       Log_fatal("Out of memory");
+               for (i = 0; (cipher = sk_SSL_CIPHER_value(cipherlist_new, i)) != NULL; i++) {
+                       offset += sprintf(cipherstring + offset, "%s:", SSL_CIPHER_get_name(cipher));
+               }
+       }
+
+       if (cipherlist_new)
+               sk_SSL_CIPHER_free(cipherlist_new);
+
+       if (strlen(cipherstring) == 0)
+               Log_fatal("No suitable ciphers found!");
+
+       if (SSL_CTX_set_cipher_list(context, cipherstring) == 0)
+               Log_fatal("Failed to set cipher list!");
+
+       free(cipherstring);
+
+       SSL_CTX_set_verify(context, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
+                          verify_callback);
+
+       SSL_free(ssl);
+       Log_info("OpenSSL library initialized");
+
+}
+
+void SSLi_deinit(void)
+{
+       SSL_CTX_free(context);
+       EVP_cleanup();
+}
+
+int SSLi_nonblockaccept(SSL_handle_t *ssl, bool_t *SSLready)
+{
+       int rc;
+       rc = SSL_accept(ssl);
+       if (rc < 0) {
+               if (SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ ||
+                       SSL_get_error(ssl, rc) == SSL_ERROR_WANT_WRITE) {
+                       Log_debug("SSL not ready");
+                       return 0;
+               } else {
+                       Log_warn("SSL error: %s", ERR_error_string(SSL_get_error(ssl, rc), NULL));
+                       return -1;
+               }
+       }
+       *SSLready = true;
+       return 0;
+}
+
+SSL_handle_t *SSLi_newconnection(int *fd, bool_t *SSLready)
+{
+       SSL *ssl;
+
+       *SSLready = false;
+       ssl = SSL_new(context);
+       SSL_set_fd(ssl, *fd);
+       if (SSLi_nonblockaccept(ssl, SSLready) < 0) {
+               SSL_free(ssl);
+               return NULL;
+       }
+       return ssl;
+}
+
+/* Create SHA1 of last certificate in the peer's chain. */
+bool_t SSLi_getSHA1Hash(SSL_handle_t *ssl, uint8_t *hash)
+{
+       X509 *x509;
+       uint8_t *buf, *p;
+       int len;
+
+       x509 = SSL_get_peer_certificate(ssl);
+       if (!x509) {
+               return false;
+       }
+
+       len = i2d_X509(x509, NULL);
+       buf = malloc(len);
+       if (buf == NULL) {
+               Log_fatal("malloc");
+       }
+
+       p = buf;
+       i2d_X509(x509, &p);
+
+       SHA1(buf, len, hash);
+       free(buf);
+       return true;
+}
+
+void SSLi_closeconnection(SSL_handle_t *ssl)
+{
+       SSL_free(ssl);
+}
+
+void SSLi_shutdown(SSL_handle_t *ssl)
+{
+       SSL_shutdown(ssl);
+}
+
+int SSLi_read(SSL_handle_t *ssl, uint8_t *buf, int len)
+{
+       return SSL_read(ssl, buf, len);
+}
+
+int SSLi_write(SSL_handle_t *ssl, uint8_t *buf, int len)
+{
+       return SSL_write(ssl, buf, len);
+}
+
+int SSLi_get_error(SSL_handle_t *ssl, int code)
+{
+       return SSL_get_error(ssl, code);
+}
+
+bool_t SSLi_data_pending(SSL_handle_t *ssl)
+{
+       return SSL_pending(ssl);
+}
+
+void SSLi_free(SSL_handle_t *ssl)
+{
+       SSL_free(ssl);
+}
+
+static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
+{
+       char    buf[256];
+       X509   *err_cert;
+       int     err, depth;
+       SSL    *ssl;
+
+    err_cert = X509_STORE_CTX_get_current_cert(ctx);
+    err = X509_STORE_CTX_get_error(ctx);
+    depth = X509_STORE_CTX_get_error_depth(ctx);
+
+    ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+    X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
+
+    if (depth > 5) {
+        preverify_ok = 0;
+        err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+        X509_STORE_CTX_set_error(ctx, err);
+    }
+    if (!preverify_ok) {
+           Log_warn("SSL: verify error:num=%d:%s:depth=%d:%s\n", err,
+                    X509_verify_cert_error_string(err), depth, buf);
+    }
+    /*
+     * At this point, err contains the last verification error. We can use
+     * it for something special
+     */
+    if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT)) {
+           X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
+           Log_warn("issuer= %s", buf);
+    }
+    return 1;
+}
diff --git a/src/ssli_polarssl.c b/src/ssli_polarssl.c
new file mode 100644 (file)
index 0000000..b553f79
--- /dev/null
@@ -0,0 +1,379 @@
+/* Copyright (C) 2009-2014, Martin Johansson <martin@fatbob.nu>
+   Copyright (C) 2005-2014, Thorvald Natvig <thorvald@natvig.com>
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+   - 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.
+   - Neither the name of the Developers nor the names of its contributors may
+     be used to endorse or promote products derived from this software without
+     specific prior written permission.
+
+   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 FOUNDATION 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 <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "conf.h"
+#include "log.h"
+#include "ssl.h"
+
+/*
+ * PolarSSL interface
+ */
+
+#include <polarssl/config.h>
+#include <polarssl/havege.h>
+#include <polarssl/certs.h>
+#include <polarssl/x509.h>
+#include <polarssl/ssl.h>
+#include <polarssl/net.h>
+
+#ifdef POLARSSL_API_V1_2_ABOVE
+int ciphers[] =
+{
+    TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+    TLS_RSA_WITH_AES_256_CBC_SHA,
+    TLS_RSA_WITH_AES_128_CBC_SHA,
+    0
+};
+#else
+int ciphers[] =
+{
+    SSL_EDH_RSA_AES_256_SHA,
+    SSL_RSA_AES_256_SHA,
+    SSL_RSA_AES_128_SHA,
+    0
+};
+#endif
+
+#ifdef POLARSSL_API_V1_3_ABOVE
+static x509_crt certificate;
+static inline int x509parse_keyfile(rsa_context *rsa, const char *path,
+                                    const char *pwd)
+{
+    int ret;
+    pk_context pk;
+
+    pk_init(&pk);
+    ret = pk_parse_keyfile(&pk, path, pwd);
+    if (ret == 0 && !pk_can_do( &pk, POLARSSL_PK_RSA))
+        ret = POLARSSL_ERR_PK_TYPE_MISMATCH;
+    if (ret == 0)
+        rsa_copy(rsa, pk_rsa(pk));
+    else
+        rsa_free(rsa);
+    pk_free(&pk);
+    return ret;
+}
+#else
+static x509_cert certificate;
+#endif
+
+static rsa_context key;
+bool_t builtInTestCertificate;
+
+#ifdef USE_POLARSSL_HAVEGE
+havege_state hs;
+#else
+int urandom_fd;
+#endif
+
+/* DH prime */
+char *my_dhm_P =
+       "9CE85640903BF123906947FEDE767261" \
+       "D9B4A973EB8F7D984A8C656E2BCC161C" \
+       "183D4CA471BA78225F940F16D1D99CA3" \
+       "E66152CC68EDCE1311A390F307741835" \
+       "44FF6AB553EC7073AD0CB608F2A3B480" \
+       "19E6C02BCED40BD30E91BB2469089670" \
+       "DEF409C08E8AC24D1732A6128D2220DC53";
+char *my_dhm_G = "4";
+
+#ifdef USE_POLARSSL_TESTCERT
+static void initTestCert()
+{
+       int rc;
+       builtInTestCertificate = true;
+#ifdef POLARSSL_API_V1_3_ABOVE
+       rc = x509_crt_parse_rsa(&certificate, (unsigned char *)test_srv_crt,
+               strlen(test_srv_crt));
+#else
+       rc = x509parse_crt(&certificate, (unsigned char *)test_srv_crt,
+               strlen(test_srv_crt));
+#endif
+       if (rc != 0)
+               Log_fatal("Could not parse built-in test certificate");
+}
+
+static void initTestKey()
+{
+       int rc;
+
+       rc = x509parse_key_rsa(&key, (unsigned char *)test_srv_key,
+                              strlen(test_srv_key), NULL, 0);
+       if (rc != 0)
+               Log_fatal("Could not parse built-in test RSA key");
+}
+#endif
+
+/*
+ * How to generate a self-signed cert with openssl:
+ * openssl genrsa 1024 > host.key
+ * openssl req -new -x509 -nodes -sha1 -days 365 -key host.key > host.cert
+ */
+static void initCert()
+{
+       int rc;
+       char *crtfile = (char *)getStrConf(CERTIFICATE);
+
+       if (crtfile == NULL) {
+#ifdef USE_POLARSSL_TESTCERT
+               Log_warn("No certificate file specified. Falling back to test certificate.");
+               initTestCert();
+#else
+               Log_fatal("No certificate file specified");
+#endif
+               return;
+       }
+#ifdef POLARSSL_API_V1_3_ABOVE
+       rc = x509_crt_parse_file(&certificate, crtfile);
+#else
+       rc = x509parse_crtfile(&certificate, crtfile);
+#endif
+       if (rc != 0) {
+#ifdef USE_POLARSSL_TESTCERT
+               Log_warn("Could not read certificate file '%s'. Falling back to test certificate.", crtfile);
+               initTestCert();
+#else
+               Log_fatal("Could not read certificate file '%s'", crtfile);
+#endif
+               return;
+       }
+}
+
+static void initKey()
+{
+       int rc;
+       char *keyfile = (char *)getStrConf(KEY);
+
+       if (keyfile == NULL)
+               Log_fatal("No key file specified");
+       rc = x509parse_keyfile(&key, keyfile, NULL);
+       if (rc != 0)
+               Log_fatal("Could not read RSA key file %s", keyfile);
+}
+
+#ifndef USE_POLARSSL_HAVEGE
+int urandom_bytes(void *ctx, unsigned char *dest, size_t len)
+{
+       int cur;
+
+       while (len) {
+               cur = read(urandom_fd, dest, len);
+               if (cur < 0)
+                       continue;
+               len -= cur;
+       }
+       return 0;
+}
+#endif
+
+#define DEBUG_LEVEL 0
+static void pssl_debug(void *ctx, int level, const char *str)
+{
+    if (level <= DEBUG_LEVEL)
+               Log_info("PolarSSL [level %d]: %s", level, str);
+}
+
+void SSLi_init(void)
+{
+       char verstring[12];
+
+       initCert();
+#ifdef USE_POLARSSL_TESTCERT
+       if (builtInTestCertificate) {
+               Log_warn("*** Using built-in test certificate and RSA key ***");
+               Log_warn("*** This is not secure! Please use a CA-signed certificate or create a key and self-signed certificate ***");
+               initTestKey();
+       }
+       else
+               initKey();
+#else
+       initKey();
+#endif
+
+       /* Initialize random number generator */
+#ifdef USE_POLARSSL_HAVEGE
+    havege_init(&hs);
+#else
+    urandom_fd = open("/dev/urandom", O_RDONLY);
+    if (urandom_fd < 0)
+           Log_fatal("Cannot open /dev/urandom");
+#endif
+
+    version_get_string(verstring);
+    Log_info("PolarSSL library version %s initialized", verstring);
+}
+
+void SSLi_deinit(void)
+{
+#ifdef POLARSSL_API_V1_3_ABOVE
+       x509_crt_free(&certificate);
+#else
+       x509_free(&certificate);
+#endif
+       rsa_free(&key);
+}
+
+/* Create SHA1 of last certificate in the peer's chain. */
+bool_t SSLi_getSHA1Hash(SSL_handle_t *ssl, uint8_t *hash)
+{
+#ifdef POLARSSL_API_V1_3_ABOVE
+       x509_crt const *cert;
+#else
+       x509_cert const *cert;
+#endif
+#ifdef POLARSSL_API_V1_2_ABOVE
+       cert = ssl_get_peer_cert(ssl);
+#else
+       cert = ssl->peer_cert;
+#endif
+       if (!cert) {
+               return false;
+       }
+       sha1(cert->raw.p, cert->raw.len, hash);
+       return true;
+}
+
+SSL_handle_t *SSLi_newconnection(int *fd, bool_t *SSLready)
+{
+       ssl_context *ssl;
+       ssl_session *ssn;
+       int rc;
+
+       ssl = malloc(sizeof(ssl_context));
+       ssn = malloc(sizeof(ssl_session));
+       if (!ssl || !ssn)
+               Log_fatal("Out of memory");
+       memset(ssl, 0, sizeof(ssl_context));
+       memset(ssn, 0, sizeof(ssl_session));
+
+       rc = ssl_init(ssl);
+       if (rc != 0 )
+               Log_fatal("Failed to initialize: %d", rc);
+
+       ssl_set_endpoint(ssl, SSL_IS_SERVER);
+       ssl_set_authmode(ssl, SSL_VERIFY_OPTIONAL);
+
+#ifdef USE_POLARSSL_HAVEGE
+       ssl_set_rng(ssl, HAVEGE_RAND, &hs);
+#else
+       ssl_set_rng(ssl, urandom_bytes, NULL);
+#endif
+
+       ssl_set_dbg(ssl, pssl_debug, NULL);
+       ssl_set_bio(ssl, net_recv, fd, net_send, fd);
+
+       ssl_set_ciphersuites(ssl, ciphers);
+
+#ifdef POLARSSL_API_V1_2_ABOVE
+    ssl_set_session(ssl, ssn);
+#else
+    ssl_set_session(ssl, 0, 0, ssn);
+#endif
+
+    ssl_set_ca_chain(ssl, &certificate, NULL, NULL);
+#ifdef POLARSSL_API_V1_3_ABOVE
+       ssl_set_own_cert_rsa(ssl, &certificate, &key);
+#else
+       ssl_set_own_cert(ssl, &certificate, &key);
+#endif
+       ssl_set_dh_param(ssl, my_dhm_P, my_dhm_G);
+
+       return ssl;
+}
+
+int SSLi_nonblockaccept(SSL_handle_t *ssl, bool_t *SSLready)
+{
+       int rc;
+
+       rc = ssl_handshake(ssl);
+       if (rc != 0) {
+               if (rc == POLARSSL_ERR_NET_WANT_READ || rc == POLARSSL_ERR_NET_WANT_WRITE) {
+                       return 0;
+               } else if (rc == POLARSSL_ERR_X509_CERT_VERIFY_FAILED) { /* Allow this (selfsigned etc) */
+                       return 0;
+               } else {
+                       Log_warn("SSL handshake failed: %d", rc);
+                       return -1;
+               }
+       }
+       *SSLready = true;
+       return 0;
+}
+
+int SSLi_read(SSL_handle_t *ssl, uint8_t *buf, int len)
+{
+       int rc;
+
+       rc = ssl_read(ssl, buf, len);
+       if (rc == POLARSSL_ERR_NET_WANT_READ)
+               return SSLI_ERROR_WANT_READ;
+       return rc;
+}
+
+int SSLi_write(SSL_handle_t *ssl, uint8_t *buf, int len)
+{
+       int rc;
+
+       rc = ssl_write(ssl, buf, len);
+       if (rc == POLARSSL_ERR_NET_WANT_WRITE)
+               return SSLI_ERROR_WANT_WRITE;
+       return rc;
+}
+
+int SSLi_get_error(SSL_handle_t *ssl, int code)
+{
+       return code;
+}
+
+bool_t SSLi_data_pending(SSL_handle_t *ssl)
+{
+       return ssl_get_bytes_avail(ssl) > 0;
+}
+
+void SSLi_shutdown(SSL_handle_t *ssl)
+{
+       ssl_close_notify(ssl);
+}
+
+void SSLi_free(SSL_handle_t *ssl)
+{
+       Log_debug("SSLi_free");
+#if (POLARSSL_VERSION_MINOR <= 2 && POLARSSL_VERSION_PATCH < 6)
+       free(ssl->session); /* Workaround for memory leak in PolarSSL < 1.2.6 */
+       ssl->session = NULL;
+#endif
+       ssl_free(ssl);
+       free(ssl);
+}
+
diff --git a/src/util.c b/src/util.c
new file mode 100644 (file)
index 0000000..74af117
--- /dev/null
@@ -0,0 +1,73 @@
+/* Copyright (C) 2014, Felix Morgner <felix.morgner@gmail.com>
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+   - 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.
+   - Neither the name of the Developers nor the names of its contributors may
+     be used to endorse or promote products derived from this software without
+     specific prior written permission.
+
+   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 FOUNDATION 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 "util.h"
+
+char* Util_addressToString(struct sockaddr_storage *address)
+{
+       char* addressString = NULL;
+
+       if (address->ss_family == AF_INET) {
+               addressString = malloc(INET_ADDRSTRLEN);
+               inet_ntop(AF_INET, &((struct sockaddr_in *)address)->sin_addr, addressString, INET_ADDRSTRLEN);
+       } else if(address->ss_family == AF_INET6) {
+               addressString = malloc(INET6_ADDRSTRLEN);
+               inet_ntop(AF_INET6, &((struct sockaddr_in6 *)address)->sin6_addr, addressString, INET6_ADDRSTRLEN);
+       }
+
+       return addressString;
+}
+
+int Util_addressToPort(struct sockaddr_storage *address)
+{
+       int port = 0;
+
+       if (address->ss_family == AF_INET) {
+               port = ntohs(((struct sockaddr_in *)address)->sin_port);
+       } else if(address->ss_family == AF_INET6) {
+               port = ntohs(((struct sockaddr_in6 *)address)->sin6_port);
+       }
+
+       return port;
+}
+
+char* Util_clientAddressToString(client_t *client)
+{
+       return Util_addressToString(&client->remote_tcp);
+}
+
+int Util_clientAddressToPortTCP(client_t *client)
+{
+       return Util_addressToPort(&client->remote_tcp);
+}
+
+int Util_clientAddressToPortUDP(client_t *client)
+{
+       return Util_addressToPort(&client->remote_udp);
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644 (file)
index 0000000..d548043
--- /dev/null
@@ -0,0 +1,43 @@
+/* Copyright (C) 2014, Felix Morgner <felix.morgner@gmail.com>
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+   - 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.
+   - Neither the name of the Developers nor the names of its contributors may
+     be used to endorse or promote products derived from this software without
+     specific prior written permission.
+
+   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 FOUNDATION 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.
+*/
+#ifndef __UTIL_H
+#define __UTIL_H
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include "client.h"
+
+char* Util_addressToString(struct sockaddr_storage *address);
+int Util_addressToPort(struct sockaddr_storage *address);
+char* Util_clientAddressToString(client_t *client);
+int Util_clientAddressToPortTCP(client_t *client);
+int Util_clientAddressToPortUDP(client_t *client);
+
+#endif // __UTIL_H