diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/auth.c | 1 | ||||
| -rw-r--r-- | src/client.c | 500 | ||||
| -rw-r--r-- | src/client.h | 4 | ||||
| -rw-r--r-- | src/obj.c | 4 | ||||
| -rw-r--r-- | src/server.c | 19 | ||||
| -rw-r--r-- | src/server_reqs.c | 5 | ||||
| -rw-r--r-- | src/telnet.c | 13 | ||||
| -rw-r--r-- | src/telnet.h | 2 | ||||
| -rw-r--r-- | src/userdb.h | 3 |
9 files changed, 330 insertions, 221 deletions
@@ -75,6 +75,7 @@ static void add_user_internal(const char *name, const char *pass, int authlevel) /* doesn't need to be malloc'd */ struct userdata_t userdata; + memset(&userdata, 0, sizeof(userdata)); strncpy(userdata.username, name, sizeof(userdata.username)); memcpy(userdata.passhash, hexhash, sizeof(userdata.passhash)); diff --git a/src/client.c b/src/client.c index 1de6818..62b9fbe 100644 --- a/src/client.c +++ b/src/client.c @@ -388,6 +388,7 @@ bool client_move(const char *dir) { "IN", DIR_IN }, { "OUT", DIR_OT }, }; + if(!dir_map) { dir_map = hash_init(ARRAYLEN(dirs), hash_djb, compare_strings); @@ -446,6 +447,272 @@ void client_user_list(void) #define WSPACE " \t\r\n" +#define CMD_OK 0 +#define CMD_LOGOUT 1 +#define CMD_QUIT 2 + +/*** callbacks ***/ + +int user_cb(char **save) +{ + char *what = strtok_r(NULL, WSPACE, save); + if(!what) + return CMD_OK; + + all_upper(what); + + if(!strcmp(what, "DEL")) + { + char *user = strtok_r(NULL, WSPACE, save); + if(user) + { + if(strcmp(user, current_user) && auth_user_del(user)) + out("Success.\n"); + else + out("Failure.\n"); + } + else + { + out("Usage: USER DEL <USERNAME>\n"); + } + } + else if(!strcmp(what, "ADD") || !strcmp(what, "MODIFY")) + { + char *user = strtok_r(NULL, WSPACE, save); + if(user) + { + if(!strcmp(user, current_user)) + { + out("Do not modify your own password using USER. User CHPASS instead.\n"); + return CMD_OK; + } + + out("Editing user '%s'\n", user); + + out("New Password (_DO_NOT_USE_A_VALUABLE_PASSWORD_): "); + + /* BAD BAD BAD BAD BAD BAD BAD CLEARTEXT PASSWORDS!!! */ + char *pass = client_read_password(); + + out("Verify Password: "); + char *pass2 = client_read_password(); + + if(strcmp(pass, pass2)) + { + memset(pass, 0, strlen(pass)); + memset(pass2, 0, strlen(pass2)); + free(pass); + free(pass2); + out("Failure.\n"); + return CMD_OK; + } + + out("Admin privileges [y/N]? "); + char *allow_admin = client_read(); + int priv = PRIV_USER; + if(toupper(allow_admin[0]) == 'Y') + priv = PRIV_ADMIN; + + free(allow_admin); + + if(auth_user_add(user, pass, priv)) + out("Success.\n"); + else + out("Failure.\n"); + memset(pass, 0, strlen(pass)); + free(pass); + } + else + out("Usage: USER <ADD|MODIFY> <USERNAME>\n"); + } + else if(!strcmp(what, "LIST")) + { + client_user_list(); + } + else + { + out("Usage: USER <ADD|DEL|MODIFY|LIST> <ARGS>\n"); + } + + return CMD_OK; +} + +int client_cb(char **save) +{ + char *what = strtok_r(NULL, WSPACE, save); + if(!what) + { + out("Usage: CLIENT <LIST|KICK> <PID>\n"); + return CMD_OK; + } + + all_upper(what); + + if(!strcmp(what, "LIST")) + { + send_master(REQ_LISTCLIENTS, NULL, 0); + } + else if(!strcmp(what, "KICK")) + { + char *pid_s = strtok_r(NULL, WSPACE, save); + all_upper(pid_s); + if(pid_s) + { + if(!strcmp(pid_s, "ALL")) + { + const char *msg = "Kicking everyone...\n"; + send_master(REQ_KICKALL, msg, strlen(msg)); + return CMD_OK; + } + /* weird pointer voodoo */ + /* TODO: simplify */ + char pidbuf[MAX(sizeof(pid_t), MSG_MAX)]; + char *end; + pid_t pid = strtol(pid_s, &end, 0); + if(pid == getpid()) + { + out("You cannot kick yourself. Use EXIT instead.\n"); + return CMD_OK; + } + else if(*end != '\0') + { + out("Expected a child PID after KICK.\n"); + return CMD_OK; + } + memcpy(pidbuf, &pid, sizeof(pid)); + int len = sizeof(pid_t) + snprintf(pidbuf + sizeof(pid_t), + sizeof(pidbuf) - sizeof(pid_t), + "You were kicked.\n"); + send_master(REQ_KICK, pidbuf, len); + debugf("Success.\n"); + } + else + out("Usage: CLIENT KICK <PID>\n"); + } + return CMD_OK; +} + +int quit_cb(char **save) +{ + (void) save; + return CMD_QUIT; +} + +int say_cb(char **save) +{ + char buf[MSG_MAX]; + char *what = strtok_r(NULL, "", save); + int len = snprintf(buf, sizeof(buf), "%s says %s\n", current_user, what); + + send_master(REQ_BCASTMSG, buf, len); + return CMD_OK; +} + +int date_cb(char **save) +{ + (void) save; + time_t t = time(NULL); + out("%s", ctime(&t)); + return CMD_OK; +} + +int logout_cb(char **save) +{ + (void) save; + out("Logged out.\n"); + telnet_clear_screen(); + return CMD_LOGOUT; +} + +int look_cb(char **save) +{ + char *what = strtok_r(NULL, " ", save); + if(!what) + client_look(); + else + { + client_look_at(what); + } + return CMD_OK; +} + +int inventory_cb(char **save) +{ + (void) save; + client_inventory(); + return CMD_OK; +} + +int take_cb(char **save) +{ + char *what = strtok_r(NULL, " ", save); + client_take(what); + return CMD_OK; +} + +int wait_cb(char **save) +{ + (void) save; + send_master(REQ_WAIT, NULL, 0); + return CMD_OK; +} + +int go_cb(char **save) +{ + char *dir = strtok_r(NULL, WSPACE, save); + if(dir) + { + all_upper(dir); + if(client_move(dir)) + client_look(); + } + else + out("Expected direction after GO.\n"); + return CMD_OK; +} + +int drop_cb(char **save) +{ + char *what = strtok_r(NULL, " ", save); + client_drop(what); + return CMD_OK; +} + +static const struct client_cmd { + const char *cmd; + int (*cb)(char **saveptr); + bool admin_only; +} cmds[] = { + { "USER", user_cb, true }, + { "CLIENT", client_cb, true }, + { "EXIT", quit_cb, false }, + { "QUIT", quit_cb, false }, + { "SAY", say_cb, false }, + { "DATE", date_cb, false }, + { "LOGOUT", logout_cb, false }, + { "LOOK", look_cb, false }, + { "INVENTORY", inventory_cb, false }, + { "TAKE", take_cb, false }, + { "WAIT", wait_cb, false }, + { "GO", go_cb, false }, + { "DROP", drop_cb, false }, +}; + +static void *cmd_map = NULL; + +void client_init(void) +{ + cmd_map = hash_init(ARRAYLEN(cmds), hash_djb, compare_strings); + hash_insert_pairs(cmd_map, (const struct hash_pair*)cmds, sizeof(cmds[0]), ARRAYLEN(cmds)); +} + +void client_shutdown(void) +{ + hash_free(cmd_map); + cmd_map = NULL; +} + + void client_main(int fd, struct sockaddr_in *addr, int total, int to, int from) { client_fd = fd; @@ -539,232 +806,47 @@ auth: while(1) { out(">> "); - char *cmd = client_read(); - char *orig = strdup(cmd); + char *line = client_read(); + char *orig = strdup(line); char *save = NULL; - char *tok = strtok_r(cmd, WSPACE, &save); + char *tok = strtok_r(line, WSPACE, &save); if(!tok) goto next_cmd; - all_upper(tok); - - if(admin) - { - if(!strcmp(tok, "USER")) - { - char *what = strtok_r(NULL, WSPACE, &save); - if(!what) - goto next_cmd; - all_upper(what); - - if(!strcmp(what, "DEL")) - { - char *user = strtok_r(NULL, WSPACE, &save); - if(user) - { - if(strcmp(user, current_user) && auth_user_del(user)) - out("Success.\n"); - else - out("Failure.\n"); - } - else - { - out("Usage: USER DEL <USERNAME>\n"); - } - } - else if(!strcmp(what, "ADD") || !strcmp(what, "MODIFY")) - { - char *user = strtok_r(NULL, WSPACE, &save); - if(user) - { - if(!strcmp(user, current_user)) - { - out("Do not modify your own password using USER. User CHPASS instead.\n"); - goto next_cmd; - } - - out("Editing user '%s'\n", user); - - out("New Password (_DO_NOT_USE_A_VALUABLE_PASSWORD_): "); - - /* BAD BAD BAD BAD BAD BAD BAD CLEARTEXT PASSWORDS!!! */ - char *pass = client_read_password(); - - out("Verify Password: "); - char *pass2 = client_read_password(); - - if(strcmp(pass, pass2)) - { - memset(pass, 0, strlen(pass)); - memset(pass2, 0, strlen(pass2)); - free(pass); - free(pass2); - out("Failure.\n"); - goto next_cmd; - } - - out("Admin privileges [y/N]? "); - char *allow_admin = client_read(); - int priv = PRIV_USER; - if(toupper(allow_admin[0]) == 'Y') - priv = PRIV_ADMIN; - - free(allow_admin); - - if(auth_user_add(user, pass, priv)) - out("Success.\n"); - else - out("Failure.\n"); - memset(pass, 0, strlen(pass)); - free(pass); - } - else - out("Usage: USER <ADD|MODIFY> <USERNAME>\n"); - } - else if(!strcmp(what, "LIST")) - { - client_user_list(); - } - else - { - out("Usage: USER <ADD|DEL|MODIFY|LIST> <ARGS>\n"); - } - } - else if(!strcmp(tok, "CLIENT")) - { - char *what = strtok_r(NULL, WSPACE, &save); - if(!what) - { - out("Usage: CLIENT <LIST|KICK> <PID>\n"); - goto next_cmd; - } - - all_upper(what); - - if(!strcmp(what, "LIST")) - { - send_master(REQ_LISTCLIENTS, NULL, 0); - } - else if(!strcmp(what, "KICK")) - { - char *pid_s = strtok_r(NULL, WSPACE, &save); - all_upper(pid_s); - if(pid_s) - { - if(!strcmp(pid_s, "ALL")) - { - const char *msg = "Kicking everyone...\n"; - send_master(REQ_KICKALL, msg, strlen(msg)); - goto next_cmd; - } - /* weird pointer voodoo */ - /* TODO: simplify */ - char pidbuf[MAX(sizeof(pid_t), MSG_MAX)]; - char *end; - pid_t pid = strtol(pid_s, &end, 0); - if(pid == getpid()) - { - out("You cannot kick yourself. Use EXIT instead.\n"); - goto next_cmd; - } - else if(*end != '\0') - { - out("Expected a child PID after KICK.\n"); - goto next_cmd; - } - memcpy(pidbuf, &pid, sizeof(pid)); - int len = sizeof(pid_t) + snprintf(pidbuf + sizeof(pid_t), - sizeof(pidbuf) - sizeof(pid_t), - "You were kicked.\n"); - send_master(REQ_KICK, pidbuf, len); - debugf("Success.\n"); - } - else - out("Usage: CLIENT KICK <PID>\n"); - } - } - //else if(!strcmp(tok, "HANG")) - //{ - // send_master(REQ_HANG); - //} - } - - /* unprivileged commands */ - if(!strcmp(tok, "QUIT") || !strcmp(tok, "EXIT")) - { - free(cmd); - goto done; - } - else if(!strcmp(tok, "SAY")) - { - char buf[MSG_MAX]; - char *what = strtok_r(NULL, "", &save); - int len = snprintf(buf, sizeof(buf), "%s says %s\n", current_user, what); + all_upper(tok); - send_master(REQ_BCASTMSG, buf, len); - } - else if(!strcmp(tok, "DATE")) + const struct client_cmd *cmd = hash_lookup(cmd_map, tok); + if(cmd && cmd->cb && (!cmd->admin_only || (cmd->admin_only && admin))) { - time_t t = time(NULL); - out("%s", ctime(&t)); - } - else if(!strcmp(tok, "LOGOUT")) - { - out("Logged out.\n"); - goto auth; - } - else if(!strcmp(tok, "LOOK")) - { - char *what = strtok_r(NULL, " ", &save); - if(!what) - client_look(); - else - { - client_look_at(what); - } - } - else if(!strcmp(tok, "INVENTORY")) - { - client_inventory(); - } - else if(!strcmp(tok, "TAKE")) - { - char *what = strtok_r(NULL, " ", &save); - client_take(what); - } - else if(!strcmp(tok, "WAIT")) - { - send_master(REQ_WAIT, NULL, 0); - } - else if(!strcmp(tok, "GO")) - { - char *dir = strtok_r(NULL, WSPACE, &save); - if(dir) + int ret = cmd->cb(&save); + switch(ret) { - all_upper(dir); - if(client_move(dir)) - client_look(); + case CMD_OK: + goto next_cmd; + case CMD_LOGOUT: + goto auth; + case CMD_QUIT: + free(line); + free(orig); + goto done; + default: + error("client: bad callback return value"); } - else - out("Expected direction after GO.\n"); } - else if(!strcmp(tok, "DROP")) + else if(cmd && cmd->admin_only && !admin) { - char *what = strtok_r(NULL, " ", &save); - client_drop(what); + out("You are not allowed to do that.\n"); + goto next_cmd; } - else - { - /* we can't handle it, send it to the master */ - send_master(REQ_EXECVERB, orig, strlen(orig) + 1); - } + /* if we can't handle it, let the master process try */ + send_master(REQ_EXECVERB, orig, strlen(orig) + 1); next_cmd: - free(cmd); + free(line); free(orig); } diff --git a/src/client.h b/src/client.h index eeb515d..4ab7f9b 100644 --- a/src/client.h +++ b/src/client.h @@ -43,3 +43,7 @@ void out_raw(const void*, size_t); /* called for every client */ void client_main(int sock, struct sockaddr_in *addr, int, int to_parent, int from_parent); + +/* can (and should) be called before forking the child */ +void client_init(void); +void client_shutdown(void); @@ -82,18 +82,20 @@ struct object_t *obj_read(int fd) struct object_t *obj_dup(struct object_t *obj) { + debugf("Adding an object reference.\n"); ++obj->refcount; return obj; } void obj_free(void *ptr) { + debugf("Freeing an object reference.\n"); struct object_t *obj = ptr; - debugf("Freeing obj %s\n", obj->name); --obj->refcount; if(!obj->refcount) { + debugf("Freeing obj %s\n", obj->name); if(obj->class->hook_destroy) obj->class->hook_destroy(obj); diff --git a/src/server.c b/src/server.c index d65a3f1..0b1569d 100644 --- a/src/server.c +++ b/src/server.c @@ -118,6 +118,7 @@ static void __attribute__((noreturn)) serv_cleanup(void) close(server_socket); /* shut down modules */ + client_shutdown(); obj_shutdown(); reqmap_free(); userdb_shutdown(); @@ -380,28 +381,32 @@ static void parse_args(int argc, char *argv[]) static SIMP_HASH(pid_t, pid_hash); static SIMP_EQUAL(pid_t, pid_equal); -int server_main(int argc, char *argv[]) +static void check_libs(void) { debugf("*** Starting NetCosm %s (libev %d.%d, %s) ***\n", NETCOSM_VERSION, EV_VERSION_MAJOR, EV_VERSION_MINOR, OPENSSL_VERSION_TEXT); - assert(ev_version_major() == EV_VERSION_MAJOR && ev_version_minor() >= EV_VERSION_MINOR); +} - parse_args(argc, argv); +int server_main(int argc, char *argv[]) +{ + check_libs(); - srand(time(0)); + parse_args(argc, argv); server_socket = server_bind(); userdb_init(USERFILE); check_userfile(); - load_worldfile(); reqmap_init(); + /* save some time after a fork() */ + client_init(); + /* this initial size very low to make iteration faster */ child_map = hash_init(16, pid_hash, pid_equal); hash_setfreedata_cb(child_map, free_child_data); @@ -409,7 +414,7 @@ int server_main(int argc, char *argv[]) debugf("Listening on port %d\n", port); - struct ev_loop *loop = EV_DEFAULT; + struct ev_loop *loop = ev_default_loop(0); /* we initialize signals after creating the default event loop * because libev grabs SIGCHLD */ @@ -418,10 +423,12 @@ int server_main(int argc, char *argv[]) ev_io server_watcher; ev_io_init(&server_watcher, new_connection_cb, server_socket, EV_READ); ev_set_priority(&server_watcher, EV_MAXPRI); + ev_io_start(EV_A_ &server_watcher); atexit(serv_cleanup); + /* everything's ready, hand it over to libev */ ev_loop(loop, 0); /* should never get here */ diff --git a/src/server_reqs.c b/src/server_reqs.c index fd1ef19..900ff87 100644 --- a/src/server_reqs.c +++ b/src/server_reqs.c @@ -374,9 +374,8 @@ static void req_listusers(unsigned char *data, size_t datalen, struct child_data if(!user) break; - send_msg(sender, "%s: priv: %d room: %d last: %s", user->username, + send_msg(sender, "%s: priv: %d last: %s", user->username, user->priv, - user->room, ctime(&user->last_login)); } } @@ -404,7 +403,7 @@ static void req_execverb(unsigned char *data, size_t datalen, struct child_data if(verb) goto exec_verb; - send_msg(sender, "I don't know how to do that.\n"); + send_msg(sender, "I don't know what that means.\n"); return; char *args; diff --git a/src/telnet.c b/src/telnet.c index 743eb53..9932d84 100644 --- a/src/telnet.c +++ b/src/telnet.c @@ -16,8 +16,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#define TELCMDS +#define TELCMDS /* see <arpa/telnet.h> */ #define TELOPTS + #include "globals.h" #include "client.h" @@ -132,3 +133,13 @@ void telnet_init(void) out_raw(init_seq, ARRAYLEN(init_seq)); } + +void telnet_clear_screen(void) +{ + /* ESC ] 2 J */ + unsigned char clear_seq[] = { '\033', + ']', + '2', + 'J' }; + out_raw(clear_seq, ARRAYLEN(clear_seq)); +} diff --git a/src/telnet.h b/src/telnet.h index 8669e87..2f40c51 100644 --- a/src/telnet.h +++ b/src/telnet.h @@ -34,3 +34,5 @@ uint16_t telnet_get_height(void); void telnet_echo_on(void); void telnet_echo_off(void); + +void telnet_clear_screen(void); diff --git a/src/userdb.h b/src/userdb.h index 1d1d6e2..4804df9 100644 --- a/src/userdb.h +++ b/src/userdb.h @@ -34,7 +34,8 @@ struct userdata_t { char passhash[AUTH_HASHLEN * 2 + 1]; priv_t priv; - room_id room; + + //room_id room; time_t last_login; void *objects; /* hash of object names -> objects */ |