diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/auth.c | 6 | ||||
| -rw-r--r-- | src/client.c | 10 | ||||
| -rw-r--r-- | src/globals.h | 6 | ||||
| -rw-r--r-- | src/obj.c | 106 | ||||
| -rw-r--r-- | src/obj.h | 86 | ||||
| -rw-r--r-- | src/room.c | 10 | ||||
| -rw-r--r-- | src/server_reqs.c | 72 | ||||
| -rw-r--r-- | src/server_reqs.h | 1 | ||||
| -rw-r--r-- | src/userdb.c | 9 | ||||
| -rw-r--r-- | src/userdb.h | 2 | ||||
| -rw-r--r-- | src/util.c | 15 | ||||
| -rw-r--r-- | src/util.h | 3 |
12 files changed, 293 insertions, 33 deletions
@@ -41,10 +41,10 @@ static char *hash_pass_hex(const char *pass, const char *salt) SHA512(salted, pass_len + SALT_LEN, hash); unsigned char tmp[AUTH_HASHLEN]; - /* now hash the hash half a million times to slow things down */ - for(int i = 0; i < HASH_ITERS - 1; ++i) + /* now hash the hash a million times to slow things down */ + for(int i = 0; i < (HASH_ITERS / 2) - 1; ++i) { - memcpy(tmp, hash, AUTH_HASHLEN); + AUTH_HASHFUNC(hash, AUTH_HASHLEN, tmp); AUTH_HASHFUNC(tmp, AUTH_HASHLEN, hash); } memset(tmp, 0, sizeof(tmp)); diff --git a/src/client.c b/src/client.c index 3fab9cb..6812702 100644 --- a/src/client.c +++ b/src/client.c @@ -436,6 +436,11 @@ void client_inventory(void) send_master(REQ_PRINTINVENTORY, NULL, 0); } +void client_drop(char *what) +{ + send_master(REQ_DROP, what, strlen(what) + 1); +} + #define WSPACE " \t\r\n" void client_main(int fd, struct sockaddr_in *addr, int total, int to, int from) @@ -739,6 +744,11 @@ auth: else out("Expected direction after GO.\n"); } + else if(!strcmp(tok, "DROP")) + { + char *what = strtok_r(NULL, " ", &save); + client_drop(what); + } next_cmd: diff --git a/src/globals.h b/src/globals.h index fcf6784..5edc153 100644 --- a/src/globals.h +++ b/src/globals.h @@ -28,7 +28,6 @@ #include <arpa/inet.h> #include <arpa/telnet.h> #include <assert.h> -#include <bsd/stdlib.h> // for arc4random #include <bsd/string.h> // for strlcat #include <ctype.h> #include <errno.h> @@ -64,9 +63,10 @@ #define WORLDFILE "world.dat" #define LOGFILE "netcosm.log" -#define WORLD_MAGIC 0x31415926 +#define WORLD_MAGIC 0x31415926 +#define USERDB_MAGIC 0x27182818 #define MAX_FAILURES 3 -#define NETCOSM_VERSION "0.3.0" +#define NETCOSM_VERSION "0.4.0-rc1" /* username length */ #define MAX_NAME_LEN 32 diff --git a/src/obj.c b/src/obj.c new file mode 100644 index 0000000..0f9c554 --- /dev/null +++ b/src/obj.c @@ -0,0 +1,106 @@ +/* + * NetCosm - a MUD server + * Copyright (C) 2016 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "globals.h" + +#include "hash.h" +#include "obj.h" + +/* map of class names -> object classes */ +static void *obj_class_map = NULL; + +struct object_t *obj_new(const char *class_name) +{ + if(!obj_class_map) + { + extern const struct obj_class_t netcosm_obj_classes[]; + extern const size_t netcosm_obj_classes_sz; + obj_class_map = hash_init(netcosm_obj_classes_sz / 2 + 1, + hash_djb, + compare_strings); + for(unsigned i = 0; i < netcosm_obj_classes_sz; ++i) + { + if(hash_insert(obj_class_map, + netcosm_obj_classes[i].class_name, + netcosm_obj_classes + i)) + error("duplicate object class name"); + } + } + + struct object_t *obj = calloc(1, sizeof(struct object_t)); + + obj->class = hash_lookup(obj_class_map, class_name); + if(!obj->class) + { + free(obj); + error("unknown object class '%s'", class_name); + } + else + return obj; +} + +void obj_write(int fd, struct object_t *obj) +{ + write_string(fd, obj->class->class_name); + write_string(fd, obj->name); + write_bool(fd, obj->list); + + if(obj->class->hook_serialize) + obj->class->hook_serialize(fd, obj); +} + +struct object_t *obj_read(int fd) +{ + char *class_name = read_string(fd); + struct object_t *obj = obj_new(class_name); + free(class_name); + + obj->name = read_string(fd); + obj->list = read_bool(fd); + if(obj->class->hook_deserialize) + obj->class->hook_deserialize(fd, obj); + + return obj; +} + +struct object_t *obj_dup(struct object_t *obj) +{ + struct object_t *new = calloc(1, sizeof(struct object_t)); + memcpy(new, obj, sizeof(*new)); + new->name = strdup(obj->name); + new->userdata = obj->class->hook_clone(obj->userdata); + return new; +} + +void obj_free(void *ptr) +{ + struct object_t *obj = ptr; + debugf("Freeing obj %s\n", obj->name); + + if(obj->class->hook_destroy) + obj->class->hook_destroy(obj); + + free(obj->name); + free(obj); +} + +void obj_shutdown(void) +{ + hash_free(obj_class_map); + obj_class_map = NULL; +} diff --git a/src/obj.h b/src/obj.h new file mode 100644 index 0000000..240b4a9 --- /dev/null +++ b/src/obj.h @@ -0,0 +1,86 @@ +/* + * NetCosm - a MUD server + * Copyright (C) 2016 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "globals.h" + +#include "room.h" + +/* Objects belong to an object class. Objects define their object + * class through the class name, which is converted to a class ID + * internally. + */ + +struct object_t; + +typedef struct child_data user_t; // see server.h for the definition + +struct obj_class_t { + const char *class_name; + + /* write an object's user data to disk */ + void (*hook_serialize)(int fd, struct object_t*); + + /* read an object's user data */ + void (*hook_deserialize)(int fd, struct object_t*); + + /* called when an object is taken; + * true = can take + * false = can't take + * no function (NULL) = can take */ + bool (*hook_take)(struct object_t*, user_t *user); + + /* called when dropping an object; + * true: can drop + * false: can't drop + * NULL: can drop + */ + bool (*hook_drop)(struct object_t*, user_t *user); + void* (*hook_clone)(void*); /* clone the user data pointer */ + void (*hook_destroy)(struct object_t*); + const char* (*hook_desc)(struct object_t*, user_t*); +}; + +struct object_t { + char *name; /* no articles: "a", "an", "the", needs to be free()able */ + + struct obj_class_t *class; + + bool list; + + void *userdata; +}; + +/* returns a new object of class 'c' */ +struct object_t *obj_new(const char *c); + +/* serialize an object */ +void obj_write(int fd, struct object_t *obj); + +/* deserialize an object */ +struct object_t *obj_read(int fd); + +/* make a duplicate of an object + * used for "moving" an object */ +struct object_t *obj_dup(struct object_t *obj); + +void obj_free(void*); + +/* shut down the obj_* module */ +void obj_shutdown(void); @@ -64,8 +64,7 @@ bool room_user_del(room_id id, struct child_data *child) void world_save(const char *fname) { int fd = open(fname, O_CREAT | O_WRONLY, 0644); - uint32_t magic = WORLD_MAGIC; - write(fd, &magic, sizeof(magic)); + write_uint32(fd, WORLD_MAGIC); write(fd, &world_sz, sizeof(world_sz)); write_string(fd, world_name); for(unsigned i = 0; i < world_sz; ++i) @@ -139,6 +138,7 @@ static void room_init_maps(struct room_t *room) room->users = hash_init((userdb_size() / 2) + 1, hash_djb, compare_strings); room->objects = hash_init(OBJMAP_SIZE, hash_djb, compare_strings); + hash_setfreedata_cb(room->objects, obj_free); } /** @@ -153,9 +153,8 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz int fd = open(fname, O_RDONLY); if(fd < 0) return false; - uint32_t magic; - read(fd, &magic, sizeof(magic)); - if(magic != WORLD_MAGIC) + + if(read_uint32(fd) != WORLD_MAGIC) return false; if(world) @@ -193,7 +192,6 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz for(unsigned j = 0; j < n_objects; ++j) { - debugf("READING %dth OBJECT\n", j); struct object_t *obj = obj_read(fd); if(!room_obj_add(i, obj)) diff --git a/src/server_reqs.c b/src/server_reqs.c index 689c2be..f02ed1c 100644 --- a/src/server_reqs.c +++ b/src/server_reqs.c @@ -47,6 +47,18 @@ tryagain: } } +static void __attribute__((format(printf,2,3))) +send_msg(struct child_data *child, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + char *buf; + int len = vasprintf(&buf, fmt, ap); + va_end(ap); + send_packet(child, REQ_BCASTMSG, buf, len); + free(buf); +} + static void req_pass_msg(unsigned char *data, size_t datalen, struct child_data *sender, struct child_data *child) { @@ -243,9 +255,7 @@ static void req_send_geninfo(unsigned char *data, size_t datalen, struct child_d { (void) data; (void) datalen; - char buf[128]; - int len = snprintf(buf, sizeof(buf), "Total clients: %d\n", num_clients); - send_packet(sender, REQ_BCASTMSG, buf, len); + send_msg(sender, "Total clients: %d\n", num_clients); } static void req_kick_always(unsigned char *data, size_t datalen, @@ -267,8 +277,7 @@ static void req_look_at(unsigned char *data, size_t datalen, struct child_data * } else { - const char *msg = "I don't know what that is.\n"; - send_packet(sender, REQ_BCASTMSG, (void*)msg, strlen(msg)); + send_msg(sender, "I don't know what that is.\n"); } } @@ -278,6 +287,12 @@ static void req_take(unsigned char *data, size_t datalen, struct child_data *sen struct object_t *obj = room_obj_get(sender->room, (const char*)data); if(obj) { + if(obj->class->hook_take && !obj->class->hook_take(obj, sender)) + { + send_msg(sender, "You can't take that.\n"); + return; + } + struct object_t *dup = obj_dup(obj); hash_insert(userdb_lookup(sender->user)->objects, dup->name, dup); @@ -285,11 +300,13 @@ static void req_take(unsigned char *data, size_t datalen, struct child_data *sen room_obj_del(sender->room, (const char*)data); world_save(WORLDFILE); + userdb_write(USERFILE); + + send_msg(sender, "Taken.\n"); } else { - const char *msg = "I don't know what that is.\n"; - send_packet(sender, REQ_BCASTMSG, (void*)msg, strlen(msg)); + send_msg(sender, "I don't know what that is.\n"); } } @@ -299,22 +316,50 @@ static void req_inventory(unsigned char *data, size_t datalen, struct child_data (void) data; void *ptr = userdb_lookup(sender->user)->objects, *save; - char buf[MSG_MAX] = "You currently have:\n"; - send_packet(sender, REQ_BCASTMSG, (void*)buf, strlen(buf)); + send_msg(sender, "You currently have:\n"); while(1) { struct object_t *obj = hash_iterate(ptr, &save, NULL); + if(!obj) break; + ptr = NULL; + + char buf[MSG_MAX]; int len = snprintf(buf, sizeof(buf), "A %s\n", obj->name); + send_packet(sender, REQ_BCASTMSG, (void*)buf, len); } - const char *msg = "Nothing!\n"; if(ptr) - send_packet(sender, REQ_BCASTMSG, (void*)msg, strlen(msg)); + send_msg(sender, "Nothing!\n"); +} + +static void req_drop(unsigned char *data, size_t datalen, struct child_data *sender) +{ + (void) datalen; + (void) data; + + struct userdata_t *user = userdb_lookup(sender->user); + if(!user) + return; + struct object_t *obj = hash_lookup(user->objects, (const char*)data); + if(!obj) + { + send_msg(sender, "You don't have that.\n"); + return; + } + + struct object_t *dup = obj_dup(obj); + room_obj_add(sender->room, dup); + hash_remove(user->objects, (const char*)data); + + send_msg(sender, "Dropped.\n"); + + world_save(WORLDFILE); + userdb_write(USERFILE); } static const struct child_request { @@ -347,8 +392,9 @@ static const struct child_request { { REQ_ADDUSERDATA, true, CHILD_NONE, NULL, req_add_user, }, { REQ_KICKALL, true, CHILD_ALL_BUT_SENDER, req_kick_always, NULL, }, { REQ_LOOKAT, true, CHILD_NONE, NULL, req_look_at, }, - { REQ_TAKE, true, CHILD_NONE, NULL, req_take }, - { REQ_PRINTINVENTORY, false, CHILD_NONE, NULL, req_inventory }, + { REQ_TAKE, true, CHILD_NONE, NULL, req_take, }, + { REQ_PRINTINVENTORY, false, CHILD_NONE, NULL, req_inventory, }, + { REQ_DROP, true, CHILD_NONE, NULL, req_drop, }, //{ REQ_ROOMMSG, true, CHILD_ALL, req_send_room_msg, NULL, }, }; diff --git a/src/server_reqs.h b/src/server_reqs.h index 8b455ce..2b3d08f 100644 --- a/src/server_reqs.h +++ b/src/server_reqs.h @@ -41,6 +41,7 @@ #define REQ_LOOKAT 19 /* server: send object description */ #define REQ_TAKE 20 /* server: add object to user inventory */ #define REQ_PRINTINVENTORY 21 /* server: print user inventory */ +#define REQ_DROP 22 /* server: drop user object if allowed */ /* 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 5e948ab..5c80581 100644 --- a/src/userdb.c +++ b/src/userdb.c @@ -30,8 +30,6 @@ static void free_userdata_and_objs(void *ptr) { struct userdata_t *data = ptr; - debugf("Freeing DATA AND OBJECTS of %s\n", data->username); - if(data->objects) { hash_setfreedata_cb(data->objects, obj_free); @@ -66,6 +64,8 @@ void userdb_init(const char *file) /* 0 is a valid fd */ if(fd >= 0) { + if(read_uint32(fd) != USERDB_MAGIC) + error("bad userdb magic value"); while(1) { struct userdata_t *data = calloc(1, sizeof(*data)); @@ -87,8 +87,6 @@ void userdb_init(const char *file) hash_djb, compare_strings); - debugf("READING %d OBJECTS INTO INVENTORY\n", n_objects); - for(unsigned i = 0; i < n_objects; ++i) { struct object_t *obj = obj_read(fd); @@ -105,6 +103,7 @@ void userdb_init(const char *file) void userdb_write(const char *file) { int fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0600); + write_uint32(fd, USERDB_MAGIC); void *save, *ptr = map; while(1) { @@ -123,8 +122,6 @@ void userdb_write(const char *file) write(fd, &n_objects, sizeof(n_objects)); - debugf("WRITING %d OBJECTS\n", n_objects); - /* write objects */ if(n_objects) diff --git a/src/userdb.h b/src/userdb.h index c4705a9..2f122a9 100644 --- a/src/userdb.h +++ b/src/userdb.h @@ -30,7 +30,7 @@ struct userdata_t { char salt[SALT_LEN + 1]; - /* in hex + NULL terminator */ + /* in lowercase hex + NULL terminator */ char passhash[AUTH_HASHLEN * 2 + 1]; priv_t priv; @@ -95,7 +95,6 @@ char *read_string(int fd) { error("read_string: EOF"); } - debugf("sz is %d\n", sz); char *ret = malloc(sz + 1); if(read(fd, ret, sz) < 0) { @@ -119,3 +118,17 @@ void write_bool(int fd, bool b) if(write(fd, &b, sizeof(b)) != sizeof(b)) error("write failed"); } + +uint32_t read_uint32(int fd) +{ + uint32_t ret; + if(read(fd, &ret, sizeof(ret)) != sizeof(ret)) + error("unexpected EOF"); + return ret; +} + +void write_uint32(int fd, uint32_t b) +{ + if(write(fd, &b, sizeof(b)) != sizeof(b)) + error("write failed"); +} @@ -36,3 +36,6 @@ room_id read_roomid(int fd); void write_bool(int fd, bool b); bool read_bool(int fd); + +void write_uint32(int fd, uint32_t i); +uint32_t read_uint32(int fd); |