diff options
| author | Franklin Wei <git@fwei.tk> | 2016-02-16 21:17:46 -0500 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2016-02-16 21:17:46 -0500 |
| commit | 02de31c48c021742c6245b711790f6d853866c36 (patch) | |
| tree | d145f3ce9156de42064f8bceb3d0eb4362e3bb5a /src | |
| parent | 8c58ee885941af4c944995b029363f139b8f54bd (diff) | |
| download | netcosm-02de31c48c021742c6245b711790f6d853866c36.zip netcosm-02de31c48c021742c6245b711790f6d853866c36.tar.gz netcosm-02de31c48c021742c6245b711790f6d853866c36.tar.bz2 netcosm-02de31c48c021742c6245b711790f6d853866c36.tar.xz | |
refactor client_ module
Diffstat (limited to 'src')
| -rw-r--r-- | src/client.c | 275 | ||||
| -rw-r--r-- | src/client.h | 17 | ||||
| -rw-r--r-- | src/client_reqs.c | 286 | ||||
| -rw-r--r-- | src/client_reqs.h | 46 | ||||
| -rw-r--r-- | src/server.c | 4 |
5 files changed, 345 insertions, 283 deletions
diff --git a/src/client.c b/src/client.c index a90dc4b..7ed0df5 100644 --- a/src/client.c +++ b/src/client.c @@ -26,9 +26,9 @@ #include "telnet.h" #include "util.h" -static bool admin = false; +bool are_admin = false; -static int client_fd, to_parent, from_parent; +int client_fd, to_parent, from_parent; static room_id current_room = 0; @@ -114,63 +114,6 @@ void __attribute__((format(printf,1,2))) out(const char *fmt, ...) free(line_buf); } -static volatile sig_atomic_t request_complete; - -/* for rate-limiting */ -static int reqs_since_ts; -static time_t ts = 0; - -void send_master(unsigned char cmd, const void *data, size_t sz) -{ - if(!admin) - { - time_t t = time(NULL); - if(ts != t) - { - ts = t; - reqs_since_ts = 0; - } - if(reqs_since_ts++ > 10) - { - out("Rate limit exceeded.\n"); - return; - } - } - - request_complete = 0; - - pid_t our_pid = getpid(); - - if(!data) - sz = 0; - - /* format of child->parent packets: - * | PID | CMD | DATA | - */ - - /* pack it all into one write so it's atomic */ - char *req = malloc(sizeof(pid_t) + 1 + sz); - - memcpy(req, &our_pid, sizeof(pid_t)); - memcpy(req + sizeof(pid_t), &cmd, 1); - if(data) - memcpy(req + sizeof(pid_t) + 1, data, sz); - - assert(1 + sizeof(pid_t) + sz <= MSG_MAX); - write(to_parent, req, 1 + sizeof(pid_t) + sz); - - /* poll till we get data */ - struct pollfd pfd; - pfd.fd = from_parent; - pfd.events = POLLIN; - - poll(&pfd, 1, -1); - - while(!request_complete) poll_requests(); - - free(req); -} - #define CLIENT_READ_SZ 128 char *client_read(void) @@ -249,211 +192,6 @@ char *client_read_password(void) return ret; } -enum reqdata_typespec reqdata_type = TYPE_NONE; -union reqdata_t returned_reqdata; - -bool poll_requests(void) -{ - if(!are_child) - return false; - - bool got_cmd = false; - - while(1) - { - unsigned char packet[MSG_MAX + 1]; - memset(packet, 0, sizeof(packet)); - - ssize_t packetlen = read(from_parent, packet, MSG_MAX); - - unsigned char *data = packet + 1; - size_t datalen = packetlen - 1; - packet[MSG_MAX] = '\0'; - - /* no data yet */ - if(packetlen < 0) - goto fail; - - /* parent closed pipe */ - if(!packetlen) - { - debugf("master process died\n"); - exit(0); - } - - got_cmd = true; - - unsigned char cmd = packet[0]; - - switch(cmd) - { - case REQ_BCASTMSG: - { - out((char*)data, datalen); - break; - } - case REQ_KICK: - - { - out((char*)data, datalen); - exit(EXIT_SUCCESS); - } - case REQ_MOVE: - { - int status = *((int*)data); - - reqdata_type = TYPE_BOOLEAN; - returned_reqdata.boolean = status; - if(!status) - out("You cannot go that way.\n"); - break; - } - case REQ_GETUSERDATA: - { - if(datalen == sizeof(struct userdata_t)) - reqdata_type = TYPE_USERDATA; - else - break; - - struct userdata_t *user = &returned_reqdata.userdata; - *user = *((struct userdata_t*)data); - break; - } - case REQ_DELUSERDATA: - { - reqdata_type = TYPE_BOOLEAN; - returned_reqdata.boolean = *((bool*)data); - break; - } - case REQ_ADDUSERDATA: - { - reqdata_type = TYPE_BOOLEAN; - returned_reqdata.boolean = *((bool*)data); - break; - } - case REQ_NOP: - break; - case REQ_PRINTNEWLINE: - { - out("\n"); - break; - } - case REQ_ALLDONE: - request_complete = 1; - return true; - default: - debugf("WARNING: client process received unknown code %d\n", cmd); - break; - } - } -fail: - - return got_cmd; -} - -void client_change_state(int state) -{ - send_master(REQ_CHANGESTATE, &state, sizeof(state)); -} - -void client_change_user(const char *user) -{ - send_master(REQ_CHANGEUSER, user, strlen(user) + 1); -} - -void client_change_room(room_id id) -{ - send_master(REQ_SETROOM, &id, sizeof(id)); -} - -void *dir_map = NULL; - -bool client_move(const char *dir) -{ - const struct dir_pair { - const char *text; - enum direction_t val; - } dirs[] = { - { "N", DIR_N }, - { "NORTH", DIR_N }, - { "NE", DIR_NE }, - { "NORTHEAST", DIR_N }, - { "E", DIR_E }, - { "EAST", DIR_E }, - { "SE", DIR_SE }, - { "SOUTHEAST", DIR_SE }, - { "S", DIR_S }, - { "SOUTH", DIR_S }, - { "SW", DIR_SW }, - { "SOUTHWEST", DIR_SW }, - { "W", DIR_W }, - { "WEST", DIR_W }, - { "NW", DIR_NW }, - { "NORTHWEST", DIR_NW }, - { "U", DIR_UP }, - { "UP", DIR_UP }, - { "D", DIR_DN }, - { "DOWN", DIR_DN }, - { "IN", DIR_IN }, - { "OUT", DIR_OT }, - }; - - if(!dir_map) - { - dir_map = hash_init(ARRAYLEN(dirs), hash_djb, compare_strings); - hash_insert_pairs(dir_map, (struct hash_pair*)dirs, sizeof(struct dir_pair), ARRAYLEN(dirs)); - } - - struct dir_pair *pair = hash_lookup(dir_map, dir); - if(pair) - { - send_master(REQ_MOVE, &pair->val, sizeof(pair->val)); - if(reqdata_type == TYPE_BOOLEAN && returned_reqdata.boolean) - return true; - else - return false; - } - else - { - out("Unknown direction.\n"); - return false; - } -} - -void client_look(void) -{ - send_master(REQ_GETROOMNAME, NULL, 0); - out("\n"); - send_master(REQ_GETROOMDESC, NULL, 0); -} - -void client_look_at(char *obj) -{ - all_lower(obj); - send_master(REQ_LOOKAT, obj, strlen(obj) + 1); -} - -void client_take(char *obj) -{ - all_lower(obj); - send_master(REQ_TAKE, obj, strlen(obj) + 1); -} - -void client_inventory(void) -{ - send_master(REQ_PRINTINVENTORY, NULL, 0); -} - -void client_drop(char *what) -{ - send_master(REQ_DROP, what, strlen(what) + 1); -} - -void client_user_list(void) -{ - send_master(REQ_LISTUSERS, NULL, 0); -} - #define WSPACE " \t\r\n" #define CMD_OK 0 @@ -721,7 +459,6 @@ void client_shutdown(void) cmd_map = NULL; } - void client_main(int fd, struct sockaddr_in *addr, int total, int to, int from) { client_fd = fd; @@ -797,8 +534,8 @@ auth: if(authlevel == PRIV_NONE) return; - admin = (authlevel == PRIV_ADMIN); - if(admin) + are_admin = (authlevel == PRIV_ADMIN); + if(are_admin) client_change_state(STATE_ADMIN); else client_change_state(STATE_LOGGEDIN); @@ -827,7 +564,7 @@ auth: all_upper(tok); const struct client_cmd *cmd = hash_lookup(cmd_map, tok); - if(cmd && cmd->cb && (!cmd->admin_only || (cmd->admin_only && admin))) + if(cmd && cmd->cb && (!cmd->admin_only || (cmd->admin_only && are_admin))) { int ret = cmd->cb(&save); switch(ret) @@ -846,7 +583,7 @@ auth: error("client: bad callback return value"); } } - else if(cmd && cmd->admin_only && !admin) + else if(cmd && cmd->admin_only && !are_admin) { out("You are not allowed to do that.\n"); goto next_cmd; diff --git a/src/client.h b/src/client.h index 4ab7f9b..5b5c5ff 100644 --- a/src/client.h +++ b/src/client.h @@ -19,25 +19,18 @@ /* You should use #pragma once everywhere. */ #pragma once +#include "globals.h" + +#include "client_reqs.h" #include "room.h" #include "userdb.h" -enum reqdata_typespec { TYPE_NONE = 0, TYPE_USERDATA, TYPE_BOOLEAN } reqdata_type; - -union reqdata_t { - struct userdata_t userdata; - bool boolean; -}; - -extern enum reqdata_typespec reqdata_type; -extern union reqdata_t returned_reqdata; +extern int client_fd, to_parent, from_parent; +extern bool are_admin; /* call from child process ONLY */ void send_master(unsigned char cmd, const void *data, size_t sz); -/* the master sends the child SIGRTMIN+0 */ -void sig_rt_0_handler(int s, siginfo_t *info, void *v); - void out(const char *fmt, ...) __attribute__((format(printf,1,2))); void out_raw(const void*, size_t); diff --git a/src/client_reqs.c b/src/client_reqs.c new file mode 100644 index 0000000..39a67f9 --- /dev/null +++ b/src/client_reqs.c @@ -0,0 +1,286 @@ +/* + * NetCosm - a MUD server + * Copyright (C) 2016 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "globals.h" + +#include "client.h" +#include "hash.h" + +enum reqdata_typespec reqdata_type = TYPE_NONE; +union reqdata_t returned_reqdata; + +static int request_complete; + +/* for rate-limiting */ +static int reqs_since_ts; +static time_t ts = 0; + +bool poll_requests(void) +{ + if(!are_child) + return false; + + bool got_cmd = false; + + while(1) + { + unsigned char packet[MSG_MAX + 1]; + memset(packet, 0, sizeof(packet)); + + ssize_t packetlen = read(from_parent, packet, MSG_MAX); + + unsigned char *data = packet + 1; + size_t datalen = packetlen - 1; + packet[MSG_MAX] = '\0'; + + /* no data yet */ + if(packetlen < 0) + goto fail; + + /* parent closed pipe */ + if(!packetlen) + { + debugf("master process died\n"); + exit(0); + } + + got_cmd = true; + + unsigned char cmd = packet[0]; + + switch(cmd) + { + case REQ_BCASTMSG: + { + out((char*)data, datalen); + break; + } + case REQ_KICK: + + { + out((char*)data, datalen); + exit(EXIT_SUCCESS); + } + case REQ_MOVE: + { + int status = *((int*)data); + + reqdata_type = TYPE_BOOLEAN; + returned_reqdata.boolean = status; + if(!status) + out("You cannot go that way.\n"); + break; + } + case REQ_GETUSERDATA: + { + if(datalen == sizeof(struct userdata_t)) + reqdata_type = TYPE_USERDATA; + else + break; + + struct userdata_t *user = &returned_reqdata.userdata; + *user = *((struct userdata_t*)data); + break; + } + case REQ_DELUSERDATA: + { + reqdata_type = TYPE_BOOLEAN; + returned_reqdata.boolean = *((bool*)data); + break; + } + case REQ_ADDUSERDATA: + { + reqdata_type = TYPE_BOOLEAN; + returned_reqdata.boolean = *((bool*)data); + break; + } + case REQ_NOP: + break; + case REQ_PRINTNEWLINE: + { + out("\n"); + break; + } + case REQ_ALLDONE: + request_complete = 1; + return true; + default: + debugf("WARNING: client process received unknown code %d\n", cmd); + break; + } + } +fail: + + return got_cmd; +} + +void client_change_state(int state) +{ + send_master(REQ_CHANGESTATE, &state, sizeof(state)); +} + +void client_change_user(const char *user) +{ + send_master(REQ_CHANGEUSER, user, strlen(user) + 1); +} + +void client_change_room(room_id id) +{ + send_master(REQ_SETROOM, &id, sizeof(id)); +} + +void send_master(unsigned char cmd, const void *data, size_t sz) +{ + if(!are_admin) + { + time_t t = time(NULL); + if(ts != t) + { + ts = t; + reqs_since_ts = 0; + } + if(reqs_since_ts++ > 10) + { + out("Rate limit exceeded.\n"); + return; + } + } + + request_complete = 0; + + pid_t our_pid = getpid(); + + if(!data) + sz = 0; + + /* + * format of child->parent packets: + * | PID | CMD | DATA | + */ + + /* pack it all into one write so it's atomic */ + char *req = malloc(sizeof(pid_t) + 1 + sz); + + memcpy(req, &our_pid, sizeof(pid_t)); + memcpy(req + sizeof(pid_t), &cmd, 1); + if(data) + memcpy(req + sizeof(pid_t) + 1, data, sz); + + assert(1 + sizeof(pid_t) + sz <= MSG_MAX); + write(to_parent, req, 1 + sizeof(pid_t) + sz); + + /* poll till we get data */ + struct pollfd pfd; + pfd.fd = from_parent; + pfd.events = POLLIN; + + poll(&pfd, 1, -1); + + while(!request_complete) poll_requests(); + + free(req); +} + +/* freed by server_cleanup */ +void *dir_map = NULL; + +bool client_move(const char *dir) +{ + const struct dir_pair { + const char *text; + enum direction_t val; + } dirs[] = { + { "N", DIR_N }, + { "NORTH", DIR_N }, + { "NE", DIR_NE }, + { "NORTHEAST", DIR_N }, + { "E", DIR_E }, + { "EAST", DIR_E }, + { "SE", DIR_SE }, + { "SOUTHEAST", DIR_SE }, + { "S", DIR_S }, + { "SOUTH", DIR_S }, + { "SW", DIR_SW }, + { "SOUTHWEST", DIR_SW }, + { "W", DIR_W }, + { "WEST", DIR_W }, + { "NW", DIR_NW }, + { "NORTHWEST", DIR_NW }, + { "U", DIR_UP }, + { "UP", DIR_UP }, + { "D", DIR_DN }, + { "DOWN", DIR_DN }, + { "IN", DIR_IN }, + { "OUT", DIR_OT }, + }; + + if(!dir_map) + { + dir_map = hash_init(ARRAYLEN(dirs), hash_djb, compare_strings); + hash_insert_pairs(dir_map, (struct hash_pair*)dirs, sizeof(struct dir_pair), ARRAYLEN(dirs)); + } + + struct dir_pair *pair = hash_lookup(dir_map, dir); + if(pair) + { + send_master(REQ_MOVE, &pair->val, sizeof(pair->val)); + if(reqdata_type == TYPE_BOOLEAN && returned_reqdata.boolean) + return true; + else + return false; + } + else + { + out("Unknown direction.\n"); + return false; + } +} + +void client_look(void) +{ + send_master(REQ_GETROOMNAME, NULL, 0); + out("\n"); + send_master(REQ_GETROOMDESC, NULL, 0); +} + +void client_look_at(char *obj) +{ + all_lower(obj); + send_master(REQ_LOOKAT, obj, strlen(obj) + 1); +} + +void client_take(char *obj) +{ + all_lower(obj); + send_master(REQ_TAKE, obj, strlen(obj) + 1); +} + +void client_inventory(void) +{ + send_master(REQ_PRINTINVENTORY, NULL, 0); +} + +void client_drop(char *what) +{ + send_master(REQ_DROP, what, strlen(what) + 1); +} + +void client_user_list(void) +{ + send_master(REQ_LISTUSERS, NULL, 0); +} diff --git a/src/client_reqs.h b/src/client_reqs.h new file mode 100644 index 0000000..979da9a --- /dev/null +++ b/src/client_reqs.h @@ -0,0 +1,46 @@ +/* + * NetCosm - a MUD server + * Copyright (C) 2016 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "globals.h" + +#include "server.h" +#include "server_reqs.h" +#include "userdb.h" + +enum reqdata_typespec { TYPE_NONE = 0, TYPE_USERDATA, TYPE_BOOLEAN } reqdata_type; + +union reqdata_t { + struct userdata_t userdata; + bool boolean; +}; + +extern enum reqdata_typespec reqdata_type; +extern union reqdata_t returned_reqdata; + +void client_change_room(room_id id); +void client_change_user(const char *user); +void client_change_state(int state); +bool client_move(const char *dir); +void client_look(void); +void client_look_at(char *obj); +void client_inventory(void); +void client_drop(char *what); +void client_user_list(void); +void client_take(char *obj); diff --git a/src/server.c b/src/server.c index 43afd69..5f2478a 100644 --- a/src/server.c +++ b/src/server.c @@ -106,7 +106,7 @@ static void handle_client(int fd, struct sockaddr_in *addr, client_main(fd, addr, nclients, to, from); } -static void __attribute__((noreturn)) serv_cleanup(void) +static void __attribute__((noreturn)) server_cleanup(void) { if(!are_child) debugf("Shutdown server.\n"); @@ -427,7 +427,7 @@ int server_main(int argc, char *argv[]) ev_io_start(EV_A_ &server_watcher); - atexit(serv_cleanup); + atexit(server_cleanup); /* everything's ready, hand it over to libev */ ev_loop(loop, 0); |