Games-FrozenBubble

 view release on metacpan or  search on metacpan

server/net.c_tmp  view on Meta::CPAN

 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ******************************************************************************/

/*
 * this file holds network transmission operations.
 * it should be as far away as possible from game considerations
 */

#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <poll.h>
#include <sys/utsname.h>
#include <sys/time.h>
#include <pwd.h>
#include <signal.h>
#include <regex.h>

#include <glib.h>

#include "game.h"
#include "tools.h"
#include "log.h"
#include "net.h"

/* this is set in game.c and used here for answering with
 * the requested command without passing this additional arg */
char* current_command;

const int proto_major = 1;
const int proto_minor = 2;

static char greets_msg_base[] = "SERVER_READY %s %s";
static char* servername = NULL;
static char* serverlanguage = NULL;

static char ok_generic[] = "OK";

static char fl_client_nolf[] = "NO_LF_WITHIN_TOO_MUCH_DATA (I bet you're not a regular FB client, hu?)";
static char fl_client_nulbyte[] = "NUL_BYTE_BEFORE_NEWLINE (I bet you're not a regular FB client, hu?)";
static char fl_server_full[] = "SERVER_IS_FULL";
static char fl_server_overloaded[] = "SERVER_IS_OVERLOADED";
static char fl_client_noactivity[] = "NO_ACTIVITY_WITHIN_GRACETIME";
static char fl_client_blacklisted[] = "YOU_ARE_BLACKLISTED";

static double date_amount_transmitted_reset;

#define DEFAULT_PORT 1511  // a.k.a 0xF 0xB thx misc
#define DEFAULT_MAX_USERS 255
#define DEFAULT_INTERVAL_REREGISTER 60
#define DEFAULT_MAX_TRANSMISSION_RATE 100000
#define DEFAULT_OUTPUT "INFO"
#define DEFAULT_GRACETIME 900
static int port = DEFAULT_PORT;
static int max_users = DEFAULT_MAX_USERS;
int interval_reregister = DEFAULT_INTERVAL_REREGISTER;
static int max_transmission_rate = DEFAULT_MAX_TRANSMISSION_RATE;
static int gracetime = DEFAULT_GRACETIME;

static int lan_game_mode = 0;

static int tcp_server_socket;
static int udp_server_socket = -1;

static int quiet = 0;

static char* external_hostname = "DISTANT_END";
static int external_port = -1;

static char* blacklisted_IPs = NULL;
char* pidfile = NULL;
char* user_to_switch = NULL;

static char* alert_words_file = NULL;
GList * alert_words = NULL;

static GList * conns = NULL;
static GList * conns_prio = NULL;

#define INCOMING_DATA_BUFSIZE 16384
static char * incoming_data_buffers[256];
static int incoming_data_buffers_count[256];
static time_t last_data_in[256];
static time_t minute_for_talk_flood[256];
int amount_talk_flood[256];
static int prio[256];

/* send line adding the protocol in front of the supplied msg */
static ssize_t send_line(int fd, char* msg)
{
        static char buf[16384] __attribute__((aligned(4096)));
        int size;
        if (current_command)
                size = snprintf(buf, sizeof(buf), "FB/%d.%d %s: %s\n", proto_major, proto_minor, current_command, msg);
        else
                size = snprintf(buf, sizeof(buf), "FB/%d.%d ???: %s\n", proto_major, proto_minor, msg);
        if (size > sizeof(buf)-1) {
                size = sizeof(buf)-1;
                buf[sizeof(buf)-2] = '\n';
        }
        if (size > 0) {
                return send(fd, buf, size, MSG_NOSIGNAL);
        } else {
                l2(OUTPUT_TYPE_ERROR, "[%d] Format failure, impossible to send message '%s'", fd, msg);
                return 0;
        }
}

ssize_t send_line_log(int fd, char* dest_msg, char* inco_msg)
{
        l3(OUTPUT_TYPE_DEBUG, "[%d] %s -> %s", fd, inco_msg, dest_msg);
        return send_line(fd, dest_msg);
}

