aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2016-01-08 22:27:43 -0500
committerFranklin Wei <git@fwei.tk>2016-01-08 22:27:43 -0500
commit50134c46dc337c35b8ecf78b3c5b4308cf8fb791 (patch)
tree89d73bca1951129ec7e15b642e2fa6c85567f872 /src
parent2819d11ceeb1ac739ed5f17ccb0abab63f494299 (diff)
downloadnetcosm-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.c170
-rw-r--r--src/auth.h12
-rw-r--r--src/child_reqs.h12
-rw-r--r--src/client.c89
-rw-r--r--src/hash.c4
-rw-r--r--src/hash.h2
-rw-r--r--src/netcosm.h55
-rw-r--r--src/room.c2
-rw-r--r--src/server.c401
-rw-r--r--src/server_reqs.c425
-rw-r--r--src/server_reqs.h50
-rw-r--r--src/telnet.c2
-rw-r--r--src/telnet.h2
-rw-r--r--src/userdb.c68
-rw-r--r--src/userdb.h5
-rw-r--r--src/util.c2
16 files changed, 693 insertions, 608 deletions
diff --git a/src/auth.c b/src/auth.c
index c0a6a46..400f5cd 100644
--- a/src/auth.c
+++ b/src/auth.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
@@ -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)
diff --git a/src/auth.h b/src/auth.h
index cd2fc1a..2717d30 100644
--- a/src/auth.h
+++ b/src/auth.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
@@ -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"))
{
diff --git a/src/hash.c b/src/hash.c
index 035236c..d5eeba0 100644
--- a/src/hash.c
+++ b/src/hash.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
@@ -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)
diff --git a/src/hash.h b/src/hash.h
index 5eb8135..b154bd0 100644
--- a/src/hash.h
+++ b/src/hash.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/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
diff --git a/src/room.c b/src/room.c
index b874f77..f79c422 100644
--- a/src/room.c
+++ b/src/room.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/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);
diff --git a/src/util.c b/src/util.c
index 25063f6..b084261 100644
--- a/src/util.c
+++ b/src/util.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