Handle systems without IPv6 support correctly
[umurmur.git] / src / client.c
index af36723f3f3679c63bbcde46942eb0e2ab3447a1..526e1f4bf5f2fc35cee0b33e10dd5ecfba2e3d90 100644 (file)
@@ -1,33 +1,33 @@
 /* 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.
-*/
+   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 <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[];
 
@@ -63,6 +64,7 @@ int iCodecAlpha, iCodecBeta;
 bool_t bPreferAlpha;
 
 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);
                        }
                }
@@ -321,35 +323,20 @@ int Client_add(int fd, struct sockaddr_storage *remote)
 {
        client_t* newclient;
        message_t *sendmsg;
-       char addressPresentation[INET6_ADDRSTRLEN];
-       int port;
 
-#warning FIX BANNING BEFORE RELEASE
-#if 0
-       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;
        }
-#endif
 
        if ((newclient = calloc(1, sizeof(client_t))) == NULL)
-               Log_fatal("Out of memory (%s:%s)", __FILE__, __LINE__);
-
-       if(remote->ss_family == AF_INET) {
-               inet_ntop(AF_INET, &((struct sockaddr_in*)remote)->sin_addr, addressPresentation, INET6_ADDRSTRLEN);
-               port = ntohs(((struct sockaddr_in*)remote)->sin_port);
-       } else {
-               inet_ntop(AF_INET6, &((struct sockaddr_in6*)remote)->sin6_addr, addressPresentation, INET6_ADDRSTRLEN);
-               port = ntohs(((struct sockaddr_in6*)remote)->sin6_port);
-       }
-
-       memcpy(newclient->addressString, addressPresentation, INET6_ADDRSTRLEN);
+               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_storage));
        newclient->ssl = SSLi_newconnection(&newclient->tcpfd, &newclient->SSLready);
        if (newclient->ssl == NULL) {
-               Log_warn("SSL negotiation failed with %s on port %d", addressPresentation, port);
+               Log_warn("SSL negotiation failed with %s on port %d", Util_addressToString(remote), Util_addressToPort(remote));
                free(newclient);
                return -1;
        }
@@ -506,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; */
@@ -528,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);
                        }
@@ -538,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");
@@ -626,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)
@@ -636,7 +624,7 @@ int Client_send_message(client_t *client, message_t *msg)
        }
        if (client->txsize != 0 || !client->SSLready) {
                /* Queue message */
-               if ((client->txQueueCount > 5 &&        msg->messageType == UDPTunnel) ||
+               if ((client->txQueueCount > 5 &&  msg->messageType == UDPTunnel) ||
                        client->txQueueCount > 30) {
                        Msg_free(msg);
                        return -1;
@@ -787,34 +775,30 @@ int Client_read_udp(int udpsock)
 
        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));
-               fromport = ntohs(((struct sockaddr_in*)&from)->sin_port);
                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));
-               fromport = ntohs(((struct sockaddr_in6*)&from)->sin6_port);
                memcpy(&key[0], &fromport, 2);
                memcpy(&key[2], fromaddress, 4 * sizeof(in_addr_t));
        }
 
-       if (len == 0) {
+       if (len <= 0)
                return -1;
-       } else 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));
@@ -850,7 +834,7 @@ int Client_read_udp(int udpsock)
                        if (memcmp(itraddress, fromaddress, addresslength) == 0) {
                                if (checkDecrypt(itr, encrypted, buffer, len)) {
                                        memcpy(itr->key, key, KEY_LENGTH);
-                                       Log_info_client(itr, "New UDP connection from %s on port %d", itr->addressString, fromport);
+                                       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;
                                }
@@ -865,21 +849,21 @@ int Client_read_udp(int udpsock)
        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", itr->addressString, fromport);
-               break;
        }
 
 out:
@@ -955,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 */
@@ -1025,15 +1009,10 @@ out:
 static int Client_send_udp(client_t *client, uint8_t *data, int len)
 {
        uint8_t *buf, *mbuf;
-       uint16_t clientport;
-       int udpsock = (client->remote_udp.ss_family == AF_INET) ? udpsocks[0] : udpsocks[1];
 
-       if (client->remote_udp.ss_family == AF_INET)
-               clientport = ntohs(((struct sockaddr_in*)&client->remote_udp)->sin_port);
-       else
-               clientport = ntohs(((struct sockaddr_in6*)&client->remote_udp)->sin6_port);
+       int udpsock = (client->remote_udp.ss_family == AF_INET) ? udpsocks[0] : udpsocks[(hasv4) ? 1 : 0];
 
-       if (clientport != 0 && CryptState_isValid(&client->cryptState) &&
+       if (Util_clientAddressToPortUDP(client) != 0 && CryptState_isValid(&client->cryptState) &&
                client->bUDP) {
 #if defined(__LP64__)
                buf = mbuf = malloc(len + 4 + 16);
@@ -1046,10 +1025,11 @@ static int Client_send_udp(client_t *client, uint8_t *data, int len)
 
                CryptState_encrypt(&client->cryptState, data, buf, len);
 
-       if (client->remote_udp.ss_family == AF_INET)
-               sendto(udpsock, buf, len + 4, 0, (struct sockaddr *)&client->remote_udp, sizeof(struct sockaddr_in));
-       else
-               sendto(udpsock, buf, len + 4, 0, (struct sockaddr *)&client->remote_udp, sizeof(struct sockaddr_in6));
+#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 {