ssize_t send_line_log_push(int fd, char* dest_msg)
{
        // drop pre-prio messages for connections in prio mode; there can be some remaining uninteresting
        // messages arriving right in between (TALK)

server/net.c_tmp  view on Meta::CPAN

                        // We've really accepted this new connection. Init data.
                        last_data_in[fd] = current_time;
                        minute_for_talk_flood[fd] = current_time/60;
                        amount_talk_flood[fd] = 0;
                        nick[fd] = NULL;
                        geoloc[fd] = NULL;
                        IP[fd] = strdup_(inet_ntoa(client_addr.sin_addr));
                        prio[fd] = 0;
                        remote_proto_minor[fd] = -1;
                        send_line_log_push(fd, get_greets_msg());
                        conns = g_list_append(conns, GINT_TO_POINTER(fd));
                        player_connects(fd);
                        incoming_data_buffers[fd] = malloc_(sizeof(char) * INCOMING_DATA_BUFSIZE);
                        incoming_data_buffers_count[fd] = 0;
                        admin_authorized[fd] = streq("127.0.0.1", inet_ntoa(client_addr.sin_addr));
                        recalculate_list_games = 1;
                }

                if (udp_server_socket != -1 && FD_ISSET(udp_server_socket, &conns_set))
                        handle_udp_request();
        }
}


int conns_nb(void)
{
        return g_list_length(conns_prio) + g_list_length(conns);
}

void add_prio(int fd)
{
        conns_prio = g_list_append(conns_prio, GINT_TO_POINTER(fd));
        new_conns = g_list_remove(new_conns, GINT_TO_POINTER(fd));
        prio[fd] = 1;
        if (lan_game_mode && g_list_length(conns_prio) > 0 && udp_server_socket != -1) {
                close(tcp_server_socket);
                close(udp_server_socket);
                tcp_server_socket = udp_server_socket = -1;
        }
}

void close_server() {
        if (tcp_server_socket != -1) {
                close(tcp_server_socket);
        }
        if (udp_server_socket != -1) {
                close(udp_server_socket);
        }
        tcp_server_socket = udp_server_socket = -1;
}

static void help(void)
{
        printf("Usage: fb-server [OPTION]...\n");
        printf("\n");
        printf("     -a lang                   set the preferred language of the server (it is just an indication used by players when choosing a server, so that they can chat using their native language - you can choose none with -z)\n");
        printf("     -A alert_words_file       set the file containing alert words (one POSIX regexp by line) - this file is reread when receiving the ADMIN_REREAD command from 127.0.0.1\n");
        printf("     -c conffile               specify the path of the configuration file\n");
        printf("     -d                        debug mode: do not daemonize, and log on STDERR rather than through syslog (implies -q)\n");
        printf("     -f pidfile                set the file in which the pid of the daemon must be written\n");
        printf("     -g gracetime              set the gracetime after which a client with no network activity is terminated (in seconds, defaults to %d)\n", DEFAULT_GRACETIME);
        printf("     -h                        display this help then exits\n");
        printf("     -H host                   set the hostname (or IP) as seen from outside (by default, when registering the server to www.frozen-bubble.org, the distant end at IP level will be used)\n");
        printf("     -i minutes                set the minutes interval for reregistering on the master server (except if -q is provided); use 0 to disable the feature; defaults to %d minutes)\n", DEFAULT_INTERVAL_REREGISTER);
        printf("     -l                        LAN mode: create an UDP server (on port %d) to answer broadcasts of clients discovering where are the servers\n", DEFAULT_PORT);
        printf("     -L                        LAN/game mode: create an UDP server as above, but limit number of games to 1 (this is for an FB client hosting a LAN server)\n");
        printf("     -m max_users              set the maximum of connected users (defaults to %d, physical maximum 255 in non debug mode)\n", DEFAULT_MAX_USERS);
        printf("     -n name                   set the server name presented to players (if unset, defaults to hostname)\n");
        printf("     -o outputtype             set the output type; can be DEBUG, CONNECT, INFO, ERROR; each level includes messages of next level; defaults to INFO\n");
        printf("     -p port                   set the server port (defaults to %d)\n", DEFAULT_PORT);
        printf("     -P port                   set the server port as seen from outside (defaults to the port specified with -p)\n");
        printf("     -q                        \"quiet\" mode: don't automatically register the server to www.frozen-bubble.org\n");
        printf("     -t max_transmission_rate  set the maximum transmission rate, in bytes per second (defaults to %d)\n", DEFAULT_MAX_TRANSMISSION_RATE);
        printf("     -u user                   switch daemon to specified user\n");
        printf("     -z                        set that there is no preferred language for the server (see -a)\n");
}

