diff --git a/src/ctl.h b/src/ctl.h index 211265e8..3a684244 100644 --- a/src/ctl.h +++ b/src/ctl.h @@ -12,6 +12,8 @@ enum { CTL_CMD_ID_INFO, CTL_CMD_DISCONNECT_NAME, CTL_CMD_DISCONNECT_ID, + CTL_CMD_LIST_BANNED, + CTL_CMD_UNBAN_IP, CTL_CMD_STATUS_REP = 101, CTL_CMD_RELOAD_REP, @@ -19,6 +21,8 @@ enum { CTL_CMD_LIST_REP, CTL_CMD_DISCONNECT_NAME_REP, CTL_CMD_DISCONNECT_ID_REP, + CTL_CMD_UNBAN_IP_REP, + CTL_CMD_LIST_BANNED_REP, }; #endif diff --git a/src/ctl.proto b/src/ctl.proto index d79bac11..5074bf31 100644 --- a/src/ctl.proto +++ b/src/ctl.proto @@ -61,3 +61,21 @@ message id_req { required sint32 id = 1; } + +message ban_info_rep +{ + required string ip = 1; + required uint32 score = 2; + optional uint32 expires = 3; +} + +message ban_list_rep +{ + repeated ban_info_rep info = 1; +} + +message unban_req +{ + required string ip = 1; +} + diff --git a/src/main-ban.c b/src/main-ban.c index 8d6cd414..47c08c78 100644 --- a/src/main-ban.c +++ b/src/main-ban.c @@ -43,14 +43,6 @@ #include #include -typedef struct ban_entry_st { - char ip[MAX_IP_STR]; - unsigned score; - - time_t last_reset; /* the time its score counting started */ - time_t expires; /* the time after the client is allowed to login */ -} ban_entry_st; - static size_t rehash(const void *_e, void *unused) { ban_entry_st *e = (void*)_e; @@ -232,3 +224,4 @@ void cleanup_banned_entries(main_server_st *s) } } + diff --git a/src/main-ban.h b/src/main-ban.h index 6e28e3f3..be09bb68 100644 --- a/src/main-ban.h +++ b/src/main-ban.h @@ -23,6 +23,14 @@ # include "main.h" +typedef struct ban_entry_st { + char ip[MAX_IP_STR]; + unsigned score; + + time_t last_reset; /* the time its score counting started */ + time_t expires; /* the time after the client is allowed to login */ +} ban_entry_st; + void cleanup_banned_entries(main_server_st *s); unsigned check_if_banned(main_server_st *s, struct sockaddr_storage *addr, socklen_t addr_size); int add_ip_to_ban_list(main_server_st *s, const char *ip, unsigned score); diff --git a/src/main-ctl-unix.c b/src/main-ctl-unix.c index 3e75f841..a13689fd 100644 --- a/src/main-ctl-unix.c +++ b/src/main-ctl-unix.c @@ -50,6 +50,8 @@ static void method_disconnect_user_name(method_ctx *ctx, int cfd, uint8_t * msg, unsigned msg_size); static void method_disconnect_user_id(method_ctx *ctx, int cfd, uint8_t * msg, unsigned msg_size); +static void method_unban_ip(method_ctx *ctx, int cfd, + uint8_t * msg, unsigned msg_size); static void method_stop(method_ctx *ctx, int cfd, uint8_t * msg, unsigned msg_size); static void method_reload(method_ctx *ctx, int cfd, uint8_t * msg, @@ -58,6 +60,8 @@ static void method_user_info(method_ctx *ctx, int cfd, uint8_t * msg, unsigned msg_size); static void method_id_info(method_ctx *ctx, int cfd, uint8_t * msg, unsigned msg_size); +static void method_list_banned(method_ctx *ctx, int cfd, uint8_t * msg, + unsigned msg_size); typedef void (*method_func) (method_ctx *ctx, int cfd, uint8_t * msg, unsigned msg_size); @@ -76,8 +80,10 @@ static const ctl_method_st methods[] = { ENTRY(CTL_CMD_RELOAD, method_reload), ENTRY(CTL_CMD_STOP, method_stop), ENTRY(CTL_CMD_LIST, method_list_users), + ENTRY(CTL_CMD_LIST_BANNED, method_list_banned), ENTRY(CTL_CMD_USER_INFO, method_user_info), ENTRY(CTL_CMD_ID_INFO, method_id_info), + ENTRY(CTL_CMD_UNBAN_IP, method_unban_ip), ENTRY(CTL_CMD_DISCONNECT_NAME, method_disconnect_user_name), ENTRY(CTL_CMD_DISCONNECT_ID, method_disconnect_user_id), {NULL, 0, NULL} @@ -425,6 +431,68 @@ static void method_list_users(method_ctx *ctx, int cfd, uint8_t * msg, return; } +static int append_ban_info(method_ctx *ctx, + BanListRep *list, + struct ban_entry_st *e) +{ + BanInfoRep *rep; + + list->info = + talloc_realloc(ctx->pool, list->info, BanInfoRep *, (1 + list->n_info)); + if (list->info == NULL) + return -1; + + rep = list->info[list->n_info] = talloc(ctx->pool, BanInfoRep); + if (rep == NULL) + return -1; + list->n_info++; + + ban_info_rep__init(rep); + + rep->ip = e->ip; + rep->score = e->score; + + if (ctx->s->config->max_ban_score > 0 && e->score >= ctx->s->config->max_ban_score) { + rep->expires = e->expires; + rep->has_expires = 1; + } + + return 0; +} + +static void method_list_banned(method_ctx *ctx, int cfd, uint8_t * msg, + unsigned msg_size) +{ + BanListRep rep = BAN_LIST_REP__INIT; + struct ban_entry_st *e = NULL; + struct htable *db = ctx->s->ban_db; + int ret; + struct htable_iter iter; + + mslog(ctx->s, NULL, LOG_DEBUG, "ctl: list-banned-ips"); + + e = htable_first(db, &iter); + while (e != NULL) { + ret = append_ban_info(ctx, &rep, e); + if (ret < 0) { + mslog(ctx->s, NULL, LOG_ERR, + "error appending ban info to reply"); + goto error; + } + e = htable_next(db, &iter); + } + + ret = send_msg(ctx->pool, cfd, CTL_CMD_LIST_BANNED_REP, &rep, + (pack_size_func) ban_list_rep__get_packed_size, + (pack_func) ban_list_rep__pack); + if (ret < 0) { + mslog(ctx->s, NULL, LOG_ERR, "error sending ban list reply"); + } + + error: + return; +} + static void single_info_common(method_ctx *ctx, int cfd, uint8_t * msg, unsigned msg_size, const char *user, unsigned id) { @@ -519,6 +587,42 @@ static void method_id_info(method_ctx *ctx, int cfd, uint8_t * msg, return; } +static void method_unban_ip(method_ctx *ctx, + int cfd, uint8_t * msg, + unsigned msg_size) +{ + UnbanReq *req; + BoolMsg rep = BOOL_MSG__INIT; + int ret; + + mslog(ctx->s, NULL, LOG_DEBUG, "ctl: unban IP"); + + req = unban_req__unpack(NULL, msg_size, msg); + if (req == NULL) { + mslog(ctx->s, NULL, LOG_ERR, + "error parsing unban IP request"); + return; + } + + if (req->ip) { + remove_ip_from_ban_list(ctx->s, req->ip); + rep.status = 1; + mslog(ctx->s, NULL, LOG_INFO, + "unbanning IP '%s' due to ctl request", req->ip); + } + + unban_req__free_unpacked(req, NULL); + + ret = send_msg(ctx->pool, cfd, CTL_CMD_UNBAN_IP_REP, &rep, + (pack_size_func) bool_msg__get_packed_size, + (pack_func) bool_msg__pack); + if (ret < 0) { + mslog(ctx->s, NULL, LOG_ERR, "error sending unban IP ctl reply"); + } + + return; +} + static void method_disconnect_user_name(method_ctx *ctx, int cfd, uint8_t * msg, unsigned msg_size) diff --git a/src/occtl-unix.c b/src/occtl-unix.c index 446e9783..666602f6 100644 --- a/src/occtl-unix.c +++ b/src/occtl-unix.c @@ -50,10 +50,12 @@ static uint8_t msg_map[] = { [CTL_CMD_RELOAD] = CTL_CMD_RELOAD_REP, [CTL_CMD_STOP] = CTL_CMD_STOP_REP, [CTL_CMD_LIST] = CTL_CMD_LIST_REP, + [CTL_CMD_LIST_BANNED] = CTL_CMD_LIST_BANNED_REP, [CTL_CMD_USER_INFO] = CTL_CMD_LIST_REP, [CTL_CMD_ID_INFO] = CTL_CMD_LIST_REP, [CTL_CMD_DISCONNECT_NAME] = CTL_CMD_DISCONNECT_NAME_REP, [CTL_CMD_DISCONNECT_ID] = CTL_CMD_DISCONNECT_ID_REP, + [CTL_CMD_UNBAN_IP] = CTL_CMD_UNBAN_IP_REP, }; struct cmd_reply_st { @@ -339,6 +341,56 @@ int handle_stop_cmd(struct unix_ctx *ctx, const char *arg) return ret; } +int handle_unban_ip_cmd(struct unix_ctx *ctx, const char *arg) +{ + int ret; + struct cmd_reply_st raw; + BoolMsg *rep; + unsigned status; + UnbanReq req = UNBAN_REQ__INIT; + PROTOBUF_ALLOCATOR(pa, ctx); + + if (arg == NULL || need_help(arg)) { + check_cmd_help(rl_line_buffer); + return 1; + } + + init_reply(&raw); + + req.ip = (void*)arg; + + ret = send_cmd(ctx, CTL_CMD_UNBAN_IP, &req, + (pack_size_func)unban_req__get_packed_size, + (pack_func)unban_req__pack, &raw); + if (ret < 0) { + goto error; + } + + rep = bool_msg__unpack(&pa, raw.data_size, raw.data); + if (rep == NULL) + goto error; + + status = rep->status; + bool_msg__free_unpacked(rep, &pa); + + if (status != 0) { + printf("IP '%s' was unbanned\n", arg); + } else { + printf("could not unban IP '%s'\n", arg); + } + + ret = 0; + goto cleanup; + + error: + fprintf(stderr, ERR_SERVER_UNREACHABLE); + ret = 1; + cleanup: + free_reply(&raw); + + return ret; +} + int handle_disconnect_user_cmd(struct unix_ctx *ctx, const char *arg) { int ret; @@ -533,6 +585,70 @@ int handle_list_users_cmd(struct unix_ctx *ctx, const char *arg) return ret; } +int handle_list_banned_cmd(struct unix_ctx *ctx, const char *arg) +{ + int ret; + struct cmd_reply_st raw; + BanListRep *rep = NULL; + unsigned i; + char str_since[64]; + FILE *out; + struct tm *tm; + time_t t; + PROTOBUF_ALLOCATOR(pa, ctx); + + init_reply(&raw); + + out = pager_start(); + + ret = send_cmd(ctx, CTL_CMD_LIST_BANNED, NULL, NULL, NULL, &raw); + if (ret < 0) { + goto error; + } + + rep = ban_list_rep__unpack(&pa, raw.data_size, raw.data); + if (rep == NULL) + goto error; + + for (i=0;in_info;i++) { + if (rep->info[i]->ip == NULL) + continue; + + /* add header */ + if (i == 0) { + fprintf(out, "%14s %14s %30s\n", + "IP", "score", "expires"); + } + + if (rep->info[i]->has_expires) { + t = rep->info[i]->expires; + tm = localtime(&t); + strftime(str_since, sizeof(str_since), DATE_TIME_FMT, tm); + } else { + str_since[0] = 0; + } + + fprintf(out, "%14s %14u %30s\n", + rep->info[i]->ip, (unsigned)rep->info[i]->score, str_since); + } + + ret = 0; + goto cleanup; + + error: + ret = 1; + fprintf(stderr, ERR_SERVER_UNREACHABLE); + + cleanup: + if (rep != NULL) + ban_list_rep__free_unpacked(rep, &pa); + + free_reply(&raw); + pager_stop(out); + + return ret; +} + int print_list_entries(FILE* out, const char* name, char **val, unsigned vsize) { const char * tmp; diff --git a/src/occtl.c b/src/occtl.c index 154a9af5..c8a2a7f8 100644 --- a/src/occtl.c +++ b/src/occtl.c @@ -51,12 +51,16 @@ static const commands_st commands[] = { "Disconnect the specified user", 1, 1), ENTRY("disconnect id", "[ID]", handle_disconnect_id_cmd, "Disconnect the specified ID", 1, 1), + ENTRY("unban", "[IP]", handle_unban_ip_cmd, + "Unban the specified IP", 1, 1), ENTRY("reload", NULL, handle_reload_cmd, "Reloads the server configuration", 1, 1), ENTRY("show status", NULL, handle_status_cmd, "Prints the status of the server", 1, 1), ENTRY("show users", NULL, handle_list_users_cmd, "Prints the connected users", 1, 1), + ENTRY("show banned", NULL, handle_list_banned_cmd, + "Prints the banned IP addresses", 1, 1), ENTRY("show user", "[NAME]", handle_show_user_cmd, "Prints information on the specified user", 1, 1), ENTRY("show id", "[ID]", handle_show_id_cmd, diff --git a/src/occtl.h b/src/occtl.h index 71d3eea6..dea62b50 100644 --- a/src/occtl.h +++ b/src/occtl.h @@ -53,9 +53,11 @@ typedef int (*cmd_func) (CONN_TYPE * conn, const char *arg); int handle_status_cmd(CONN_TYPE * conn, const char *arg); int handle_list_users_cmd(CONN_TYPE * conn, const char *arg); +int handle_list_banned_cmd(CONN_TYPE * conn, const char *arg); int handle_show_user_cmd(CONN_TYPE * conn, const char *arg); int handle_show_id_cmd(CONN_TYPE * conn, const char *arg); int handle_disconnect_user_cmd(CONN_TYPE * conn, const char *arg); +int handle_unban_ip_cmd(CONN_TYPE * conn, const char *arg); int handle_disconnect_id_cmd(CONN_TYPE * conn, const char *arg); int handle_reload_cmd(CONN_TYPE * conn, const char *arg); int handle_stop_cmd(CONN_TYPE * conn, const char *arg);