Add banlist message handling. Bans can now be edited, added and removed via the banli...
authorMartin Johansson <martin@fatbob.nu>
Tue, 6 Mar 2012 19:40:45 +0000 (20:40 +0100)
committerMartin Johansson <martin@fatbob.nu>
Tue, 6 Mar 2012 19:40:45 +0000 (20:40 +0100)
src/ban.c
src/ban.h
src/messagehandler.c
src/messages.c
src/messages.h

index fbf711ac22a6762cf66013e9cbec07774fd6a667..710148508e2be5bb4f49a1bf9799dc53ef50db42 100644 (file)
--- a/src/ban.c
+++ b/src/ban.c
@@ -30,6 +30,7 @@
 */
 
 #include <stdlib.h>
+#include <time.h>
 #include "log.h"
 #include "list.h"
 #include "ban.h"
 
 declare_list(banlist);
 static int bancount; /* = 0 */
+static int ban_duration;
 
+void Ban_init(void)
+{
+       ban_duration = getIntConf(BAN_LENGTH);
+       /* Read ban file here */
+}
+
+void Ban_deinit(void)
+{
+       /* Save banlist */
+}
 void Ban_UserBan(client_t *client, char *reason)
 {
        ban_t *ban;
        char hexhash[41];
 
        ban = malloc(sizeof(ban_t));
+       if (ban == NULL)
+               Log_fatal("Out of memory");
+       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;
        ban->reason = strdup(reason);
        ban->name = strdup(client->username);
+       ban->time = time(NULL);
+       ban->duration = ban_duration;
        Timer_init(&ban->startTime);
        list_add_tail(&ban->node, &banlist);
+       bancount++;
        
        SSLi_hash2hex(ban->hash, hexhash);
        Log_info_client(client, "User kickbanned. Reason: '%s' Hash: %s IP: %s Banned for: %d seconds",
                        ban->name, ban->reason, hexhash, inet_ntoa(*((struct in_addr *)&ban->address)),
-                       getIntConf(BAN_LENGTH));
+                       ban->duration);
 }
 
 
 void Ban_pruneBanned()
 {
        struct dlist *itr;
-       static int64_t bantime = 0;
        ban_t *ban;
        char hexhash[41];
-       
-       if (bantime == 0) {
-               bantime = getIntConf(BAN_LENGTH) * 1000000LL;
-       }
-       
+       uint64_t bantime_long;
+               
        list_iterate(itr, &banlist) {
                ban = list_get_entry(itr, ban_t, node);
+               bantime_long = ban->duration * 1000000LL;
 #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 / 1000000LL - Timer_elapsed(&ban->startTime) / 1000000LL);
+                         bantime_long / 1000000LL - Timer_elapsed(&ban->startTime) / 1000000LL);
 #endif
-               if (Timer_isElapsed(&ban->startTime, bantime)) {
+               /* Duration of 0 = forever */
+               if (ban->duration != 0 && Timer_isElapsed(&ban->startTime, bantime_long)) {
                        free(ban->name);
                        free(ban->reason);
                        list_del(&ban->node);
                        free(ban);
+                       bancount--;
                }
        }
 }
@@ -104,11 +123,95 @@ bool_t Ban_isBannedAddr(in_addr_t *addr)
 {
        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);
-               if (memcmp(&ban->address, addr, sizeof(in_addr_t)) == 0) 
+               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 (memcmp(&tempaddr1, &tempaddr2, sizeof(in_addr_t)) == 0) 
                        return true;
        }
        return false;
 }
 
