diff options
| author | Franklin Wei <git@fwei.tk> | 2016-01-26 19:22:53 -0500 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2016-01-26 19:27:13 -0500 |
| commit | 3b8df7146fe31518080f3a38eee98222d97ee5b2 (patch) | |
| tree | 7fd7dffd98d0bfd3bdf20e24b184ec5440ca3909 | |
| parent | cbe6bc14de8ad8fd035e0598a3fcc23fb38cfa1a (diff) | |
| download | netcosm-3b8df7146fe31518080f3a38eee98222d97ee5b2.zip netcosm-3b8df7146fe31518080f3a38eee98222d97ee5b2.tar.gz netcosm-3b8df7146fe31518080f3a38eee98222d97ee5b2.tar.bz2 netcosm-3b8df7146fe31518080f3a38eee98222d97ee5b2.tar.xz | |
fix a memory leak, bumps version to 0.4.0-rc1
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | README.md | 12 | ||||
| -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 | ||||
| -rw-r--r-- | worlds/test.c | 9 |
15 files changed, 308 insertions, 41 deletions
@@ -11,7 +11,7 @@ INCLUDES = -I src/ -I export/include/ WARNFLAGS = -Wall -Wextra -Wshadow -fno-strict-aliasing -OPTFLAGS = -Og +OPTFLAGS = -O3 DEBUGFLAGS = -g CFLAGS = $(OPTFLAGS) $(DEBUGFLAGS) $(WARNFLAGS) -std=c99 $(INCLUDES) @@ -9,7 +9,7 @@ features might drift out of existence without prior warning! ### Prerequisites: * openssl (for password hashing) -* libbsd (for arc4random, strlcpy, etc.) +* libbsd (for strlcpy) * libev * linux >= 3.4 (for "packet mode" pipes) * glibc >= 2.9 @@ -51,8 +51,8 @@ Both of these pipes are created in "packet mode" (see pipe(2)), and therefore require at least linux 3.4 and glibc 2.9. Many operations, such as listing clients, require the help of the -master process. To request an operation, the child writes it's request -data to it's pipe to the parent. The size of the request MUST be less +master process. To request an operation, the child writes its request +data to its pipe to the parent. The size of the request MUST be less than PIPE_BUF, which is 4096 on Linux. The format of the request is as follows: @@ -91,7 +91,7 @@ latency. Versions are numbered using the MAJOR.MINOR.BUGFIX scheme. -Current stable is 0.3.0. +The latest version is 0.4.0-rc1. Major versions mark major milestones (see below), minor versions mark incremental milestones and compatibility of data files, and bugfix @@ -102,8 +102,8 @@ number. #### 0.4.0 -* Object support -* User inventory support +* Object support [DONE, needs testing] +* User inventory support [PARTIAL] #### 0.5.0 @@ -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); diff --git a/worlds/test.c b/worlds/test.c index 2ce4122..722584c 100644 --- a/worlds/test.c +++ b/worlds/test.c @@ -115,13 +115,20 @@ void weap_destroy(struct object_t *obj) obj->userdata = NULL; } +void *weap_clone(void *userdata) +{ + void *ret = malloc(sizeof(double)); + memcpy(ret, userdata, sizeof(double)); + return ret; +} + const struct obj_class_t netcosm_obj_classes[] = { { "weapon", weap_serialize, weap_deserialize, NULL, NULL, - NULL, + weap_clone, weap_destroy, shiny }, }; |