diff options
| author | Franklin Wei <git@fwei.tk> | 2016-01-08 22:27:43 -0500 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2016-01-08 22:27:43 -0500 |
| commit | 50134c46dc337c35b8ecf78b3c5b4308cf8fb791 (patch) | |
| tree | 89d73bca1951129ec7e15b642e2fa6c85567f872 /src | |
| parent | 2819d11ceeb1ac739ed5f17ccb0abab63f494299 (diff) | |
| download | netcosm-50134c46dc337c35b8ecf78b3c5b4308cf8fb791.zip netcosm-50134c46dc337c35b8ecf78b3c5b4308cf8fb791.tar.gz netcosm-50134c46dc337c35b8ecf78b3c5b4308cf8fb791.tar.bz2 netcosm-50134c46dc337c35b8ecf78b3c5b4308cf8fb791.tar.xz | |
some stuff
Diffstat (limited to 'src')
| -rw-r--r-- | src/auth.c | 170 | ||||
| -rw-r--r-- | src/auth.h | 12 | ||||
| -rw-r--r-- | src/child_reqs.h | 12 | ||||
| -rw-r--r-- | src/client.c | 89 | ||||
| -rw-r--r-- | src/hash.c | 4 | ||||
| -rw-r--r-- | src/hash.h | 2 | ||||
| -rw-r--r-- | src/netcosm.h | 55 | ||||
| -rw-r--r-- | src/room.c | 2 | ||||
| -rw-r--r-- | src/server.c | 401 | ||||
| -rw-r--r-- | src/server_reqs.c | 425 | ||||
| -rw-r--r-- | src/server_reqs.h | 50 | ||||
| -rw-r--r-- | src/telnet.c | 2 | ||||
| -rw-r--r-- | src/telnet.h | 2 | ||||
| -rw-r--r-- | src/userdb.c | 68 | ||||
| -rw-r--r-- | src/userdb.h | 5 | ||||
| -rw-r--r-- | src/util.c | 2 |
16 files changed, 693 insertions, 608 deletions
@@ -1,6 +1,6 @@ /* * NetCosm - a MUD server - * Copyright (C) 2015 Franklin Wei + * 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 @@ -74,7 +74,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; - userdata.username = (char*)name; + strncpy(userdata.username, name, sizeof(userdata.username)); memcpy(userdata.passhash, hexhash, sizeof(userdata.passhash)); @@ -84,57 +84,7 @@ static void add_user_internal(const char *name, const char *pass, int authlevel) memcpy(userdata.salt, salt, sizeof(salt)); - userdb_add(&userdata); -} - -/* writes the contents of USERFILE to a temp file, and return its path, which is statically allocated */ -static int remove_user_internal(const char *user, int *found, char **filename) -{ - FILE *in_fd = fopen(USERFILE, "a+"); - static char tmp[32]; - const char *template = "userlist_tmp.XXXXXX"; - strncpy(tmp, template, sizeof(tmp)); - - int out_fd = mkstemp(tmp); - - if(found) - *found = 0; - if(filename) - *filename = tmp; - - while(1) - { - char *line = NULL; - char *junk; - size_t buflen = 0; - ssize_t len = getline(&line, &buflen, in_fd); - - /* getline's return value is the actual length of the line read */ - /* it's second argument in fact stores the length of the /buffer/, not the line */ - if(len < 0) - { - free(line); - break; - } - - char *old = strdup(line); - - char *user_on_line = strtok_r(line, ":\r\n", &junk); - - if(strcmp(user_on_line, user) != 0) - { - write(out_fd, old, len); - } - else - if(found) - (*found)++; - free(line); - free(old); - } - - fclose(in_fd); - - return out_fd; + userdb_request_add(&userdata); } bool auth_user_del(const char *user2) @@ -142,22 +92,7 @@ bool auth_user_del(const char *user2) char *user = strdup(user2); remove_cruft(user); if(valid_login_name(user)) - { - int found = 0; - char *tmp; - remove_user_internal(user, &found, &tmp); - free(user); - if(found) - { - rename(tmp, USERFILE); - return true; - } - else - { - remove(tmp); - return false; - } - } + return userdb_request_remove(user); else { free(user); @@ -193,20 +128,15 @@ bool auth_user_add(const char *user2, const char *pass2, int level) static bool valid_login_name(const char *name) { + size_t len = 0; while(*name) { char c = *name++; - switch(c) - { - case ' ': - case ':': - case '\n': - case '\r': - case '\t': + ++len; + if(len > MAX_NAME_LEN) + return false; + if(!(isalpha(c) || isdigit(c))) return false; - default: - break; - } } return true; } @@ -243,7 +173,7 @@ void first_run_setup(void) free(admin_pass); } -struct authinfo_t auth_check(const char *name2, const char *pass2) +struct userdata_t *auth_check(const char *name2, const char *pass2) { /* get our own copy to remove newlines */ char *name = strdup(name2); @@ -252,80 +182,28 @@ struct authinfo_t auth_check(const char *name2, const char *pass2) remove_cruft(pass); /* find it in the user list */ + struct userdata_t *data = userdb_request_lookup(name); + char *salt = data->salt, *hash = data->passhash; - FILE *f = fopen(USERFILE, "r"); - - flock(fileno(f), LOCK_SH); - - struct authinfo_t ret; - ret.success = false; - ret.authlevel = PRIV_NONE; - ret.user = NULL; - - while(1) + if(data) { - char *line = NULL; - char *save; - size_t len = 0; - if(getline(&line, &len, f) < 0) - { - free(line); - free(name); - memset(pass, 0, strlen(pass)); - free(pass); - goto bad; - } - if(!strcmp(strtok_r(line, ":\r\n", &save), name)) - { - free(name); + char *new_hash_hex = hash_pass_hex(pass, salt); - char *salt = strdup(strtok_r(NULL, ":\r\n", &save)); - char *hash = strdup(strtok_r(NULL, ":\r\n", &save)); + memset(pass, 0, strlen(pass)); + free(pass); - ret.authlevel = strtol(strtok_r(NULL, ":\r\n", &save), NULL, 10); + /* hashes are in HEX to avoid the Trucha bug */ + bool success = !memcmp(new_hash_hex, hash, strlen(hash)); - free(line); + free(new_hash_hex); - unsigned int hash_len = gcry_md_get_algo_dlen(ALGO); - - if(strlen(hash) != hash_len * 2) - error("hash corrupt (wrong length)"); - if(strlen(salt) != SALT_LEN) - error("salt corrupt (wrong length)"); - - char *hex = hash_pass_hex(pass, salt); - - memset(pass, 0, strlen(pass)); - free(pass); - free(salt); - - if(!memcmp(hex, hash, hash_len * 2)) - { - free(hex); - free(hash); - ret.success = true; - goto good; - } - else - { - free(hex); - free(hash); - ret.success = false; - goto bad; - } - } - else - free(line); + if(success) + return data; } -good: - debugf("Successful authentication.\n"); - fclose(f); - return ret; -bad: + + /* failure */ sleep(2); - fclose(f); - debugf("Failed authentication.\n"); - return ret; + return NULL; } void auth_user_list(void) @@ -1,6 +1,6 @@ /* * NetCosm - a MUD server - * Copyright (C) 2015 Franklin Wei + * 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 @@ -16,9 +16,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#ifndef _AUTH_H_ +#define _AUTH_H_ + #define SALT_LEN 12 #define ALGO GCRY_MD_SHA512 -#define AUTH_HASHLEN 64 +#define AUTH_HASHLEN (512/8) //#define HASH_ITERS 500000 #define HASH_ITERS 1 @@ -31,10 +34,13 @@ struct authinfo_t { /* makes admin account */ void first_run_setup(void); -struct authinfo_t auth_check(const char *user, const char *pass); +/* NULL on failure, user data struct on success */ +struct userdata_t *auth_check(const char *user, const char *pass); bool auth_user_add(const char *user, const char *pass, int authlevel); bool auth_user_del(const char *user); /* lists users through out() */ void auth_user_list(void); + +#endif diff --git a/src/child_reqs.h b/src/child_reqs.h new file mode 100644 index 0000000..db3257a --- /dev/null +++ b/src/child_reqs.h @@ -0,0 +1,12 @@ +/* functions allowing a child process to send its master requests */ + +extern struct userdata_t sent_userdata; +extern bool child_req_success; + +void client_change_room(room_id id); +void client_change_state(int state); +void client_change_user(const char *user); +void client_move(const char *dir); +void client_look(void); + +void send_master(unsigned char cmd, const void *data, size_t sz); diff --git a/src/client.c b/src/client.c index 8591858..3eb77d4 100644 --- a/src/client.c +++ b/src/client.c @@ -1,6 +1,6 @@ /* * NetCosm - a MUD server - * Copyright (C) 2015 Franklin Wei + * 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 @@ -85,16 +85,18 @@ void send_master(unsigned char cmd, const void *data, size_t sz) pid_t our_pid = getpid(); size_t total_len = (data?sz:0) + 1; - char header[sizeof(pid_t) + sizeof(size_t) + 1]; + if(!data) + sz = 0; - memcpy(header, &our_pid, sizeof(pid_t)); - memcpy(header + sizeof(pid_t), &total_len, sizeof(size_t)); - memcpy(header + sizeof(pid_t) + sizeof(size_t), &cmd, 1); + /* pack it all into one write so it's atomic */ + char *req = malloc(1 + sizeof(pid_t) + sizeof(size_t) + sz); - write(to_parent, header, sizeof(header)); + memcpy(req, &our_pid, sizeof(pid_t)); + memcpy(req + sizeof(pid_t), &total_len, sizeof(size_t)); + memcpy(req + sizeof(pid_t) + sizeof(size_t), &cmd, 1); + memcpy(req + sizeof(pid_t) + sizeof(size_t) + 1, data, sz); - if(data) - write(to_parent, data, sz); + write(to_parent, req, 1 + sizeof(pid_t) + sizeof(size_t) + sz); sigsuspend(&old); sigprocmask(SIG_SETMASK, &old, NULL); @@ -159,6 +161,24 @@ static void print_all(int fd) } while(1); } +struct userdata_t sent_userdata; +bool child_req_success; + +enum reqdata_typespec reqdata_type = TYPE_NONE; +union reqdata_t returned_reqdata; + +void read_string_max(int fd, char *buf, size_t max) +{ + size_t len; + if(read(fd, &len, sizeof(len)) != sizeof(len)) + error("read_string_max"); + if(len > max - 1) + error("read_string_max"); + if(read(fd, buf, len) != (int)len) + error("unexpected EOF"); + buf[max - 1] = '\0'; +} + void sig_rt_0_handler(int s, siginfo_t *info, void *v) { (void) s; @@ -171,8 +191,8 @@ void sig_rt_0_handler(int s, siginfo_t *info, void *v) return; } + reqdata_type = TYPE_NONE; unsigned char cmd; - /* drop this command */ read(from_parent, &cmd, 1); switch(cmd) @@ -192,11 +212,33 @@ void sig_rt_0_handler(int s, siginfo_t *info, void *v) } case REQ_MOVE: { - int status; + bool status; read(from_parent, &status, sizeof(status)); + reqdata_type = TYPE_BOOLEAN; + returned_reqdata.boolean = status; if(!status) out("Cannot go that way.\n"); } + case REQ_GETUSERDATA: + { + sig_debugf("got user data\n"); + bool success; + read(from_parent, &success, sizeof(success)); + if(success) + reqdata_type = TYPE_USERDATA; + else + { + sig_debugf("failure\n"); + break; + } + + struct userdata_t *user = &returned_reqdata.userdata; + read_string_max(from_parent, user->username, sizeof(user->username)); + read_string_max(from_parent, user->salt, sizeof(user->salt)); + read_string_max(from_parent, user->passhash, sizeof(user->passhash)); + read(from_parent, &user->priv, sizeof(user->priv)); + break; + } case REQ_NOP: break; default: @@ -225,22 +267,22 @@ static void sigpipe_handler(int s) _exit(0); } -static void client_change_state(int state) +void client_change_state(int state) { send_master(REQ_CHANGESTATE, &state, sizeof(state)); } -static void client_change_user(const char *user) +void client_change_user(const char *user) { send_master(REQ_CHANGEUSER, user, strlen(user) + 1); } -static void client_change_room(room_id id) +void client_change_room(room_id id) { send_master(REQ_SETROOM, &id, sizeof(id)); } -static void client_move(const char *dir) +void client_move(const char *dir) { const struct dir_pair { const char *text; @@ -285,7 +327,7 @@ static void client_move(const char *dir) out("Unknown direction.\n"); } -static void client_look(void) +void client_look(void) { send_master(REQ_GETROOMNAME, NULL, 0); out("\n"); @@ -330,6 +372,7 @@ auth: int authlevel; char *current_user; + struct userdata_t *current_data = NULL; client_change_state(STATE_AUTH); @@ -337,19 +380,25 @@ auth: while(1) { out("login: "); + current_user = client_read(); remove_cruft(current_user); + out("Password: "); + char *pass = client_read_password(); + client_change_state(STATE_CHECKING); - struct authinfo_t auth = auth_check(current_user, pass); + + current_data = auth_check(current_user, pass); + memset(pass, 0, strlen(pass)); free(pass); - authlevel = auth.authlevel; - if(auth.success) + if(current_data) { out("Access Granted.\n\n"); + authlevel = current_data->priv; break; } else @@ -469,6 +518,10 @@ auth: { auth_user_list(); } + else + { + out("Usage: USER <ADD|DEL|MODIFY|LIST> <ARGS>\n"); + } } else if(!strcmp(tok, "CLIENT")) { @@ -1,6 +1,6 @@ /* * NetCosm - a MUD server - * Copyright (C) 2015 Franklin Wei + * 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 @@ -17,6 +17,7 @@ */ #include "hash.h" + #include <stdlib.h> #include <string.h> @@ -89,6 +90,7 @@ void hash_free(void *ptr) while(node) { struct hash_node *next = node->next; + if(map->free_data) map->free_data((void*)node->data); if(map->free_key) @@ -1,6 +1,6 @@ /* * NetCosm - a MUD server - * Copyright (C) 2015 Franklin Wei + * 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 diff --git a/src/netcosm.h b/src/netcosm.h index c6acf44..c5801a6 100644 --- a/src/netcosm.h +++ b/src/netcosm.h @@ -1,6 +1,6 @@ /* * NetCosm - a MUD server - * Copyright (C) 2015 Franklin Wei + * 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 @@ -44,46 +44,18 @@ #include <time.h> #include <unistd.h> -#include "auth.h" -#include "hash.h" -#include "telnet.h" -#include "userdb.h" - #define USERFILE "users.dat" #define WORLDFILE "world.dat" #define WORLD_MAGIC 0xff467777 #define MAX_FAILURES 3 #define NETCOSM_VERSION "v0.1" +#define MAX_NAME_LEN 20 + #define PRIV_NONE -1 #define PRIV_USER 0 #define PRIV_ADMIN 1337 -/* child<->master commands */ -/* children might not implement all of these */ -/* meanings might be different for the server and child, see comments */ -#define REQ_NOP 0 /* server, child: do nothing */ -#define REQ_BCASTMSG 1 /* server: broadcast text; child: print following text */ -#define REQ_LISTCLIENTS 2 /* server: list childs */ -#define REQ_CHANGESTATE 3 /* server: change child state flag */ -#define REQ_CHANGEUSER 4 /* server: change child login name */ -#define REQ_HANG 5 /* <UNIMP> server: loop forever */ -#define REQ_KICK 6 /* server: kick PID with message; child: print message, quit */ -#define REQ_WAIT 7 /* server: sleep 10s */ -#define REQ_GETROOMDESC 8 /* server: send child room description */ -#define REQ_SETROOM 9 /* server: set child room */ -#define REQ_MOVE 10 /* server: move child based on direction; child: success or failure */ -#define REQ_GETROOMNAME 11 /* server: send child's room name */ -#define REQ_LISTROOMCLIENTS 12 /* server: list clients in child's room */ - -/* child states, sent as an int to the master */ -#define STATE_INIT 0 /* initial state */ -#define STATE_AUTH 1 /* at login screen */ -#define STATE_CHECKING 2 /* checking password */ -#define STATE_LOGGEDIN 3 /* logged in as user */ -#define STATE_ADMIN 4 /* logged in w/ admin privs */ -#define STATE_FAILED 5 /* failed a password attempt */ - /* for convenience when writing world specs */ #define NONE_N NULL #define NONE_NE NULL @@ -98,7 +70,7 @@ #define NONE_IN NULL #define NONE_OT NULL -#define MSG_MAX 512 +#define MSG_MAX 2048 #define ARRAYLEN(x) (sizeof(x)/sizeof(x[0])) #define MAX(a,b) ((a>b)?(a):(b)) @@ -187,6 +159,15 @@ extern const struct roomdata_t netcosm_world[]; extern const size_t netcosm_world_sz; extern const char *netcosm_world_name; +#include "auth.h" +#include "hash.h" +#include "server_reqs.h" +#include "telnet.h" +#include "userdb.h" + +extern volatile int num_clients; +extern void *child_map; + /* called for every client */ void client_main(int sock, struct sockaddr_in *addr, int, int to_parent, int from_parent); @@ -209,4 +190,14 @@ void __attribute__((noreturn,format(printf,1,2))) error(const char *fmt, ...); void debugf_real(const char *fmt, ...); void remove_cruft(char*); +extern enum reqdata_typespec { TYPE_NONE = 0, TYPE_USERDATA, TYPE_BOOLEAN } reqdata_type; +extern union reqdata_t { + struct userdata_t userdata; + bool boolean; +} returned_reqdata; + + +/* call from child process ONLY */ +void send_master(unsigned char cmd, const void *data, size_t sz); + #endif @@ -1,6 +1,6 @@ /* * NetCosm - a MUD server - * Copyright (C) 2015 Franklin Wei + * 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 diff --git a/src/server.c b/src/server.c index e24c9f7..02e71b2 100644 --- a/src/server.c +++ b/src/server.c @@ -1,6 +1,6 @@ /* * NetCosm - a MUD server - * Copyright (C) 2015 Franklin Wei + * 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 @@ -79,19 +79,23 @@ static void handle_client(int fd, struct sockaddr_in *addr, int server_socket; -static void *request_map = NULL; - static void serv_cleanup(void) { sig_debugf("Shutdown server.\n"); + if(shutdown(server_socket, SHUT_RDWR) > 0) error("shutdown"); close(server_socket); + world_free(); - hash_free(request_map); - request_map = NULL; + + reqmap_free(); + hash_free(child_map); child_map = NULL; + + userdb_shutdown(); + _exit(0); } @@ -101,383 +105,6 @@ static void sigint_handler(int s) serv_cleanup(); } -static volatile sig_atomic_t num_acks_wanted, num_acks_recvd, inc_acks = 0; - -static void req_pass_msg(unsigned char *data, size_t datalen, - struct child_data *sender, struct child_data *child) -{ - (void) sender; - - if(sender->pid != child->pid) - { - unsigned char cmd = REQ_BCASTMSG; - write(child->outpipe[1], &cmd, 1); - } - - write(child->outpipe[1], data, datalen); - union sigval nothing; - - if(sender->pid != child->pid) - { - sigqueue(child->pid, SIGRTMIN, nothing); - ++num_acks_wanted; - } -} - -static void req_send_clientinfo(unsigned char *data, size_t datalen, - struct child_data *sender, struct child_data *child) -{ - (void) data; - (void) datalen; - char buf[128]; - const char *state[] = { - "INIT", - "LOGIN SCREEN", - "CHECKING CREDENTIALS", - "LOGGED IN AS USER", - "LOGGED IN AS ADMIN", - "ACCESS DENIED", - }; - - if(child->user) - snprintf(buf, sizeof(buf), "Client %s PID %d [%s] USER %s", - inet_ntoa(child->addr), child->pid, state[child->state], child->user); - else - snprintf(buf, sizeof(buf), "Client %s PID %d [%s]", - inet_ntoa(child->addr), child->pid, state[child->state]); - - if(sender->pid == child->pid) - strncat(buf, " [YOU]\n", sizeof(buf)); - else - strncat(buf, "\n", sizeof(buf)); - - write(sender->outpipe[1], buf, strlen(buf)); -} - -static void req_change_state(unsigned char *data, size_t datalen, - struct child_data *sender, struct child_data *child) -{ - (void) data; (void) datalen; (void) child; (void) sender; - if(datalen == sizeof(sender->state)) - { - sender->state = *((int*)data); - debugf("State changed to %d\n", sender->state); - } - else - { - debugf("State data is of the wrong size\n"); - for(size_t i = 0; i < datalen; ++i) - debugf("%02x\n", data[i]); - } -} - -static void req_change_user(unsigned char *data, size_t datalen, - struct child_data *sender, struct child_data *child) -{ - (void) data; (void) datalen; (void) child; (void) sender; - if(sender->user) - free(sender->user); - sender->user = strdup((char*)data); -} - -//void req_hang(unsigned char *data, size_t datalen, -// struct child_data *sender, struct child_data *child) -//{ -// while(1); -//} - -static void req_kick_client(unsigned char *data, size_t datalen, - struct child_data *sender, struct child_data *child) -{ - (void) data; (void) datalen; (void) child; (void) sender; - if(datalen >= sizeof(pid_t)) - { - pid_t kicked_pid = *((pid_t*)data); - if(kicked_pid == child->pid) - { - unsigned char cmd = REQ_KICK; - write(child->outpipe[1], &cmd, 1); - write(child->outpipe[1], data + sizeof(pid_t), datalen - sizeof(pid_t)); - union sigval nothing; - sigqueue(child->pid, SIGRTMIN, nothing); - } - } -} - -static void req_wait(unsigned char *data, size_t datalen, struct child_data *sender) -{ - (void) data; (void) datalen; (void) sender; - sleep(10); -} - -static void req_send_desc(unsigned char *data, size_t datalen, struct child_data *sender) -{ - (void) data; (void) datalen; (void) sender; - struct room_t *room = room_get(sender->room); - write(sender->outpipe[1], room->data.desc, strlen(room->data.desc) + 1); - - char newline = '\n'; - write(sender->outpipe[1], &newline, 1); -} - -static void req_send_roomname(unsigned char *data, size_t datalen, struct child_data *sender) -{ - (void) data; (void) datalen; (void) sender; - struct room_t *room = room_get(sender->room); - write(sender->outpipe[1], room->data.name, strlen(room->data.name) + 1); - - char newline = '\n'; - write(sender->outpipe[1], &newline, 1); -} - -static void child_set_room(struct child_data *child, room_id id) -{ - child->room = id; - room_user_add(id, child); -} - -static void req_set_room(unsigned char *data, size_t datalen, struct child_data *sender) -{ - (void) data; (void) datalen; (void) sender; - room_id id = *((room_id*)data); - - child_set_room(sender, id); -} - -static void req_move_room(unsigned char *data, size_t datalen, struct child_data *sender) -{ - (void) data; (void) datalen; (void) sender; - enum direction_t dir = *((enum direction_t*)data); - struct room_t *current = room_get(sender->room); - - room_user_del(sender->room, sender); - - /* TODO: checking */ - sig_debugf("Moving in direction %d\n", dir); - room_id new = current->adjacent[dir]; - int status; - if(new != ROOM_NONE) - { - child_set_room(sender, new); - status = 1; - } - else - { - status = 0; - } - write(sender->outpipe[1], &status, sizeof(status)); -} - -static const struct child_request { - unsigned char code; - - bool havedata; - - enum { CHILD_NONE, CHILD_SENDER, CHILD_ALL_BUT_SENDER, CHILD_ALL } which; - - /* sender_pipe is the pipe to the sender of the request */ - /* data points to bogus if havedata = false */ - void (*handle_child)(unsigned char *data, size_t len, - struct child_data *sender, struct child_data *child); - - void (*finalize)(unsigned char *data, size_t len, struct child_data *sender); - - /* byte to write back to the sender */ - unsigned char cmd_to_send; -} requests[] = { - { REQ_NOP, false, CHILD_NONE, NULL, NULL, REQ_NOP }, - { REQ_BCASTMSG, true, CHILD_ALL, req_pass_msg, NULL, REQ_BCASTMSG }, - { REQ_LISTCLIENTS, false, CHILD_ALL, req_send_clientinfo, NULL, REQ_BCASTMSG }, - { REQ_CHANGESTATE, true, CHILD_SENDER, req_change_state, NULL, REQ_NOP }, - { REQ_CHANGEUSER, true, CHILD_SENDER, req_change_user, NULL, REQ_NOP }, - { REQ_KICK, true, CHILD_ALL, req_kick_client, NULL, REQ_NOP }, - { REQ_WAIT, false, CHILD_NONE, NULL, req_wait, REQ_NOP }, - { REQ_GETROOMDESC, false, CHILD_NONE, NULL, req_send_desc, REQ_BCASTMSG }, - { REQ_GETROOMNAME, false, CHILD_NONE, NULL, req_send_roomname, REQ_BCASTMSG }, - { REQ_SETROOM, true, CHILD_NONE, NULL, req_set_room, REQ_NOP }, - { REQ_MOVE, true, CHILD_NONE, NULL, req_move_room, REQ_MOVE }, - //{ REQ_ROOMMSG, true, CHILD_ALL, req_send_room_msg, NULL, REQ_BCASTMSG }, -}; - -static SIMP_HASH(unsigned char, uchar_hash); -static SIMP_EQUAL(unsigned char, uchar_equal); - -static void reqmap_init(void) -{ - request_map = hash_init(ARRAYLEN(requests), uchar_hash, uchar_equal); - for(unsigned i = 0; i < ARRAYLEN(requests); ++i) - hash_insert(request_map, &requests[i].code, requests + i); -} - -/** - * Here's how child-parent requests work - * 1. Child writes its PID and length of request to the parent's pipe, followed - * by up to MSG_MAX bytes of data. If the length exceeds MSG_MAX bytes, the - * request will be ignored. - * 1.1 Child spins until parent response. - * 2. Parent handles the request. - * 3. Parent writes its PID and length of message back to the child(ren). - * 4. Parent signals child(ren) with SIGRTMIN - * 5. Child(ren) handle parent's message. - * 6. Child(ren) send the parent SIGRTMIN+1 to acknowledge receipt of message. - * 7. Parent spins until the needed number of signals is reached. - */ - -static bool handle_child_req(int in_fd) -{ - pid_t sender_pid; - - if(read(in_fd, &sender_pid, sizeof(sender_pid)) != sizeof(sender_pid)) - { - sig_debugf("Couldn't get sender PID\n"); - return false; - } - - sig_debugf("Got request from PID %d\n", sender_pid); - - size_t msglen; - const struct child_request *req = NULL; - size_t datalen; - - unsigned char cmd, msg[MSG_MAX + 1]; - - struct child_data *sender = hash_lookup(child_map, &sender_pid); - - if(!sender) - { - sig_debugf("WARNING: got data from unknown PID, ignoring.\n"); - goto fail; - } - - if(read(in_fd, &msglen, sizeof(msglen)) != sizeof(msglen)) - { - sig_debugf("Couldn't read message length, dropping.\n"); - goto fail; - } - - if(msglen < 1) - { - sig_debugf("message too short to be valid, ignoring.\n"); - goto fail; - } - else if(msglen > MSG_MAX) - { - sig_debugf("message too long, ignoring.\n"); - goto fail; - } - - unsigned char *msgptr = msg; - size_t have = 0; - while(have < msglen) - { - ssize_t ret = read(sender->readpipe[0], msgptr, msglen - have); - if(ret < 0) - { - sig_debugf("unexpected EOF\n"); - goto fail; - } - msgptr += ret; - have += ret; - } - - cmd = msg[0]; - msg[MSG_MAX] = '\0'; - - unsigned char *data = msg + 1; - - datalen = msglen - 1; - req = hash_lookup(request_map, &cmd); - - sigset_t old, block; - - sigemptyset(&block); - sigaddset(&block, SIGRTMIN+1); - sigprocmask(SIG_BLOCK, &block, &old); - - num_acks_wanted = 1; - num_acks_recvd = 0; - inc_acks = 1; - - if(!req) - { - sig_debugf("Unknown request.\n"); - goto fail; - } - - write(sender->outpipe[1], &req->cmd_to_send, 1); - - switch(req->which) - { - case CHILD_SENDER: - case CHILD_ALL: - req->handle_child(data, datalen, sender, sender); - if(req->which == CHILD_SENDER) - goto finish; - break; - case CHILD_NONE: - goto finish; - default: - break; - } - - struct child_data *child = NULL; - void *ptr = child_map, *save; - - do { - pid_t *key; - child = hash_iterate(ptr, &save, (void**)&key); - ptr = NULL; - if(!child) - break; - if(child->pid == sender->pid) - continue; - - switch(req->which) - { - case CHILD_ALL: - case CHILD_ALL_BUT_SENDER: - req->handle_child(data, datalen, sender, child); - break; - default: - break; - } - } while(1); - -finish: - - if(req && req->finalize) - req->finalize(data, datalen, sender); - - union sigval junk; - if(sender) - sigqueue(sender->pid, SIGRTMIN, junk); - else - sig_debugf("Unknown PID sent request.\n"); - - while(num_acks_recvd < MIN(num_clients,num_acks_wanted)) - { - sigsuspend(&old); - } - - inc_acks = 0; - - sigprocmask(SIG_SETMASK, &old, NULL); - - return true; -fail: - return true; -} - -static void master_ack_handler(int s, siginfo_t *info, void *v) -{ - (void) s; - (void) v; - if(inc_acks && hash_lookup(child_map, &info->si_pid)) - { - ++num_acks_recvd; - } -} - void init_signals(void) { struct sigaction sa; @@ -526,7 +153,7 @@ void init_signals(void) static void check_userfile(void) { - if(access(USERFILE, F_OK) < 0) + if(access(USERFILE, F_OK) < 0 || userdb_size() == 0) first_run_setup(); if(access(USERFILE, R_OK | W_OK) < 0) @@ -535,7 +162,7 @@ static void check_userfile(void) static void load_worldfile(void) { - if(access(WORLDFILE, F_OK) < 0 || userdb_size() == 0) + if(access(WORLDFILE, F_OK) < 0) { world_init(netcosm_world, netcosm_world_sz, netcosm_world_name); @@ -662,11 +289,13 @@ int main(int argc, char *argv[]) /* only the master process controls the world */ world_free(); - hash_free(request_map); - request_map = NULL; + reqmap_free(); hash_free(child_map); child_map = NULL; + /* user DB requests go through the master */ + userdb_shutdown(); + debugf("Child with PID %d spawned\n", getpid()); server_socket = new_sock; diff --git a/src/server_reqs.c b/src/server_reqs.c new file mode 100644 index 0000000..1501434 --- /dev/null +++ b/src/server_reqs.c @@ -0,0 +1,425 @@ +#include "netcosm.h" + +static volatile sig_atomic_t num_acks_wanted, num_acks_recvd, inc_acks = 0; + +static void req_pass_msg(unsigned char *data, size_t datalen, + struct child_data *sender, struct child_data *child) +{ + (void) sender; + + if(sender->pid != child->pid) + { + unsigned char cmd = REQ_BCASTMSG; + write(child->outpipe[1], &cmd, 1); + } + + write(child->outpipe[1], data, datalen); + union sigval nothing; + + if(sender->pid != child->pid) + { + sigqueue(child->pid, SIGRTMIN, nothing); + ++num_acks_wanted; + } +} + +static void req_send_clientinfo(unsigned char *data, size_t datalen, + struct child_data *sender, struct child_data *child) +{ + (void) data; + (void) datalen; + char buf[128]; + const char *state[] = { + "INIT", + "LOGIN SCREEN", + "CHECKING CREDENTIALS", + "LOGGED IN AS USER", + "LOGGED IN AS ADMIN", + "ACCESS DENIED", + }; + + if(child->user) + snprintf(buf, sizeof(buf), "Client %s PID %d [%s] USER %s", + inet_ntoa(child->addr), child->pid, state[child->state], child->user); + else + snprintf(buf, sizeof(buf), "Client %s PID %d [%s]", + inet_ntoa(child->addr), child->pid, state[child->state]); + + if(sender->pid == child->pid) + strncat(buf, " [YOU]\n", sizeof(buf)); + else + strncat(buf, "\n", sizeof(buf)); + + write(sender->outpipe[1], buf, strlen(buf)); +} + +static void req_change_state(unsigned char *data, size_t datalen, + struct child_data *sender, struct child_data *child) +{ + (void) data; (void) datalen; (void) child; (void) sender; + if(datalen == sizeof(sender->state)) + { + sender->state = *((int*)data); + debugf("State changed to %d\n", sender->state); + } + else + { + debugf("State data is of the wrong size\n"); + for(size_t i = 0; i < datalen; ++i) + debugf("%02x\n", data[i]); + } +} + +static void req_change_user(unsigned char *data, size_t datalen, + struct child_data *sender, struct child_data *child) +{ + (void) data; (void) datalen; (void) child; (void) sender; + if(sender->user) + free(sender->user); + sender->user = strdup((char*)data); +} + +//void req_hang(unsigned char *data, size_t datalen, +// struct child_data *sender, struct child_data *child) +//{ +// while(1); +//} + +static void req_kick_client(unsigned char *data, size_t datalen, + struct child_data *sender, struct child_data *child) +{ + (void) data; (void) datalen; (void) child; (void) sender; + if(datalen >= sizeof(pid_t)) + { + pid_t kicked_pid = *((pid_t*)data); + if(kicked_pid == child->pid) + { + unsigned char cmd = REQ_KICK; + write(child->outpipe[1], &cmd, 1); + write(child->outpipe[1], data + sizeof(pid_t), datalen - sizeof(pid_t)); + union sigval nothing; + sigqueue(child->pid, SIGRTMIN, nothing); + } + } +} + +static void req_wait(unsigned char *data, size_t datalen, struct child_data *sender) +{ + (void) data; (void) datalen; (void) sender; + sleep(10); +} + +static void req_send_desc(unsigned char *data, size_t datalen, struct child_data *sender) +{ + (void) data; (void) datalen; (void) sender; + struct room_t *room = room_get(sender->room); + write(sender->outpipe[1], room->data.desc, strlen(room->data.desc) + 1); + + char newline = '\n'; + write(sender->outpipe[1], &newline, 1); +} + +static void req_send_roomname(unsigned char *data, size_t datalen, struct child_data *sender) +{ + (void) data; (void) datalen; (void) sender; + struct room_t *room = room_get(sender->room); + write(sender->outpipe[1], room->data.name, strlen(room->data.name) + 1); + + char newline = '\n'; + write(sender->outpipe[1], &newline, 1); +} + +static void child_set_room(struct child_data *child, room_id id) +{ + child->room = id; + room_user_add(id, child); +} + +static void req_set_room(unsigned char *data, size_t datalen, struct child_data *sender) +{ + (void) data; (void) datalen; (void) sender; + room_id id = *((room_id*)data); + + child_set_room(sender, id); +} + +static void req_move_room(unsigned char *data, size_t datalen, struct child_data *sender) +{ + (void) data; (void) datalen; (void) sender; + enum direction_t dir = *((enum direction_t*)data); + struct room_t *current = room_get(sender->room); + + room_user_del(sender->room, sender); + + /* TODO: checking */ + sig_debugf("Moving in direction %d\n", dir); + room_id new = current->adjacent[dir]; + bool status; + if(new != ROOM_NONE) + { + child_set_room(sender, new); + status = true; + } + else + { + status = false; + } + write(sender->outpipe[1], &status, sizeof(status)); +} + +static void send_string(int fd, const char *s) +{ + size_t len = strlen(s); + write(fd, &len, sizeof(len)); + write(fd, s, len); +} + +static void req_send_user(unsigned char *data, size_t datalen, struct child_data *sender) +{ + if(datalen) + { + struct userdata_t *user = userdb_lookup((char*)data); + + if(user) + { + bool confirm = true; + write(sender->outpipe[1], &confirm, sizeof(confirm)); + send_string(sender->outpipe[1], user->username); + send_string(sender->outpipe[1], user->salt); + send_string(sender->outpipe[1], user->passhash); + write(sender->outpipe[1], &user->priv, sizeof(user->priv)); + return; + } + + sig_debugf("failure 2\n"); + } + + sig_debugf("failure 1\n"); + + bool fail = false; + write(sender->outpipe[1], &fail, sizeof(fail)); +} + +static const struct child_request { + unsigned char code; + + bool havedata; + + enum { CHILD_NONE, CHILD_SENDER, CHILD_ALL_BUT_SENDER, CHILD_ALL } which; + + /* sender_pipe is the pipe to the sender of the request */ + /* data points to bogus if havedata = false */ + void (*handle_child)(unsigned char *data, size_t len, + struct child_data *sender, struct child_data *child); + + void (*finalize)(unsigned char *data, size_t len, struct child_data *sender); + + /* byte to write back to the sender */ + unsigned char cmd_to_send; +} requests[] = { + { REQ_NOP, false, CHILD_NONE, NULL, NULL, REQ_NOP }, + { REQ_BCASTMSG, true, CHILD_ALL, req_pass_msg, NULL, REQ_BCASTMSG }, + { REQ_LISTCLIENTS, false, CHILD_ALL, req_send_clientinfo, NULL, REQ_BCASTMSG }, + { REQ_CHANGESTATE, true, CHILD_SENDER, req_change_state, NULL, REQ_NOP }, + { REQ_CHANGEUSER, true, CHILD_SENDER, req_change_user, NULL, REQ_NOP }, + { REQ_KICK, true, CHILD_ALL, req_kick_client, NULL, REQ_NOP }, + { REQ_WAIT, false, CHILD_NONE, NULL, req_wait, REQ_NOP }, + { REQ_GETROOMDESC, false, CHILD_NONE, NULL, req_send_desc, REQ_BCASTMSG }, + { REQ_GETROOMNAME, false, CHILD_NONE, NULL, req_send_roomname, REQ_BCASTMSG }, + { REQ_SETROOM, true, CHILD_NONE, NULL, req_set_room, REQ_NOP }, + { REQ_MOVE, true, CHILD_NONE, NULL, req_move_room, REQ_MOVE }, + { REQ_GETUSERDATA, true, CHILD_NONE, NULL, req_send_user, REQ_GETUSERDATA }, + { REQ_DELUSERDATA, true, CHILD_NONE, NULL, req_del_user, REQ_DELUSERDATA }, + { REQ_ADDUSERDATA, true, CHILD_NONE, NULL, req_add_user, REQ_ADDUSERDATA }, + //{ REQ_ROOMMSG, true, CHILD_ALL, req_send_room_msg, NULL, REQ_BCASTMSG }, +}; + +static SIMP_HASH(unsigned char, uchar_hash); +static SIMP_EQUAL(unsigned char, uchar_equal); + +static void *request_map = NULL; + +void reqmap_init(void) +{ + request_map = hash_init(ARRAYLEN(requests), uchar_hash, uchar_equal); + for(unsigned i = 0; i < ARRAYLEN(requests); ++i) + hash_insert(request_map, &requests[i].code, requests + i); +} + +void reqmap_free(void) +{ + if(request_map) + { + hash_free(request_map); + request_map = NULL; + } +} + +/** + * Here's how child-parent requests work + * 1. Child writes its PID and length of request to the parent's pipe, followed + * by up to MSG_MAX bytes of data. If the length exceeds MSG_MAX bytes, the + * request will be ignored. + * 1.1 Child spins until parent response. + * 2. Parent handles the request. + * 3. Parent writes its PID and length of message back to the child(ren). + * 4. Parent signals child(ren) with SIGRTMIN + * 5. Child(ren) handle parent's message. + * 6. Child(ren) send the parent SIGRTMIN+1 to acknowledge receipt of message. + * 7. Parent spins until the needed number of signals is reached. + */ + +bool handle_child_req(int in_fd) +{ + pid_t sender_pid; + + if(read(in_fd, &sender_pid, sizeof(sender_pid)) != sizeof(sender_pid)) + { + sig_debugf("Couldn't get sender PID\n"); + return false; + } + + sig_debugf("Got request from PID %d\n", sender_pid); + + size_t msglen; + const struct child_request *req = NULL; + size_t datalen; + + unsigned char cmd, msg[MSG_MAX + 1]; + + struct child_data *sender = hash_lookup(child_map, &sender_pid); + + if(!sender) + { + sig_debugf("WARNING: got data from unknown PID, ignoring.\n"); + goto fail; + } + + if(read(in_fd, &msglen, sizeof(msglen)) != sizeof(msglen)) + { + sig_debugf("Couldn't read message length, dropping.\n"); + goto fail; + } + + if(msglen < 1) + { + sig_debugf("message too short to be valid, ignoring.\n"); + goto fail; + } + else if(msglen > MSG_MAX) + { + sig_debugf("message too long, ignoring.\n"); + goto fail; + } + + unsigned char *msgptr = msg; + size_t have = 0; + while(have < msglen) + { + ssize_t ret = read(sender->readpipe[0], msgptr, msglen - have); + if(ret < 0) + { + sig_debugf("unexpected EOF\n"); + goto fail; + } + msgptr += ret; + have += ret; + } + + cmd = msg[0]; + msg[MSG_MAX] = '\0'; + + unsigned char *data = msg + 1; + + datalen = msglen - 1; + req = hash_lookup(request_map, &cmd); + + sigset_t old, block; + + sigemptyset(&block); + sigaddset(&block, SIGRTMIN+1); + sigprocmask(SIG_BLOCK, &block, &old); + + num_acks_wanted = 1; + num_acks_recvd = 0; + inc_acks = 1; + + if(!req) + { + sig_debugf("Unknown request.\n"); + goto fail; + } + + write(sender->outpipe[1], &req->cmd_to_send, 1); + + switch(req->which) + { + case CHILD_SENDER: + case CHILD_ALL: + req->handle_child(data, datalen, sender, sender); + if(req->which == CHILD_SENDER) + goto finish; + break; + case CHILD_NONE: + goto finish; + default: + break; + } + + struct child_data *child = NULL; + void *ptr = child_map, *save; + + do { + pid_t *key; + child = hash_iterate(ptr, &save, (void**)&key); + ptr = NULL; + if(!child) + break; + if(child->pid == sender->pid) + continue; + + switch(req->which) + { + case CHILD_ALL: + case CHILD_ALL_BUT_SENDER: + req->handle_child(data, datalen, sender, child); + break; + default: + break; + } + } while(1); + +finish: + + if(req && req->finalize) + req->finalize(data, datalen, sender); + + union sigval junk; + if(sender) + sigqueue(sender->pid, SIGRTMIN, junk); + else + sig_debugf("Unknown PID sent request.\n"); + + while(num_acks_recvd < MIN(num_clients,num_acks_wanted)) + { + sigsuspend(&old); + } + + inc_acks = 0; + + sigprocmask(SIG_SETMASK, &old, NULL); + + return true; +fail: + return true; +} + +void master_ack_handler(int s, siginfo_t *info, void *v) +{ + (void) s; + (void) v; + if(inc_acks && hash_lookup(child_map, &info->si_pid)) + { + ++num_acks_recvd; + } +} diff --git a/src/server_reqs.h b/src/server_reqs.h new file mode 100644 index 0000000..1c15d12 --- /dev/null +++ b/src/server_reqs.h @@ -0,0 +1,50 @@ +/* + * 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/>. + */ + +/* child<->master commands */ +/* children might not implement all of these */ +/* meanings might be different for the server and child, see comments */ +#define REQ_NOP 0 /* server, child: do nothing */ +#define REQ_BCASTMSG 1 /* server: broadcast text; child: print following text */ +#define REQ_LISTCLIENTS 2 /* server: list childs */ +#define REQ_CHANGESTATE 3 /* server: change child state flag */ +#define REQ_CHANGEUSER 4 /* server: change child login name */ +#define REQ_HANG 5 /* <UNIMP> server: loop forever */ +#define REQ_KICK 6 /* server: kick PID with message; child: print message, quit */ +#define REQ_WAIT 7 /* server: sleep 10s */ +#define REQ_GETROOMDESC 8 /* server: send child room description */ +#define REQ_SETROOM 9 /* server: set child room */ +#define REQ_MOVE 10 /* server: move child based on direction; child: success or failure */ +#define REQ_GETROOMNAME 11 /* server: send child's room name */ +#define REQ_LISTROOMCLIENTS 12 /* server: list clients in child's room */ +#define REQ_GETUSERDATA 13 /* server: send user data; child: get user data */ +#define REQ_DELUSERDATA 14 /* server: delete user data; child: success/failure */ +#define REQ_ADDUSERDATA 15 /* server: insert user data; child: success/fail */ + +/* child states, sent as an int to the master */ +#define STATE_INIT 0 /* initial state */ +#define STATE_AUTH 1 /* at login screen */ +#define STATE_CHECKING 2 /* checking password */ +#define STATE_LOGGEDIN 3 /* logged in as user */ +#define STATE_ADMIN 4 /* logged in w/ admin privs */ +#define STATE_FAILED 5 /* failed a password attempt */ + +bool handle_child_req(int in_fd); +void master_ack_handler(int s, siginfo_t *info, void *v); +void reqmap_init(void); +void reqmap_free(void); diff --git a/src/telnet.c b/src/telnet.c index bd800ff..173574b 100644 --- a/src/telnet.c +++ b/src/telnet.c @@ -1,6 +1,6 @@ /* * NetCosm - a MUD server - * Copyright (C) 2015 Franklin Wei + * 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 diff --git a/src/telnet.h b/src/telnet.h index 1ca7b4c..c0725c4 100644 --- a/src/telnet.h +++ b/src/telnet.h @@ -1,6 +1,6 @@ /* * NetCosm - a MUD server - * Copyright (C) 2015 Franklin Wei + * 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 diff --git a/src/userdb.c b/src/userdb.c index 4315da4..c58771f 100644 --- a/src/userdb.c +++ b/src/userdb.c @@ -4,8 +4,6 @@ static void *map = NULL; static char *db_file = NULL; static size_t entries = 0; -#define DELIM ":" - /* * the user DB is stored on disk as an ASCII database * @@ -20,12 +18,11 @@ void userdb_init(const char *file) /* FILE* for getline() */ FILE *f = fopen(file, "r"); map = hash_init(256, hash_djb, compare_strings); - hash_setfreekey_cb(map, free); hash_setfreedata_cb(map, free); char *format; - asprintf(&format, "%%ms:%%%d%%s:%%%d%%s:%%d\n", - SALT_LEN, AUTH_HASHLEN * 2); + asprintf(&format, "%%%d[a-z0-9]:%%%d[A-Z]:%%%ds:%%d\n", + MAX_NAME_LEN, SALT_LEN, AUTH_HASHLEN * 2); if(f) { @@ -33,11 +30,13 @@ void userdb_init(const char *file) { struct userdata_t *data = calloc(1, sizeof(*data)); - if(fscanf(f, format, - &data->username, - data->salt, - data->passhash, - &data->priv) != 6) + int ret = fscanf(f, format, + &data->username, + data->salt, + data->passhash, + &data->priv); + + if(ret != 4) break; hash_insert(map, data->username, data); @@ -60,7 +59,8 @@ void userdb_write(const char *file) ptr = NULL; if(!user) break; - dprintf(fd, "%s:%*s:%*s:%d\n", user->username, + dprintf(fd, "%*s:%*s:%*s:%d\n", + MAX_NAME_LEN, user->username, SALT_LEN, user->salt, AUTH_HASHLEN*2, user->passhash, user->priv); @@ -73,18 +73,25 @@ struct userdata_t *userdb_lookup(const char *key) return hash_lookup(map, key); } -void userdb_remove(const char *key) +bool userdb_remove(const char *key) { if(hash_remove(map, key)) + { --entries; + userdb_write(db_file); + return true; + } + return false; } struct userdata_t *userdb_add(struct userdata_t *data) { struct userdata_t *new = calloc(1, sizeof(*new)); /* only in C! */ memcpy(new, data, sizeof(*new)); - new->username = strdup(data->username); + strncpy(new->username, data->username, sizeof(new->username)); + struct userdata_t *ret; + if((ret = hash_insert(map, new->username, new))) /* failure */ { hash_remove(map, new->username); @@ -93,19 +100,50 @@ struct userdata_t *userdb_add(struct userdata_t *data) userdb_write(db_file); if(!ret) - --entries; + ++entries; return ret; } void userdb_shutdown(void) { - hash_free(map); + debugf("shutting down userdb with %d entries\n", entries); + if(map) + { + hash_free(map); + map = NULL; + } if(db_file) + { free(db_file); + db_file = NULL; + } } size_t userdb_size(void) { return entries; } + +/* child request wrappers */ + +/* loop until we can return a user data structure */ +struct userdata_t *userdb_request_lookup(const char *name) +{ + send_master(REQ_GETUSERDATA, name, strlen(name) + 1); + if(reqdata_type == TYPE_USERDATA) + return &returned_reqdata.userdata; + return NULL; +} + +bool userdb_request_add(struct userdata_t *data) +{ + send_master(REQ_ADDUSERDATA, data, sizeof(*data)); + return returned_reqdata.boolean; +} + +bool userdb_request_remove(const char *name) +{ + send_master(REQ_DELUSERDATA, name, strlen(name) + 1); + return returned_reqdata.boolean; +} diff --git a/src/userdb.h b/src/userdb.h index 89e2566..ac169b2 100644 --- a/src/userdb.h +++ b/src/userdb.h @@ -1,4 +1,5 @@ #include "netcosm.h" +#include "auth.h" /* * on-disk database for storing user data @@ -7,7 +8,7 @@ */ struct userdata_t { - char *username; + char username[MAX_NAME_LEN + 1]; char salt[SALT_LEN + 1]; @@ -23,7 +24,7 @@ void userdb_init(const char *dbfile); /* looks up a username in the DB, returns NULL upon failure */ struct userdata_t *userdb_lookup(const char *username); -void userdb_remove(const char *username); +bool userdb_remove(const char *username); /* is it empty? */ size_t userdb_size(void); @@ -1,6 +1,6 @@ /* * NetCosm - a MUD server - * Copyright (C) 2015 Franklin Wei + * 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 |