+int Ban_getBanCount(void)
+{
+       return bancount;
+}
+
+message_t *Ban_getBanList(void)
+{
+       int i = 0;
+       struct dlist *itr;
+       ban_t *ban;
+       message_t *msg;
+       struct tm timespec;
+       char timestr[32];
+       char hexhash[41];
+       uint8_t address[16];
+       
+       msg = Msg_banList_create(bancount);
+       list_iterate(itr, &banlist) {
+               ban = list_get_entry(itr, ban_t, node);
+               gmtime_r(&ban->time, &timespec);
+               strftime(timestr, 32, "%Y-%m-%dT%H:%M:%S", &timespec);
+               SSLi_hash2hex(ban->hash, hexhash);
+               /* ipv4 representation as ipv6 address. */
+               memset(address, 0, 16);
+               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);
+       }
+       return msg;
+}
+
+void Ban_clearBanList()
+{
+       ban_t *ban;
+       struct dlist *itr, *save;
+       list_iterate_safe(itr, save, &banlist) {
+               ban = list_get_entry(itr, ban_t, node);
+               free(ban->name);
+               free(ban->reason);
+               list_del(&ban->node);
+               free(ban);
+               bancount--;
+       }
+}
+
+void Ban_putBanList(message_t *msg, int n_bans)
+{
+       int i = 0;
+       struct tm timespec;
+       ban_t *ban;
+       char *hexhash, *name, *reason, *start;
+       uint32_t duration, mask;
+       uint8_t *address;
+       
+       for (i = 0; i < n_bans; i++) {
+               Msg_banList_getEntry(msg, i, &address, &mask, &name, &hexhash, &reason, &start, &duration);
+               ban = malloc(sizeof(ban_t));
+               if (ban == NULL)
+                       Log_fatal("Out of memory");
+               memset(ban, 0, sizeof(ban_t));
+               SSLi_hex2hash(hexhash, ban->hash);
+               memcpy(&ban->address, &address[12], 4);
+               ban->mask = mask;
+               ban->reason = strdup(reason);
+               ban->name = strdup(name);
+               strptime(start, "%Y-%m-%dT%H:%M:%S", &timespec);
+               ban->time = mktime(&timespec);
+               Timer_init(&ban->startTime);
+               ban->duration = duration;
+               list_add_tail(&ban->node, &banlist);
+               bancount++;
+       }
+}
index 61d5774b03c8510928606bae4503d966ba0df4df..f74fd7a0176992721f7b257008f77df856c759cb 100644 (file)
--- a/src/ban.h
+++ b/src/ban.h
 typedef struct {
        uint8_t hash[20];
        in_addr_t address;
+       uint32_t mask;
        char *reason;
        char *name;
+       time_t time;
+       uint32_t duration;
        etimer_t startTime;
        struct dlist node;
 } ban_t;
@@ -49,5 +52,10 @@ 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);
+int Ban_getBanCount(void);
+message_t *Ban_getBanList(void);
+void Ban_putBanList(message_t *msg, int n_bans);
+void Ban_init(void);
+void Ban_deinit(void);
 
 #endif
index 50b648a29fbecf31a8d9e22d3b8d276945f16ada..f07e3ca186de5099adfdb2cee5869e43508c22c3 100644 (file)
@@ -40,6 +40,7 @@
 #include "channel.h"
 #include "conf.h"
 #include "voicetarget.h"
+#include "ban.h"
 
 #define MAX_TEXT 512
 #define MAX_USERNAME 128
@@ -627,6 +628,8 @@ void Mh_handle_message(client_t *client, message_t *msg)
                
                if (!getBoolConf(ALLOW_TEXTMESSAGE))
                        msg->payload.permissionQuery->permissions &= ~PERM_TEXTMESSAGE;
+               if (!getBoolConf(ENABLE_BAN))
+                       msg->payload.permissionQuery->permissions &= ~PERM_BAN;
                
                Client_send_message(client, msg);
                break;
@@ -800,6 +803,7 @@ void Mh_handle_message(client_t *client, message_t *msg)
                        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 */
                        sendmsg->payload.userStats->address.len = 16;
                }
                /* BW */