static void create_udp_server(void)
{
        struct sockaddr_in server_addr;

        printf("-l: creating UDP server for answering broadcast server discover, on default port %d\n", DEFAULT_PORT);

        udp_server_socket = socket(AF_INET, SOCK_DGRAM, 0);
        if (udp_server_socket < 0) {
                perror("socket");
                exit(EXIT_FAILURE);
        }

        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        server_addr.sin_port = htons(DEFAULT_PORT);
        if (bind(udp_server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
                perror("bind UDP 1511");
                exit(EXIT_FAILURE);
        }
}

static void cleanup_alert_words(gpointer data, gpointer user_data)
{
        regex_t* preg = data;
        regfree(preg);
        free(preg);
}

static char* read_alert_words(char* file)
{
        char buf[512];
        FILE* f;
        if (alert_words) {
                g_list_foreach(alert_words, cleanup_alert_words, NULL);
                g_list_free(alert_words);
                alert_words = NULL;
        }
        f = fopen(file, "r");
        if (!f) {
                return asprintf_("Error opening file");
        } else {
                while (fgets(buf, sizeof(buf), f)) {
                        char errbuf[512];
                        int errcode;

server/net.c_tmp  view on Meta::CPAN

        if (alert_words_file) {
                char* response;
                l1(OUTPUT_TYPE_INFO, "Rereading alert words file '%s'.", alert_words_file);
                response = read_alert_words(alert_words_file);
                if (response) {
                        l1(OUTPUT_TYPE_ERROR, "Error rereading alert words file: '%s'\n", response);
                        free(response);
                }
        }

        l0(OUTPUT_TYPE_INFO, "Downloading latest blacklisted IPs.");
        download_blacklisted_IPs();
}

static void handle_parameter(char command, char * param) {
        char* response;
        switch (command) {
        case 'a':
                if (streq(param, "af") || streq(param, "ar") || streq(param, "az") || streq(param, "bg") || streq(param, "br")
                    || streq(param, "bs") || streq(param, "ca") || streq(param, "cs") || streq(param, "cy") || streq(param, "da")
                    || streq(param, "de") || streq(param, "el") || streq(param, "en") || streq(param, "eo") || streq(param, "eu")
                    || streq(param, "es") || streq(param, "fi") || streq(param, "fr") || streq(param, "ga") || streq(param, "gl")
                    || streq(param, "hr") || streq(param, "hu") || streq(param, "id") || streq(param, "ir") || streq(param, "is") || streq(param, "it")
                    || streq(param, "ja") || streq(param, "ko") || streq(param, "lt") || streq(param, "lv") || streq(param, "mk")
                    || streq(param, "ms") || streq(param, "nl") || streq(param, "ne") || streq(param, "no") || streq(param, "pl") || streq(param, "pt")
                    || streq(param, "pt_BR") || streq(param, "ro") || streq(param, "ru") || streq(param, "sk") || streq(param, "sl")
                    || streq(param, "sq") || streq(param, "sv") || streq(param, "tg") || streq(param, "tr") || streq(param, "uk")
                    || streq(param, "uz") || streq(param, "vi") || streq(param, "wa") || streq(param, "zh_CN") || streq(param, "zh_TW")) {
                        serverlanguage = strdup(param);
                        printf("-a: setting preferred language for users of the server to '%s'\n", serverlanguage);
                } else {
                        fprintf(stderr, "-a: '%s' not a valid language, ignoring\n", param);
                        fprintf(stderr, "    valid languages are: af, ar, az, bg, br, bs, ca, cs, cy, da, de, el, en, eo, eu, es, fi, fr, ga, gl, hr, hu, id, ir, is, it, ja, ko, lt, lv, mk, ms, nl, ne, no, pl, pt, pt_BR, ro, ru, sk, sl, sq, sv, tg, t...
                }
                break;
        case 'A':
                response = read_alert_words(param);
                if (response) {
                        fprintf(stderr, "-A: error reading alert words file '%s': %s\n", param, response);
                        free(response);
                } else {
                        printf("-A: successfully read alert words file '%s'\n", param);
                        alert_words_file = strdup(param);
                }
                break;
        case 'd':
                printf("-d: debug mode on: will not daemonize and will display log messages on STDERR\n");
                debug_mode = TRUE;
                quiet = TRUE;
                break;
        case 'f':
                printf("-f: will store pid of daemon into file '%s'\n", param);
                pidfile = strdup(param);
                break;
        case 'g':
                gracetime = charstar_to_int(param);
                if (gracetime != 0)
                        printf("-g: setting gracetime to '%d' seconds (equals '%d' minutes)\n", gracetime, gracetime/60);
                else {
                        fprintf(stderr, "-g: '%s' not convertible to int, ignoring\n", param);
                        gracetime = DEFAULT_GRACETIME;
                }
                break;
        case 'h':
                help();
                exit(EXIT_SUCCESS);
        case 'H':
                printf("-H: setting hostname as seen from outside to '%s'\n", param);
                external_hostname = strdup(param);
                break;
        case 'i':
                interval_reregister = charstar_to_int(param);
                if (interval_reregister > 0) {
                        printf("-i: setting interval for re-registering to master server to %s minutes\n", param);
                } else {
                        printf("-i: interval for re-registering to master server set to 0, e.g. disabled\n");
                }
                break;
        case 'l':
                create_udp_server();
                break;
        case 'L':
                create_udp_server();
                lan_game_mode = 1;
                break;
        case 'm':
                max_users = charstar_to_int(param);
                if (max_users > 0 && max_users <= 255)
                        printf("-m: setting maximum users to '%d'\n", max_users);
                else {
                        fprintf(stderr, "-m: '%s' not convertible to int or not in 1..255, ignoring\n", param);
                        max_users = DEFAULT_MAX_USERS;
                }
                break;
        case 'n':
                if (strlen(param) > 12) {
                        fprintf(stderr, "-n: name is too long, maximum is 12 characters\n");
                        exit(EXIT_FAILURE);
                } else {
                        int i;
                        for (i = 0; i < strlen(param); i++) {
                                if (!((param[i] >= 'a' && param[i] <= 'z')
                                      || (param[i] >= 'A' && param[i] <= 'Z')
                                      || (param[i] >= '0' && param[i] <= '9')
                                      || param[i] == '.' || param[i] == '-')) {
                                        fprintf(stderr, "-n: name must contain only chars in [a-zA-Z0-9.-]\n");
                                        exit(EXIT_FAILURE);
                                }
                        }
                        printf("-n: setting servername to '%s'\n", param);
                        servername = strdup(param);
                }
                break;
        case 'o':
                if (streq(param, "DEBUG")) {
                        printf("-o: setting output type to DEBUG\n");
                        output_type = OUTPUT_TYPE_DEBUG;
                } else if (streq(param, "CONNECT")) {
                        printf("-o: setting output type to CONNECT\n");
                        output_type = OUTPUT_TYPE_CONNECT;
                } else if (streq(param, "INFO")) {



( run in 0.636 second using v1.01-cache-2.11-cpan-39bf76dae61 )