diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/auth.c | 5 | ||||
| -rw-r--r-- | src/auth.h | 6 | ||||
| -rw-r--r-- | src/client.c | 64 | ||||
| -rw-r--r-- | src/client_reqs.c | 6 | ||||
| -rw-r--r-- | src/globals.h | 7 | ||||
| -rw-r--r-- | src/obj.c | 6 | ||||
| -rw-r--r-- | src/obj.h | 2 | ||||
| -rw-r--r-- | src/server.c | 50 | ||||
| -rw-r--r-- | src/server_reqs.c | 62 | ||||
| -rw-r--r-- | src/server_reqs.h | 5 | ||||
| -rw-r--r-- | src/userdb.c | 2 | ||||
| -rw-r--r-- | src/util.c | 87 | ||||
| -rw-r--r-- | src/util.h | 3 | ||||
| -rw-r--r-- | src/world.c | 30 |
14 files changed, 210 insertions, 125 deletions
@@ -193,11 +193,12 @@ struct userdata_t *auth_check(const char *name2, const char *pass2) { debugf("auth module: user %s found\n", name2); - /* hashes are in HEX to avoid the Trucha bug */ + /* hashes are in lowercase hex to avoid the Trucha bug + * but still allow comparison with strcmp() */ char *new_hash_hex = hash_pass_hex(pass, salt); bool success = true; - /* constant-time comparison to a timing attack */ + /* constant-time comparison to hopefully prevent a timing attack */ for(int i = 0; i < AUTH_HASHLEN; ++i) { if(new_hash_hex[i] != hash[i]) @@ -26,12 +26,6 @@ //#define HASH_ITERS 500000 #define HASH_ITERS 1 -struct authinfo_t { - bool success; - const char *user; - int authlevel; -}; - /* makes admin account */ void first_run_setup(void); diff --git a/src/client.c b/src/client.c index 3117b63..d66836e 100644 --- a/src/client.c +++ b/src/client.c @@ -46,9 +46,9 @@ void out_raw(const void *buf, size_t len) error("out() called from master"); if(!len) return; - + try_again: - + while(output_locked); /* something weird happened and the value changed between the loop and here */ @@ -103,8 +103,8 @@ void __attribute__((format(printf,1,2))) out(const char *fmt, ...) if(is_newline) ++ptr; /* skip the newline */ - /* skip following spaces */ - while(*ptr == ' ') + /* skip following spaces */ + while(*ptr == ' ') ++ptr; last_space = 0; pos = 0; @@ -208,7 +208,10 @@ int user_cb(char **save) { char *what = strtok_r(NULL, WSPACE, save); if(!what) + { + out("Usage: USER <ADD|DEL|MODIFY|LIST> <ARGS>\n"); return CMD_OK; + } all_upper(what); @@ -308,7 +311,7 @@ int client_cb(char **save) char *pid_s = strtok_r(NULL, WSPACE, save); if(pid_s) { - all_upper(pid_s); + all_upper(pid_s); if(!strcmp(pid_s, "ALL")) { const char *msg = "Kicking everyone...\n"; @@ -338,7 +341,7 @@ int client_cb(char **save) debugf("Success.\n"); } else - out("Usage: CLIENT KICK <PID>\n"); + out("Usage: CLIENT KICK <PID|ALL>\n"); } return CMD_OK; } @@ -427,9 +430,53 @@ int drop_cb(char **save) { char *what = strtok_r(NULL, "", save); if(what) - client_drop(what); + client_drop(what); else - out("You must supply an object.\n"); + out("You must supply an object.\n"); + return CMD_OK; +} + +int chpass_cb(char **save) +{ + out("Enter current password: "); + char *current = client_read_password(); + + struct userdata_t *current_data = auth_check(current_user, current); + + memset(current, 0, strlen(current)); + free(current); + + if(!current_data) + { + out("Password mismatch.\n"); + return CMD_OK; + } + + out("Enter new password: "); + char *pass1 = client_read_password(); + + out("Retype new password: "); + + char *pass2 = client_read_password(); + + if(strcmp(pass1, pass2)) + { + memset(pass1, 0, strlen(pass1)); + memset(pass2, 0, strlen(pass2)); + free(pass1); + free(pass2); + + out("Passwords do not match.\n"); + return CMD_OK; + } + + memset(pass2, 0, strlen(pass2)); + free(pass2); + + auth_user_add(current_user, pass1, current_data->priv); + + memset(pass1, 0, strlen(pass1)); + return CMD_OK; } @@ -451,6 +498,7 @@ static const struct client_cmd { { "WAIT", wait_cb, true }, { "GO", go_cb, false }, { "DROP", drop_cb, false }, + { "CHPASS", chpass_cb, false }, }; static void *cmd_map = NULL; diff --git a/src/client_reqs.c b/src/client_reqs.c index 36f341c..21174c4 100644 --- a/src/client_reqs.c +++ b/src/client_reqs.c @@ -267,11 +267,11 @@ void client_take(char *obj) { if(obj) { - all_lower(obj); - send_master(REQ_TAKE, obj, strlen(obj) + 1); + all_lower(obj); + send_master(REQ_TAKE, obj, strlen(obj) + 1); } else - out("You must supply an object.\n"); + out("You must supply an object.\n"); } void client_inventory(void) diff --git a/src/globals.h b/src/globals.h index 0177b8a..5a164ba 100644 --- a/src/globals.h +++ b/src/globals.h @@ -21,17 +21,18 @@ #define _GNU_SOURCE -#include <libev/ev.h> +#include <ev.h> + #include <openssl/sha.h> #include <openssl/opensslv.h> #include <arpa/inet.h> #include <arpa/telnet.h> #include <assert.h> -//#include <bsd/string.h> // for strlcat #include <ctype.h> #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <limits.h> #include <netdb.h> #include <netinet/in.h> @@ -66,7 +67,7 @@ #define WORLD_MAGIC 0x31415926 #define USERDB_MAGIC 0x27182818 #define MAX_FAILURES 3 -#define NETCOSM_VERSION "0.5.0" +#define NETCOSM_VERSION "0.5.1" /* username length */ #define MAX_NAME_LEN 32 @@ -144,7 +144,7 @@ struct object_t *obj_copy(struct object_t *obj) struct object_t *obj_dup(struct object_t *obj) { - debugf("Adding an object reference to #%lu.\n", obj->id); + debugf("Adding an object reference to #%" PRI_OBJID ".\n", obj->id); ++obj->refcount; return obj; } @@ -154,11 +154,11 @@ void obj_free(void *ptr) struct object_t *obj = ptr; --obj->refcount; - debugf("Freeing an object reference for #%lu (%s, %d).\n", obj->id, obj->name, obj->refcount); + debugf("Freeing an object reference for #%" PRI_OBJID" (%s, %d).\n", obj->id, obj->name, obj->refcount); if(!obj->refcount) { - debugf("Freeing object #%lu\n", obj->id); + debugf("Freeing object #%"PRI_OBJID"\n", obj->id); if(obj->class->hook_destroy) obj->class->hook_destroy(obj); @@ -54,6 +54,8 @@ struct obj_class_t { typedef uint64_t obj_id; +#define PRI_OBJID PRId64 + struct obj_alias_t { char *alias; struct obj_alias_t *next; diff --git a/src/server.c b/src/server.c index 6e8dba2..6d90c61 100644 --- a/src/server.c +++ b/src/server.c @@ -255,10 +255,34 @@ static void new_connection_cb(EV_P_ ev_io *w, int revents) int readpipe[2]; /* child->parent */ int outpipe [2]; /* parent->child */ - if(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, readpipe) < 0) - error("error creating pipe, need linux kernel >= 3.4"); - if(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, outpipe) < 0) - error("error creating pipe, need linux kernel >= 3.4"); + /* try several methods to create a packet pipe between the child and master: */ + + /* first try creating a pipe in "packet mode": see pipe(2) */ + if(pipe2(readpipe, O_DIRECT) < 0) + { + /* then try a SOCK_SEQPACKET socket pair: see unix(7) */ + if(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, readpipe) < 0) + { + /* if that failed, try a SOCK_DGRAM socket as a last resort */ + if(socketpair(AF_UNIX, SOCK_DGRAM, 0, readpipe) < 0) + error("couldn't create child-master communication pipe"); + else + debugf("WARNING: Using a SOCK_DGRAM socket pair for IPC, performance may be degraded.\n"); + } + else + debugf("Using a SOCK_SEQPACKET socket pair for IPC.\n"); + } + else + debugf("Using a packet-mode pipe for IPC.\n"); + + if(pipe2(outpipe, O_DIRECT) < 0) + { + if(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, outpipe) < 0) + { + if(socketpair(AF_UNIX, SOCK_DGRAM, 0, outpipe) < 0) + error("error creating pipe, need linux kernel >= 3.4"); + } + } pid_t pid = fork(); if(pid < 0) @@ -329,6 +353,7 @@ static void init_signals(void) { struct sigaction sa; + /* SIGINT and SIGTERM cause graceful shutdown */ sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGINT); sa.sa_handler = sigint_handler; @@ -338,6 +363,7 @@ static void init_signals(void) if(sigaction(SIGTERM, &sa, NULL) < 0) error("sigaction"); + /* ignore SIGPIPE */ sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; sa.sa_flags = 0; @@ -345,7 +371,7 @@ static void init_signals(void) error("sigaction"); /* libev's default SIGCHLD handler exhibits some really strange - * behavior, which we don't like, so we use our own ;) */ + * behavior, which we don't like, so we use our own */ sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGCHLD); sa.sa_handler = sigchld_handler; @@ -367,7 +393,7 @@ static void parse_args(int argc, char *argv[]) switch(c) { case 'h': /* help */ - debugf("FIXME: usage message"); + debugf("Usage: %s [-d PREFIX] [-a <username> <password>]\n", argv[0]); exit(0); case 'a': /* automatic first-run config */ autoconfig = true; @@ -393,7 +419,7 @@ static SIMP_EQUAL(pid_t, pid_equal); static void check_libs(void) { - debugf("*** Starting NetCosm %s (libev %d.%d, %s) ***\n", + debugf("*** NetCosm %s (libev %d.%d, %s) ***\n", NETCOSM_VERSION, EV_VERSION_MAJOR, EV_VERSION_MINOR, OPENSSL_VERSION_TEXT); assert(ev_version_major() == EV_VERSION_MAJOR && ev_version_minor() >= EV_VERSION_MINOR); @@ -405,11 +431,9 @@ int server_main(int argc, char *argv[]) parse_args(argc, argv); - server_socket = server_bind(); - userdb_init(USERFILE); - - /* also perform first-time setup */ + + /* also performs first-time setup: */ check_userfile(); load_worldfile(); @@ -427,10 +451,12 @@ int server_main(int argc, char *argv[]) debugf("Listening on port %d\n", port); + server_socket = server_bind(); + struct ev_loop *loop = ev_default_loop(0); /* we initialize signals after creating the default event loop - * because libev grabs SIGCHLD */ + * because libev grabs SIGCHLD in the process */ init_signals(); ev_io server_watcher; diff --git a/src/server_reqs.c b/src/server_reqs.c index 676403d..21787f4 100644 --- a/src/server_reqs.c +++ b/src/server_reqs.c @@ -150,7 +150,7 @@ static void req_send_desc(unsigned char *data, size_t datalen, struct child_data send_packet(sender, REQ_BCASTMSG, (void*)room->data.desc, strlen(room->data.desc)); send_packet(sender, REQ_PRINTNEWLINE, NULL, 0); - + /* list objects */ char buf[MSG_MAX]; buf[0] = 0; @@ -189,7 +189,7 @@ static void req_send_desc(unsigned char *data, size_t datalen, struct child_data { strlcat(buf, "There are ", sizeof(buf)); char n[32]; - snprintf(n, sizeof(n), "%lu ", n_objs); + snprintf(n, sizeof(n), "%zu ", n_objs); strlcat(buf, n, sizeof(buf)); strlcat(buf, name, sizeof(buf)); strlcat(buf, "s here.\n", sizeof(buf)); @@ -238,25 +238,25 @@ static void req_move_room(unsigned char *data, size_t datalen, struct child_data /* TODO: bounds checking on `dir' */ room_id new = current->adjacent[dir]; - + if(new == ROOM_NONE) { - send_msg(sender, "You cannot go that way.\n"); + send_msg(sender, "You cannot go that way.\n"); } else { - struct room_t *new_room = room_get(new); - - if((!new_room->data.hook_enter || - (new_room->data.hook_enter && new_room->data.hook_enter(new, sender))) && - (!current->data.hook_leave || - (current->data.hook_leave && current->data.hook_leave(sender->room, sender)))) - { - room_user_del(sender->room, sender); - - child_set_room(sender, new); - status = 1; - } + struct room_t *new_room = room_get(new); + + if((!new_room->data.hook_enter || + (new_room->data.hook_enter && new_room->data.hook_enter(new, sender))) && + (!current->data.hook_leave || + (current->data.hook_leave && current->data.hook_leave(sender->room, sender)))) + { + room_user_del(sender->room, sender); + + child_set_room(sender, new); + status = 1; + } } send_packet(sender, REQ_MOVE, &status, sizeof(status)); @@ -419,34 +419,8 @@ static void req_inventory(unsigned char *data, size_t datalen, struct child_data struct object_t *obj = iter->val; if(!strcmp(name, obj->name)) { - if(n_objs == 1) - { - if(obj->default_article) - { - char *article = (is_vowel(name[0])?"An":"A"); - strlcat(buf, article, sizeof(buf)); - strlcat(buf, " ", sizeof(buf)); - strlcat(buf, name, sizeof(buf)); - } - else - { - char tmp[2]; - tmp[0] = toupper(name[0]); - tmp[1] = '\0'; - strlcat(buf, tmp, sizeof(buf)); - strlcat(buf, name + 1, sizeof(buf)); - } - strlcat(buf, "\n", sizeof(buf)); - } - else - { - char n[32]; - snprintf(n, sizeof(n), "%lu", n_objs); - strlcat(buf, n, sizeof(buf)); - strlcat(buf, " ", sizeof(buf)); - strlcat(buf, name, sizeof(buf)); - strlcat(buf, "s\n", sizeof(buf)); - } + format_noun(buf, sizeof(buf), name, n_objs, obj->default_article, true); + strlcat(buf, "\n", sizeof(buf)); send_packet(sender, REQ_BCASTMSG, buf, strlen(buf)); } diff --git a/src/server_reqs.h b/src/server_reqs.h index 68ba74a..10c1755 100644 --- a/src/server_reqs.h +++ b/src/server_reqs.h @@ -23,7 +23,7 @@ #include "server.h" /* child<->master commands */ -/* children might not implement all of these */ +/* not all of these are implemented by both parties */ /* meanings might be different for the server and child, see comments */ #define REQ_NOP 0 /* server, child: do nothing (used for acknowledgement) */ #define REQ_BCASTMSG 1 /* server: broadcast text; child: print following text */ @@ -32,7 +32,7 @@ #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_WAIT 7 /* <DEBUG> 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 */ @@ -50,6 +50,7 @@ #define REQ_DROP 22 /* server: drop user object if allowed */ #define REQ_LISTUSERS 23 /* server: list users in USERFILE */ #define REQ_EXECVERB 24 /* server: execute a verb with its arguments */ +#define REQ_RAWMODE 25 /* child: toggle the child's processing of commands and instead sending input directly to master */ /* child states, sent as an int to the master */ #define STATE_INIT 0 /* initial state */ diff --git a/src/userdb.c b/src/userdb.c index 54dd19e..0a13319 100644 --- a/src/userdb.c +++ b/src/userdb.c @@ -232,7 +232,7 @@ void userdb_dump(void) while(iter) { struct object_t *obj = iter->val; - debugf(" - Obj #%lu class %s: name %s\n", obj->id, obj->class->class_name, obj->name); + debugf(" - Obj #%"PRI_OBJID" class %s: name %s\n", obj->id, obj->class->class_name, obj->name); iter = iter->next; } } @@ -188,9 +188,9 @@ bool is_vowel(char c) } } -/* $NetBSD: strlcat.c,v 1.4 2005/05/16 06:55:48 lukem Exp $ */ -/* from NetBSD: strlcat.c,v 1.16 2003/10/27 00:12:42 lukem Exp */ -/* from OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp */ +/* $NetBSD: strlcat.c,v 1.4 2005/05/16 06:55:48 lukem Exp $ */ +/* from NetBSD: strlcat.c,v 1.16 2003/10/27 00:12:42 lukem Exp */ +/* from OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp */ /* * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> @@ -218,27 +218,62 @@ bool is_vowel(char c) size_t strlcat(char *dst, const char *src, size_t siz) { - char *d = dst; - const char *s = src; - size_t n = siz; - size_t dlen; - - /* Find the end of dst and adjust bytes left but don't go past end */ - while (n-- != 0 && *d != '\0') - d++; - dlen = d - dst; - n = siz - dlen; - - if (n == 0) - return(dlen + strlen(s)); - while (*s != '\0') { - if (n != 1) { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return(dlen + (s - src)); /* count does not include NUL */ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} + +char *format_noun(char *buf, size_t len, const char *name, size_t count, bool default_article, bool capitalize) +{ + assert(len > 1); + buf[0] = '\0'; + if(count == 1) + { + if(default_article) + { + char *article = capitalize?(is_vowel(name[0])? "An" : "A"):(is_vowel(name[0])? "an" : "a"); + strlcat(buf, article, len); + strlcat(buf, " ", len); + strlcat(buf, name, len); + } + else + { + char tmp[2]; + tmp[0] = toupper(name[0]); + tmp[1] = '\0'; + strlcat(buf, tmp, len); + strlcat(buf, name + 1, len); + } + } + else + { + char n[32]; + snprintf(n, sizeof(n), "%zu", count); + strlcat(buf, n, len); + strlcat(buf, " ", len); + strlcat(buf, name, len); + strlcat(buf, "s", len); + } + + return buf; } @@ -51,3 +51,6 @@ size_t read_size(int fd); bool is_vowel(char c); size_t strlcat(char *dst, const char *src, size_t siz); + +/* formats a noun's name */ +char *format_noun(char *buf, size_t len, const char *name, size_t count, bool default_article, bool capitalize); diff --git a/src/world.c b/src/world.c index a06b08e..8c25d7d 100644 --- a/src/world.c +++ b/src/world.c @@ -134,13 +134,13 @@ void world_save(const char *fname) void *ptr = world_map, *save; for(unsigned int i = 0; i < world_sz; ++i) { - void *key; - struct room_t *room = hash_iterate(ptr, &save, &key); - if(!room) - break; - ptr = NULL; - write_string(fd, key); - write_roomid(fd, &room->id); + void *key; + struct room_t *room = hash_iterate(ptr, &save, &key); + if(!room) + break; + ptr = NULL; + write_string(fd, key); + write_roomid(fd, &room->id); } close(fd); @@ -250,13 +250,13 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz /* read in the room name -> room map */ world_map = hash_init(world_sz * 2, hash_djb, compare_strings); - + for(unsigned int i = 0; i < world_sz; ++i) { - const char *key = read_string(fd); - room_id id = read_roomid(fd); - debugf("'%s' -> %d\n", key, id); - hash_insert(world_map, key, world + id); + const char *key = read_string(fd); + room_id id = read_roomid(fd); + debugf("'%s' -> %d\n", key, id); + hash_insert(world_map, key, world + id); } close(fd); @@ -269,7 +269,7 @@ static SIMP_EQUAL(enum direction_t, dir_equal); /* loads room data (supplied by the world module) into our internal format */ void world_init(const struct roomdata_t *data, size_t sz, const char *name) { - debugf("Loading world with %lu rooms.\n", sz); + debugf("Loading world with %zu rooms.\n", sz); world = calloc(sz, sizeof(struct room_t)); world_sz = 0; world_name = strdup(name); @@ -400,7 +400,7 @@ room_id room_get_id(const char *uniq_id) { struct room_t *room = hash_lookup(world_map, uniq_id); if(!room) - return ROOM_NONE; + return ROOM_NONE; else - return room->id; + return room->id; } |