@@ -835,7 +839,10 @@ void Mh_handle_message(client_t *client, message_t *msg)
                msg->payload.userRemove->actor = client->sessionId;
 
                if (msg->payload.userRemove->has_ban && msg->payload.userRemove->ban) {
-                       Ban_UserBan(target, msg->payload.userRemove->reason);
+                       if (!getBoolConf(ENABLE_BAN))
+                               sendPermissionDenied(client, "Permission denied");
+                       else
+                               Ban_UserBan(target, msg->payload.userRemove->reason);
                } else {
                        Log_info_client(target, "User kicked. Reason: '%s'",
                                        strlen(msg->payload.userRemove->reason) == 0 ? "N/A" : msg->payload.userRemove->reason);
@@ -846,13 +853,32 @@ void Mh_handle_message(client_t *client, message_t *msg)
                Client_send_message_except(NULL, msg);
                Client_close(target);
                break;
+       case BanList:
+               /* Only admin can issue this */
+               if (!client->isAdmin) {
+                       sendPermissionDenied(client, "Permission denied");
+                       break;
+               }
+               if (!getBoolConf(ENABLE_BAN)) {
+                       sendPermissionDenied(client, "Permission denied");
+                       break;
+               }
+               if (msg->payload.banList->has_query && msg->payload.banList->query) {
+                       /* Create banlist message and add banentrys */
+                       sendmsg = Ban_getBanList();
+                       Client_send_message(client, sendmsg);
+               } else {
+                       /* Clear banlist and set the new one */
+                       Ban_clearBanList();
+                       Ban_putBanList(msg, msg->payload.banList->n_bans);
+               }
+               break;
                
                /* Permission denied for all these messages. Not implemented. */
        case ChannelRemove:
        case ContextAction:
        case ContextActionAdd:
        case ACL:
-       case BanList:
        case UserList:
        case QueryUsers:
                sendPermissionDenied(client, "Not supported by uMurmur");
index 893708acec63a04ffeb40e4990dec1a3345821b5..b9ce9a7aa968327c9ddbac06c445e08172f2b425 100644 (file)
@@ -247,6 +247,16 @@ int Msg_messageToNetwork(message_t *msg, uint8_t *buffer)
                mumble_proto__server_config__pack(msg->payload.serverConfig, bufptr);
                break;
 
+       case BanList:
+               len = mumble_proto__ban_list__get_packed_size(msg->payload.banList);
+               if (len > MAX_MSGSIZE) {
+                       Log_warn("Too big tx message. Discarding");
+                       break;
+                       }
+               Msg_addPreamble(buffer, msg->messageType, len);
+               Log_debug("Msg_MessageToNetwork: BanList size %d", len);
+               mumble_proto__ban_list__pack(msg->payload.banList, bufptr);
+               break;
        default:
                Log_warn("Msg_MessageToNetwork: Unsupported message %d", msg->messageType);
                return 0;
@@ -270,6 +280,7 @@ static message_t *Msg_create_nopayload(messageType_t messageType)
 message_t *Msg_create(messageType_t messageType)
 {
        message_t *msg = Msg_create_nopayload(messageType);
+       int i;
        
        switch (messageType) {
        case Version:
@@ -370,6 +381,73 @@ message_t *Msg_create(messageType_t messageType)
        return msg;
 }
 
+message_t *Msg_banList_create(int n_bans)
+{
+       message_t *msg = Msg_create_nopayload(BanList);
+       int i;
+       
+       msg->payload.banList = malloc(sizeof(MumbleProto__BanList));
+       if (msg->payload.banList == NULL)
+               Log_fatal("Out of memory");
+       memset(msg->payload.banList, 0, sizeof(MumbleProto__BanList));
+       mumble_proto__ban_list__init(msg->payload.banList);
+       msg->payload.banList->n_bans = n_bans;
+       msg->payload.banList->bans = malloc(sizeof(MumbleProto__BanList__BanEntry *) * n_bans);
+       if (msg->payload.banList->bans == NULL)
+               Log_fatal("Out of memory");
+       for (i = 0; i < n_bans; i++) {
+               msg->payload.banList->bans[i] = malloc(sizeof(MumbleProto__BanList__BanEntry));
+               if (msg->payload.banList->bans[i] == NULL)
+                       Log_fatal("Out of memory");
+               memset(msg->payload.banList->bans[i], 0, sizeof(MumbleProto__BanList__BanEntry));
+               mumble_proto__ban_list__ban_entry__init(msg->payload.banList->bans[i]);
+       }
+       return msg;
+}
+
+void Msg_banList_addEntry(message_t *msg, int index, uint8_t *address, uint32_t mask,
+                          char *name, char *hash, char *reason, char *start, uint32_t duration)
+{
+       MumbleProto__BanList__BanEntry *entry = msg->payload.banList->bans[index];
+       
+       entry->address.data = malloc(16);
+       if (!entry->address.data)
+               Log_fatal("Out of memory");     
+       memcpy(entry->address.data, address, 16);
+       entry->address.len = 16;
+       entry->mask = mask;
+       entry->name = strdup(name);
+       entry->hash = strdup(hash);
+       entry->reason = strdup(reason);
+       entry->start = strdup(start);
+       if (!entry->name || !entry->hash || !entry->reason || !entry->start)
+               Log_fatal("Out of memory");     
+               
+       if (duration > 0) {
+               entry->duration = duration;
+               entry->has_duration = true;
+       }
+       Log_debug("Msg_banList_addEntry: %s %s %s %s %s", entry->name, entry->hash, entry->address.data, entry->reason, entry->start);
+}
+
+void Msg_banList_getEntry(message_t *msg, int index, uint8_t **address, uint32_t *mask,
+                          char **name, char **hash, char **reason, char **start, uint32_t *duration)
+{
+       MumbleProto__BanList__BanEntry *entry = msg->payload.banList->bans[index];
+
+       *address =  entry->address.data;
+       *mask = entry->mask;
+       *name = entry->name;
+       *hash = entry->hash;
+       *reason = entry->reason;
+       *start = entry->start;
+       if (entry->has_duration)
+               *duration = entry->duration;
+       else
+               *duration = 0;
+}
+
+
 void Msg_inc_ref(message_t *msg)
 {
        msg->refcount++;
@@ -377,6 +455,8 @@ void Msg_inc_ref(message_t *msg)
 
 void Msg_free(message_t *msg)
 {
+       int i;
+       
        if (msg->refcount) msg->refcount--;
        if (msg->refcount > 0)
                return;
@@ -563,6 +643,22 @@ void Msg_free(message_t *msg)
                        free(msg->payload.serverConfig);
                }
                break;
+       case BanList:
+               if (msg->unpacked)
+                       mumble_proto__ban_list__free_unpacked(msg->payload.banList, NULL);
+               else {
+                       for (i = 0; i < msg->payload.banList->n_bans; i++) {
+                               free(msg->payload.banList->bans[i]->address.data);
+                               free(msg->payload.banList->bans[i]->name);
+                               free(msg->payload.banList->bans[i]->hash);
+                               free(msg->payload.banList->bans[i]->reason);
+                               free(msg->payload.banList->bans[i]->start);
+                               free(msg->payload.banList->bans[i]);
+                       }
+                       free(msg->payload.banList->bans);
+                       free(msg->payload.banList);
+               }
+               break;
 
        default:
                Log_warn("Msg_free: Unsupported message %d", msg->messageType);
@@ -758,9 +854,18 @@ message_t *Msg_networkToMessage(uint8_t *data, int size)
                        goto err_out;
                break;
        }
+       case BanList:
+       {
+               msg = Msg_create_nopayload(BanList);
+               msg->unpacked = true;
+               msg->payload.banList = mumble_proto__ban_list__unpack(NULL, msgLen, msgData);
+               if (msg->payload.banList == NULL)
+                       goto err_out;
+               break;
+       }
 
        default:
-               Log_warn("Unsupported message %d", messageType);
+               Log_warn("Msg_networkToMessage: Unsupported message %d", messageType);
                break;
        }
        return msg;
index e92fe2afc93e6815686b30abc202d38bebb696c6..69013a00136663b6e2ba63e44fcd216f327dc95f 100644 (file)
@@ -32,6 +32,7 @@
 #define MESSAGES_H_89768
 
 #include <stdint.h>
+#include <arpa/inet.h>
 #include "Mumble.pb-c.h"
 #include "list.h"
 #include "types.h"
@@ -111,8 +112,7 @@ typedef union payload {
        struct  _MumbleProto__ChannelState *channelState;
        struct  _MumbleProto__UserRemove *userRemove;
        struct  _MumbleProto__UserState *userState;
-       /* BanEntry not supported */
-       /* BanList not supported */
+       struct  _MumbleProto__BanList *banList; 
        struct  _MumbleProto__TextMessage *textMessage;
        struct  _MumbleProto__PermissionDenied *permissionDenied;
        /* ChanACL not supported */
@@ -147,5 +147,11 @@ void Msg_inc_ref(message_t *msg);
 
 message_t *Msg_CreateVoiceMsg(uint8_t *data, int size);
 message_t *Msg_create(messageType_t messageType);
+void Msg_banList_addEntry(message_t *msg, int index, uint8_t *address, uint32_t mask,
+                          char *name, char *hash, char *reason, char *start, uint32_t duration);
+void Msg_banList_getEntry(message_t *msg, int index, uint8_t **address, uint32_t *mask,
+                          char **name, char **hash, char **reason, char **start, uint32_t *duration);
+message_t *Msg_banList_create(int n_bans);
+
 
 #endif