Add the Client_find_by_session() function.
[umurmur.git] / src / client.c
index 37bdb370ee53c27f07a8b9eb4be9b96792598790..b69da172cc51324bf87701bbe698d035fa321b55 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>
@@ -36,6 +36,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include "log.h"
+#include "memory.h"
 #include "list.h"
 #include "client.h"
 #include "ssl.h"
 #include "version.h"
 #include "voicetarget.h"
 #include "ban.h"
+#include "util.h"
 
 extern char system_string[], version_string[];
 
 static int Client_read(client_t *client);
 static int Client_write(client_t *client);
 static int Client_send_udp(client_t *client, uint8_t *data, int len);
+static client_t *Client_find_by_fd(int fd);
 void Client_free(client_t *client);
 
 declare_list(clients);
@@ -62,7 +65,8 @@ bool_t bOpus = true;
 int iCodecAlpha, iCodecBeta;
 bool_t bPreferAlpha;
 
-extern int udpsock;
+extern int* udpsocks;
+extern bool_t hasv4;
 
 void Client_init()
 {
@@ -101,7 +105,7 @@ void Client_janitor()
                c->availableBandwidth += maxBandwidth;
                if (c->availableBandwidth > bwTop)
                        c->availableBandwidth = bwTop;
-               
+
                if (Timer_isElapsed(&c->lastActivity, 1000000LL * INACTIVITY_TIMEOUT)) {
                        /* No activity from client - assume it is lost and close. */
                        Log_info_client(c, "Timeout, closing.");
@@ -113,9 +117,7 @@ void Client_janitor()
 
 void Client_codec_add(client_t *client, int codec)
 {
-       codec_t *cd = malloc(sizeof(codec_t));
-       if (cd == NULL)
-               Log_fatal("Out of memory");
+       codec_t *cd = Memory_safeMalloc(1, sizeof(codec_t));
        init_list_entry(&cd->node);
        cd->codec = codec;
        list_add_tail(&cd->node, &client->codecs);
@@ -136,7 +138,7 @@ codec_t *Client_codec_iterate(client_t *client, codec_t **codec_itr)
 
        if (list_empty(&client->codecs))
                return NULL;
-       
+
        if (cd == NULL) {
                cd = list_get_entry(list_get_first(&client->codecs), codec_t, node);
        } else {
@@ -155,9 +157,7 @@ void Client_token_add(client_t *client, char *token_string)
 
        if (client->tokencount >= MAX_TOKENS)
                return;
-       token = malloc(sizeof(token_t));
-       if (token == NULL)
-               Log_fatal("Out of memory");
+       token = Memory_safeMalloc(1, sizeof(token_t));
        init_list_entry(&token->node);
        token->token = strdup(token_string);
        if (token->token == NULL)
@@ -166,11 +166,11 @@ void Client_token_add(client_t *client, char *token_string)
        client->tokencount++;
 }
 
-bool_t Client_token_match(client_t *client, char *str)
+bool_t Client_token_match(client_t *client, char const *str)
 {
        token_t *token;
        struct dlist *itr;
-       
+
        if (list_empty(&client->tokens))
                return false;
        list_iterate(itr, &client->tokens) {
@@ -185,7 +185,7 @@ void Client_token_free(client_t *client)
 {
        struct dlist *itr, *save;
        token_t *token;
-       
+
        list_iterate_safe(itr, save, &client->tokens) {
                token = list_get_entry(itr, token_t, node);
                list_del(&token->node);
@@ -210,13 +210,13 @@ void recheckCodecVersions(client_t *connectingClient)
        bool_t enableOpus;
 
        init_list_entry(&codec_list);
-       
+
        while (Client_iterate(&client_itr) != NULL) {
                codec_itr = NULL;
                if (client_itr->codec_count == 0 && !client_itr->bOpus)
                        continue;
                while (Client_codec_iterate(client_itr, &codec_itr) != NULL) {
-                       found = false;                      
+                       found = false;
                        list_iterate(itr, &codec_list) {
                                cd = list_get_entry(itr, codec_t, node);
                                if (cd->codec == codec_itr->codec) {
@@ -225,10 +225,7 @@ void recheckCodecVersions(client_t *connectingClient)
                                }
                        }
                        if (!found) {
-                               cd = malloc(sizeof(codec_t));
-                               if (!cd)
-                                       Log_fatal("Out of memory");
-                               memset(cd, 0, sizeof(codec_t));
+                               cd = Memory_safeCalloc(1, sizeof(codec_t));
                                init_list_entry(&cd->node);
                                cd->codec = codec_itr->codec;
                                cd->count = 1;
@@ -239,7 +236,7 @@ void recheckCodecVersions(client_t *connectingClient)
                if (client_itr->bOpus)
                        opus++;
        }
-       if (users == 0) 
+       if (users == 0)
                return;
 
        enableOpus = ((opus * 100 / users) >= getIntConf(OPUS_THRESHOLD));
@@ -266,7 +263,7 @@ void recheckCodecVersions(client_t *connectingClient)
                        bPreferAlpha = true;
                else
                        bPreferAlpha = !bPreferAlpha;
-               
+
                if (bPreferAlpha)
                        iCodecAlpha = version;
                else
@@ -285,18 +282,18 @@ void recheckCodecVersions(client_t *connectingClient)
        sendmsg->payload.codecVersion->opus = enableOpus;
 
        Client_send_message_except(NULL, sendmsg);
-       
+
        if (enableOpus && !bOpus) {
                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);
                        }
                }
-               Log_info("OPUS codec %s", bOpus ? "enabled" : "disabled");      
+               Log_info("OPUS codec %s", bOpus ? "enabled" : "disabled");
        }
-       
+
        bOpus = enableOpus;
 }
 
@@ -317,26 +314,28 @@ 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;
+       char* addressString = NULL;
 
-       if (Ban_isBannedAddr((in_addr_t *)&remote->sin_addr)) {
-               Log_info("Address %s banned. Disconnecting", inet_ntoa(remote->sin_addr));
+       if (Ban_isBannedAddr(remote)) {
+               addressString = Util_addressToString(remote);
+               Log_info("Address %s banned. Disconnecting", addressString);
+               free(addressString);
                return -1;
        }
-       newclient = malloc(sizeof(client_t));
-       if (newclient == NULL)
-               Log_fatal("Out of memory");
-       memset(newclient, 0, sizeof(client_t));
+
+       newclient = Memory_safeCalloc(1, 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));
+               addressString = Util_addressToString(remote);
+               Log_warn("SSL negotiation failed with %s on port %d", addressString, Util_addressToPort(remote));
+               free(addressString);
                free(newclient);
                return -1;
        }
@@ -347,17 +346,17 @@ int Client_add(int fd, struct sockaddr_in *remote)
        newclient->sessionId = findFreeSessionId();
        if (newclient->sessionId < 0)
                Log_fatal("Could not find a free session ID");
-       
+
        init_list_entry(&newclient->txMsgQueue);
        init_list_entry(&newclient->chan_node);
        init_list_entry(&newclient->node);
        init_list_entry(&newclient->voicetargets);
        init_list_entry(&newclient->codecs);
        init_list_entry(&newclient->tokens);
-       
+
        list_add_tail(&newclient->node, &clients);
        clientcount++;
-       
+
        /* Send version message to client */
        sendmsg = Msg_create(Version);
        sendmsg->payload.version->has_version = true;
@@ -395,22 +394,18 @@ void Client_free(client_t *client)
        Client_codec_free(client);
        Voicetarget_free_all(client);
        Client_token_free(client);
-       
+
        list_del(&client->node);
        if (client->ssl)
                SSLi_free(client->ssl);
        close(client->tcpfd);
        clientcount--;
-       if (client->release)
-               free(client->release);
-       if (client->os)
-               free(client->os);                       
-       if (client->os_version)
-               free(client->os_version);                       
-       if (client->username)
-               free(client->username);
-       if (client->context)
-               free(client->context);
+
+       free(client->release);
+       free(client->os);
+       free(client->os_version);
+       free(client->username);
+       free(client->context);
        free(client);
 
        if (authenticatedLeft)
@@ -426,23 +421,48 @@ void Client_close(client_t *client)
 void Client_disconnect_all()
 {
        struct dlist *itr, *save;
-       
+
        list_iterate_safe(itr, save, &clients) {
                Client_free(list_get_entry(itr, client_t, node));
        }
 }
 
-int Client_read_fd(int fd)
+client_t *Client_find_by_session(int session_id)
 {
        struct dlist *itr;
-       client_t *client = NULL;
-       
+
        list_iterate(itr, &clients) {
-               if (fd == list_get_entry(itr, client_t, node)->tcpfd) {
-                       client = list_get_entry(itr, client_t, node);
-                       break;
+               client_t *client = list_get_entry(itr, client_t, node);
+
+               if (client->sessionId == session_id) {
+                       return client;
+               }
+       }
+
+       return NULL;
+}
+
+client_t *Client_find_by_fd(int fd)
+{
+       struct dlist *itr;
+
+       list_iterate(itr, &clients) {
+               client_t *client = list_get_entry(itr, client_t, node);
+
+               if (client->tcpfd == fd) {
+                       return client;
                }
        }
+
+       return NULL;
+}
+
+int Client_read_fd(int fd)
+{
+       client_t *client;
+
+       client = Client_find_by_fd(fd);
+
        if (client != NULL)
                return Client_read(client);
        else
@@ -454,13 +474,13 @@ int Client_read(client_t *client)
        int rc;
 
        Timer_restart(&client->lastActivity);
-       
+
        if (client->writeBlockedOnRead) {
                client->writeBlockedOnRead = false;
                Log_debug("Client_read: writeBlockedOnRead == true");
                return Client_write(client);
        }
-       
+
        if (client->shutdown_wait) {
                Client_free(client);
                return 0;
@@ -476,7 +496,7 @@ int Client_read(client_t *client)
 
        do {
                errno = 0;
-               if (!client->msgsize) 
+               if (!client->msgsize)
                        rc = SSLi_read(client->ssl, &client->rxbuf[client->rxcount], 6 - client->rxcount);
                else
                        rc = SSLi_read(client->ssl, &client->rxbuf[client->rxcount], client->msgsize);
@@ -493,8 +513,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; */
@@ -514,8 +534,8 @@ int Client_read(client_t *client)
                                client->readBlockedOnWrite = true;
                                return 0;
                        }
-                       else if (SSLi_get_error(client->ssl, rc) == SSLI_ERROR_ZERO_RETURN || 
-                                SSLi_get_error(client->ssl, rc) == 0) {
+                       else if (SSLi_get_error(client->ssl, rc) == SSLI_ERROR_ZERO_RETURN ||
+                               SSLi_get_error(client->ssl, rc) == 0) {
                                Log_info_client(client, "Connection closed by peer");
                                Client_close(client);
                        }
@@ -524,8 +544,8 @@ int Client_read(client_t *client)
                                        if (errno == 0)
                                                Log_info_client(client, "Connection closed by peer");
                                        else
-                                               Log_info_client(client,"Error: %s  - Closing connection (code %d)", 
-                                                               strerror(errno));
+                                               Log_info_client(client,"Error: %s  - Closing connection (code %d)",
+                                                       strerror(errno));
                                }
                                else if (SSLi_get_error(client->ssl, rc) == SSLI_ERROR_CONNRESET) {
                                        Log_info_client(client, "Connection reset by peer");
@@ -538,21 +558,16 @@ int Client_read(client_t *client)
                        }
                }
        } while (SSLi_data_pending(client->ssl));
-       
+
        return 0;
 }
 
 int Client_write_fd(int fd)
 {
-       struct dlist *itr;
-       client_t *client = NULL;
-       
-       list_iterate(itr, &clients) {
-               if(fd == list_get_entry(itr, client_t, node)->tcpfd) {
-                       client = list_get_entry(itr, client_t, node);
-                       break;
-               }
-       }
+       client_t *client;
+
+       client = Client_find_by_fd(fd);
+
        if (client != NULL)
                return Client_write(client);
        else
@@ -562,7 +577,7 @@ int Client_write_fd(int fd)
 int Client_write(client_t *client)
 {
        int rc;
-       
+
        if (client->readBlockedOnWrite) {
                client->readBlockedOnWrite = false;
                Log_debug("Client_write: readBlockedOnWrite == true");
@@ -584,7 +599,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 +628,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)
@@ -650,7 +666,7 @@ client_t *Client_iterate(client_t **client_itr)
 
        if (list_empty(&clients))
                return NULL;
-       
+
        if (c == NULL) {
                c = list_get_entry(list_get_first(&clients), client_t, node);
        } else {
@@ -663,24 +679,24 @@ client_t *Client_iterate(client_t **client_itr)
        return c;
 }
 
-void Client_textmessage(client_t *client, char *text)
+void Client_textmessage(client_t *client, const char *text)
 {
        char *message;
        uint32_t *tree_id;
        message_t *sendmsg = NULL;
 
-       message = malloc(strlen(text) + 1);
-       if (!message)
-               Log_fatal("Out of memory");
-       tree_id = malloc(sizeof(uint32_t));
-       if (!tree_id)
+       message = strdup(text);
+
+       if (message == NULL)
                Log_fatal("Out of memory");
+
+       tree_id = Memory_safeMalloc(1, sizeof(uint32_t));
        *tree_id = 0;
        sendmsg = Msg_create(TextMessage);
        sendmsg->payload.textMessage->message = message;
        sendmsg->payload.textMessage->n_tree_id = 1;
        sendmsg->payload.textMessage->tree_id = tree_id;
-       strcpy(message, text);
+
        Client_send_message(client, sendmsg);
 }
 
@@ -688,46 +704,32 @@ void Client_textmessage(client_t *client, char *text)
 int Client_send_message_except(client_t *client, message_t *msg)
 {
        client_t *itr = NULL;
-       int count = 0;
-       
-       Msg_inc_ref(msg); /* Make sure a reference is held during the whole iteration. */
+
        while (Client_iterate(&itr) != NULL) {
                if (itr != client) {
-                       if (count++ > 0)
-                               Msg_inc_ref(msg); /* One extra reference for each new copy */
+                       Msg_inc_ref(msg); /* One extra reference for each new copy */
                        Log_debug("Msg %d to %s refcount %d",  msg->messageType, itr->username, msg->refcount);
                        Client_send_message(itr, msg);
                }
        }
-       Msg_free(msg); /* Free our reference to the message */
-       
-       if (count == 0)
-               Msg_free(msg); /* If only 1 client is connected then no message is passed
-                                               * to Client_send_message(). Free it here. */
-               
+       Msg_free(msg); /* Consume caller's reference. */
+
        return 0;
 }
 
 int Client_send_message_except_ver(client_t *client, message_t *msg, uint32_t version)
 {
        client_t *itr = NULL;
-       int count = 0;
-       
-       Msg_inc_ref(msg); /* Make sure a reference is held during the whole iteration. */
+
        while (Client_iterate(&itr) != NULL) {
                if (itr != client) {
-                       if (count++ > 0)
-                               Msg_inc_ref(msg); /* One extra reference for each new copy */
+                       Msg_inc_ref(msg); /* One extra reference for each new copy */
                        Log_debug("Msg %d to %s refcount %d",  msg->messageType, itr->username, msg->refcount);
                        Client_send_message_ver(itr, msg, version);
                }
        }
-       Msg_free(msg); /* Free our reference to the message */
-       
-       if (count == 0)
-               Msg_free(msg); /* If only 1 client is connected then no message is passed
-                                               * to Client_send_message(). Free it here. */
-               
+       Msg_free(msg); /* Consume caller's reference. */
+
        return 0;
 }
 
@@ -741,9 +743,9 @@ static bool_t checkDecrypt(client_t *client, const uint8_t *encrypted, uint8_t *
                if (Timer_elapsed(&client->cryptState.tLastRequest) > 5000000ULL) {
                        message_t *sendmsg;
                        Timer_restart(&client->cryptState.tLastRequest);
-                       
+
                        sendmsg = Msg_create(CryptSetup);
-                       Log_info_client(client, "Requesting voice channel crypt resync");               
+                       Log_info_client(client, "Requesting voice channel crypt resync");
                        Client_send_message(client, sendmsg);
                }
        }
@@ -751,15 +753,17 @@ 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];
        uint8_t *encrypted = encbuff + 4;
@@ -767,49 +771,74 @@ int Client_read_udp()
        uint8_t encrypted[UDP_PACKET_SIZE];
 #endif
        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
+       else if (len < 5 || len > UDP_PACKET_SIZE) /* 4 bytes crypt header + type + session */
                return 0;
-       } else if (len > UDP_PACKET_SIZE) {
-               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));
-               
+
                sendto(udpsock, encrypted, 6 * sizeof(uint32_t), 0, (struct sockaddr *)&from, fromlen);
                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);
+                                       char* clientAddressString = Util_clientAddressToString(itr);
+                                       Log_info_client(itr, "New UDP connection from %s on port %d", clientAddressString, fromport);
+                                       free(clientAddressString);
+                                       memcpy(&itr->remote_udp, &from, sizeof(struct sockaddr_storage));
                                        break;
                                }
                        }
