Fix bug #185 - Args of !commands lost/damaged.

All string arguments were incorrectly freed after being added to the argument list for a command.
Instead this fix makes sure it is properly copied into a new string, and by doing so this requires
a new API for dealing with hub command arguments in a type safe manner, and also allows for each
argument to be cleaned up properly when the command is no longer needed.

This also fixes issues with parse errors for certain types, and optional arguments (previously it was impossible
to tell the difference for an integer with value 0 or if no integer was given).
This commit is contained in:
Jan Vidar Krey 2012-03-12 01:21:08 +01:00
parent 390b63e80a
commit 714d110a94
2 changed files with 134 additions and 68 deletions

View File

@ -95,16 +95,36 @@ static int command_is_available(struct command_handle* handle, const struct hub_
return handle->cred <= user->credentials;
}
void hub_command_args_free(struct hub_command* cmd)
{
struct hub_command_arg_data* data = NULL;
if (!cmd->args)
return;
for (data = (struct hub_command_arg_data*) list_get_first(cmd->args); data; data = (struct hub_command_arg_data*) list_get_next(cmd->args))
{
switch (data->type)
{
case type_string:
hub_free(data->data.string);
break;
default:
break;
}
}
list_clear(cmd->args, hub_free);
list_destroy(cmd->args);
cmd->args = NULL;
}
void command_free(struct hub_command* cmd)
{
if (!cmd) return;
hub_free(cmd->prefix);
if (cmd->args)
{
list_clear(cmd->args, &null_free);
list_destroy(cmd->args);
}
hub_command_args_free(cmd);
hub_free(cmd);
}
@ -124,33 +144,27 @@ static struct command_handle* command_handler_lookup(struct command_base* cbase,
return NULL;
}
static enum command_parse_status command_extract_arguments(struct command_base* cbase, const struct hub_user* user, struct command_handle* command, struct linked_list* tokens, struct linked_list** args)
static enum command_parse_status command_extract_arguments(struct command_base* cbase, const struct hub_user* user, struct command_handle* command, struct linked_list* tokens, struct linked_list* args)
{
int arg = 0;
int opt = 0;
char arg_code;
char* token = NULL;
struct hub_user* target = NULL;
struct command_handle* target_command = NULL;
enum auth_credentials cred;
struct hub_command_arg_data* data = NULL;
enum command_parse_status status = cmd_status_ok;
int args_addr_range = 0;
int temp_num;
// Ignore the first token since it is the prefix.
token = list_get_first(tokens);
list_remove(tokens, token);
hub_free(token);
uhub_assert(args);
*args = list_create();
while ((arg_code = command->args[arg++]))
while (status == cmd_status_ok && (arg_code = command->args[arg++]))
{
token = list_get_first(tokens);
if (!token || !*token)
{
status = (arg_code == '?') ? cmd_status_ok : cmd_status_missing_args;
status = (arg_code == '?' ? cmd_status_ok : cmd_status_missing_args);
break;
}
@ -161,90 +175,113 @@ static enum command_parse_status command_extract_arguments(struct command_base*
continue;
case 'n':
target = uman_get_user_by_nick(cbase->hub, token);
if (!target)
data = hub_malloc(sizeof(*data));
data->type = type_user;
data->data.user = uman_get_user_by_nick(cbase->hub, token);
if (!data->data.user)
{
hub_free(data);
data = NULL;
status = cmd_status_arg_nick;
goto parse_arguments_error;
}
list_append(*args, target);
break;
case 'i':
target = uman_get_user_by_cid(cbase->hub, token);
if (!target)
data = hub_malloc(sizeof(*data));
data->type = type_user;
data->data.user = uman_get_user_by_cid(cbase->hub, token);
if (!data->data.user)
{
hub_free(data);
data = NULL;
status = cmd_status_arg_cid;
goto parse_arguments_error;
}
list_append(*args, target);
break;
case 'a':
uhub_assert(args_addr_range == 0 || !"BUG: Can only be one address range argument per command!");
if (!ip_convert_address_to_range(token, &cbase->range))
data = hub_malloc(sizeof(*data));
data->type = type_address;
if (ip_convert_to_binary(token, data->data.address) == -1)
{
hub_free(data);
data = NULL;
status = cmd_status_arg_address;
}
break;
case 'r':
data = hub_malloc(sizeof(*data));
data->type = type_range;
if (!ip_convert_address_to_range(token, data->data.range))
{
hub_free(data);
data = NULL;
status = cmd_status_arg_address;
goto parse_arguments_error;
}
list_append(*args, &cbase->range);
args_addr_range++;
break;
case 'm':
case 'p':
list_append(*args, token);
data = hub_malloc(sizeof(*data));
data->type = type_string;
data->data.string = strdup(token);
break;
case 'c':
target_command = command_handler_lookup(cbase, token);
if (!target_command)
data = hub_malloc(sizeof(*data));
data->type = type_command;
data->data.command = command_handler_lookup(cbase, token);
if (!data->data.command)
{
hub_free(data);
data = NULL;
status = cmd_status_arg_command;
goto parse_arguments_error;
}
list_append(*args, target_command);
break;
case 'C':
if (!auth_string_to_cred(token, &cred))
data = hub_malloc(sizeof(*data));
data->type = type_credentials;
if (!auth_string_to_cred(token, &data->data.credentials))
{
hub_free(data);
data = NULL;
status = cmd_status_arg_cred;
goto parse_arguments_error;
}
list_append(*args, (void*) cred);
break;
case 'N':
if (!is_number(token, &temp_num))
data->type = type_integer;
if (!is_number(token, &data->data.integer))
{
status = cmd_status_arg_number;
goto parse_arguments_error;
hub_free(data);
data = NULL;
return cmd_status_arg_number;
}
list_append(*args, (void*) (int*) (intptr_t) temp_num);
break;
case '\0':
if (!opt)
{
status = cmd_status_missing_args;
goto parse_arguments_error;
}
else
{
status = cmd_status_ok;
break;
}
}
if (data)
{
list_append(args, data);
data = NULL;
}
list_remove(tokens, token);
hub_free(token);
}
return status;
parse_arguments_error:
list_clear(*args, &null_free);
list_destroy(*args);
*args = NULL;
hub_free(data);
return status;
}
@ -287,7 +324,7 @@ struct hub_command* command_parse(struct command_base* cbase, const struct hub_u
cmd->status = cmd_status_ok;
cmd->message = message;
cmd->prefix = NULL;
cmd->args = NULL;
cmd->args = list_create();
cmd->user = user;
if (split_string(message, " ", tokens, 0) <= 0)
@ -305,7 +342,7 @@ struct hub_command* command_parse(struct command_base* cbase, const struct hub_u
goto command_parse_cleanup;
// Parse arguments
cmd->status = command_extract_arguments(cbase, user, handle, tokens, &cmd->args);
cmd->status = command_extract_arguments(cbase, user, handle, tokens, cmd->args);
goto command_parse_cleanup;
command_parse_cleanup:
@ -465,7 +502,8 @@ static int command_help(struct command_base* cbase, struct hub_user* user, struc
{
size_t n;
struct cbuffer* buf = cbuf_create(MAX_HELP_LINE);
struct command_handle* command = list_get_first(cmd->args);
struct hub_command_arg_data* data = list_get_first(cmd->args);
struct command_handle* command = data->data.command;
if (!command)
{
@ -724,6 +762,8 @@ static int command_log(struct command_base* cbase, struct hub_user* user, struct
}
buf = cbuf_create(128);
cbuf_append_format(buf, "Logged entries: " PRINTF_SIZE_T, list_size(messages));
search = list_get_first(cmd->args);
if (search)
{
@ -732,11 +772,7 @@ static int command_log(struct command_base* cbase, struct hub_user* user, struct
if (search_len)
{
cbuf_append_format(buf, "Logged entries: " PRINTF_SIZE_T ", searching for \"%s\"", list_size(messages), search);
}
else
{
cbuf_append_format(buf, "Logged entries: " PRINTF_SIZE_T, list_size(messages));
cbuf_append_format(buf, ", searching for \"%s\"", list_size(messages), search);
}
command_status(cbase, user, cmd, buf);
@ -785,7 +821,8 @@ static int command_register(struct command_base* cbase, struct hub_user* user, s
{
struct cbuffer* buf = cbuf_create(128);
struct auth_info data;
char* password = list_get_first(cmd->args);
struct hub_command_arg_data* args = (struct hub_command_arg_data*) list_get_first(cmd->args);
char* password = args->data.string;
strncpy(data.nickname, user->id.nick, MAX_NICK_LEN);
strncpy(data.password, password, MAX_PASS_LEN);
@ -808,7 +845,8 @@ static int command_password(struct command_base* cbase, struct hub_user* user, s
{
struct cbuffer* buf = cbuf_create(128);
struct auth_info data;
char* password = list_get_first(cmd->args);
struct hub_command_arg_data* args = (struct hub_command_arg_data*) list_get_first(cmd->args);
char* password = args->data.string;
strncpy(data.nickname, user->id.nick, MAX_NICK_LEN);
strncpy(data.password, password, MAX_PASS_LEN);
@ -930,24 +968,24 @@ void commands_builtin_add(struct command_base* cbase)
ADD_COMMAND("getip", 5, "n", auth_cred_operator, command_getip, "Show IP address for a user" );
ADD_COMMAND("help", 4, "?c",auth_cred_guest, command_help, "Show this help message." );
ADD_COMMAND("kick", 4, "n", auth_cred_operator, command_kick, "Kick a user" );
ADD_COMMAND("log", 3, "", auth_cred_operator, command_log, "Display log" );
ADD_COMMAND("log", 3, "?m", auth_cred_operator, command_log, "Display log" ); // fail
ADD_COMMAND("mute", 4, "n", auth_cred_operator, command_mute, "Mute user" );
ADD_COMMAND("myip", 4, "", auth_cred_guest, command_myip, "Show your own IP." );
ADD_COMMAND("register", 8, "p", auth_cred_guest, command_register, "Register your username." );
ADD_COMMAND("register", 8, "p", auth_cred_guest, command_register, "Register your username." ); // fail
ADD_COMMAND("reload", 6, "", auth_cred_admin, command_reload, "Reload configuration files." );
ADD_COMMAND("password", 8, "p", auth_cred_user, command_password, "Change your own password." );
ADD_COMMAND("password", 8, "p", auth_cred_user, command_password, "Change your own password." ); // fail
ADD_COMMAND("shutdown", 8, "", auth_cred_admin, command_shutdown_hub, "Shutdown hub." );
ADD_COMMAND("stats", 5, "", auth_cred_super, command_stats, "Show hub statistics." );
ADD_COMMAND("unban", 5, "n", auth_cred_operator, command_unban, "Lift ban on a user" );
ADD_COMMAND("unban", 5, "n", auth_cred_operator, command_unban, "Lift ban on a user" ); // not implemented
ADD_COMMAND("unmute", 6, "n", auth_cred_operator, command_mute, "Unmute user" );
ADD_COMMAND("uptime", 6, "", auth_cred_guest, command_uptime, "Display hub uptime info." );
ADD_COMMAND("useradd", 7, "np",auth_cred_operator, command_useradd, "Register a new user." );
ADD_COMMAND("userdel", 7, "n", auth_cred_operator, command_userdel, "Delete a registered user." );
ADD_COMMAND("userinfo", 8, "n", auth_cred_operator, command_userinfo, "Show registered user info." );
ADD_COMMAND("usermod", 7, "nC",auth_cred_admin, command_usermod, "Modify user credentials." );
ADD_COMMAND("userpass", 8, "np",auth_cred_operator, command_userpass, "Change password for a user." );
ADD_COMMAND("useradd", 7, "np",auth_cred_operator, command_useradd, "Register a new user." ); // fail
ADD_COMMAND("userdel", 7, "n", auth_cred_operator, command_userdel, "Delete a registered user." ); // fail
ADD_COMMAND("userinfo", 8, "n", auth_cred_operator, command_userinfo, "Show registered user info." ); // fail
ADD_COMMAND("usermod", 7, "nC",auth_cred_admin, command_usermod, "Modify user credentials." ); // fail
ADD_COMMAND("userpass", 8, "np",auth_cred_operator, command_userpass, "Change password for a user." ); // fail
ADD_COMMAND("version", 7, "", auth_cred_guest, command_version, "Show hub version info." );
ADD_COMMAND("whoip", 5, "a", auth_cred_operator, command_whoip, "Show users matching IP range" );
ADD_COMMAND("whoip", 5, "a", auth_cred_operator, command_whoip, "Show users matching IP range" ); // ok
#ifdef DEBUG_UNLOAD_PLUGINS
ADD_COMMAND("load", 4, "", auth_cred_admin, command_load, "Load plugins." );

View File

@ -41,6 +41,33 @@ enum command_parse_status
cmd_status_arg_command, /** <<< "A command argument is not valid ('c')" */
};
struct hub_command_arg_data
{
enum Type {
type_integer,
type_string,
type_user,
type_address,
type_range,
type_credentials,
type_command
} type;
union {
int integer;
char* string;
struct hub_user* user;
struct ip_addr_encap* address;
struct ip_range* range;
enum auth_credentials credentials;
struct command_handle* command;
} data;
struct hub_command_arg_data* next;
};
void hub_command_args_free(struct hub_command* command);
/**
* This struct contains all information needed to invoke
* a command, which includes the whole message, the prefix,
@ -70,6 +97,7 @@ struct hub_command
* n = nick name (must exist in hub session)
* i = CID (must exist in hub)
* a = (IP) address (must be a valid IPv4 or IPv6 address)
* r = (IP) address range (either: IP-IP or IP/mask, both IPv4 or IPv6 work)
* m = message (string)
* p = password (string)
* C = credentials (see auth_string_to_cred).