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 )