@@ -818,28 +847,33 @@ int Client_read_udp()
        if (itr == NULL) { /* Couldn't find this peer among connected clients */
                goto out;
        }
-       
+
        itr->bUDP = true;
        len -= 4; /* Adjust for crypt header */
        msgType = (UDPMessageType_t)((buffer[0] >> 5) & 0x7);
+
+       char *clientAddressString = NULL;
+
        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:
+                       clientAddressString = Util_clientAddressToString(itr);
+                       Log_debug("Unknown UDP message type from %s port %d", clientAddressString, fromport);
+                       free(clientAddressString);
                        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:
        return 0;
 }
@@ -851,7 +885,7 @@ static inline void Client_send_voice(client_t *src, client_t *dst, uint8_t *data
                        src->context != NULL && dst->context != NULL && /* ...both source and destination has context */
                        strcmp(src->context, dst->context) == 0) /* ...and the contexts match */
                        Client_send_udp(dst, data, len);
-               else 
+               else
                        Client_send_udp(dst, data, len - poslen);
        }
 }
@@ -867,21 +901,21 @@ int Client_voiceMsg(client_t *client, uint8_t *data, int len)
        unsigned int poslen, counter, size;
        int offset, packetsize;
        voicetarget_t *vt;
-       
-       channel_t *ch = (channel_t *)client->channel;
+
+       channel_t *ch = client->channel;
        struct dlist *itr;
-       
+
        if (!client->authenticated || client->mute || client->self_mute || ch->silent)
                goto out;
-       
+
        packetsize = 20 + 8 + 4 + len;
        if (client->availableBandwidth - packetsize < 0)
                goto out; /* Discard */
        client->availableBandwidth -= packetsize;
-       
+
        Timer_restart(&client->idleTime);
        Timer_restart(&client->lastActivity);
-       
+
        counter = Pds_get_numval(pdi); /* step past session id */
        if ((type >> 5) != UDPVoiceOpus) {
                do {
@@ -892,28 +926,28 @@ int Client_voiceMsg(client_t *client, uint8_t *data, int len)
                size = Pds_get_numval(pdi);
                Pds_skip(pdi, size & 0x1fff);
        }
-               
+
        poslen = pdi->maxsize - pdi->offset; /* For stripping of positional info */
-       
+
        Pds_add_numval(pds, client->sessionId);
        Pds_append_data_nosize(pds, data + 1, len - 1);
-       
+
        if (target == 0x1f) { /* Loopback */
                buffer[0] = (uint8_t) type;
                Client_send_udp(client, buffer, pds->offset + 1);
        }
        else if (target == 0) { /* regular channel speech */
                buffer[0] = (uint8_t) type;
-               
+
                if (ch == NULL)
                        goto out;
-               
+
                list_iterate(itr, &ch->clients) {
                        client_t *c;
                        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 */
@@ -932,8 +966,10 @@ int Client_voiceMsg(client_t *client, uint8_t *data, int len)
                        if (vt->channels[i].linked && !list_empty(&ch->channel_links)) {
                                struct dlist *ch_itr;
                                list_iterate(ch_itr, &ch->channel_links) {
+                                       channellist_t *chl;
                                        channel_t *ch_link;
-                                       ch_link = list_get_entry(ch_itr, channel_t, link_node);
+                                       chl = list_get_entry(ch_itr, channellist_t, node);
+                                       ch_link = chl->chan;
                                        list_iterate(itr, &ch_link->clients) {
                                                client_t *c;
                                                c = list_get_entry(itr, client_t, chan_node);
@@ -959,10 +995,10 @@ int Client_voiceMsg(client_t *client, uint8_t *data, int len)
                                }
                                Chan_freeTreeList(&chanlist);
                        }
-               }                       
+               }
                /* Sessions */
                for (i = 0; i < TARGET_MAX_SESSIONS && vt->sessions[i] != -1; i++) {
-                       client_t *c;
+                       client_t *c = NULL;
                        buffer[0] = (uint8_t) (type | 2);
                        Log_debug("Whisper session %d", vt->sessions[i]);
                        while (Client_iterate(&c) != NULL) {
@@ -976,30 +1012,32 @@ int Client_voiceMsg(client_t *client, uint8_t *data, int len)
 out:
        Pds_free(pds);
        Pds_free(pdi);
-       
+
        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);
+               buf = mbuf = Memory_safeMalloc(1, len + 4 + 16);
                buf += 4;
 #else
-               mbuf = buf = malloc(len + 4);
+               mbuf = buf = Memory_safeMalloc(1, len + 4);
 #endif
-               if (mbuf == NULL)
-                       Log_fatal("Out of memory");
-               
                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 {
                message_t *msg;