degesch: irc_{host,port} -> addresses

Now you can finally specify multiple addresses to connect to.
This commit is contained in:
Přemysl Eric Janouch 2015-05-14 06:22:58 +02:00
parent c5b38842bf
commit a77ab689eb
2 changed files with 109 additions and 52 deletions

125
degesch.c
View File

@ -1249,6 +1249,29 @@ config_validate_nonjunk_string
return true; return true;
} }
static bool
config_validate_addresses
(const struct config_item_ *item, struct error **e)
{
if (item->type == CONFIG_ITEM_NULL)
return true;
if (!config_validate_nonjunk_string (item, e))
return false;
// Comma-separated list of "host[:port]" pairs
regex_t re;
int err = regcomp (&re, "^([^/:,]+(:[^/:,]+)?)?"
"(,([^/:,]+(:[^/:,]+)?)?)*$", REG_EXTENDED | REG_NOSUB);
hard_assert (!err);
bool result = !regexec (&re, item->value.string.str, 0, NULL, 0);
if (!result)
error_set (e, "invalid address list string");
regfree (&re);
return result;
}
static bool static bool
config_validate_nonnegative config_validate_nonnegative
(const struct config_item_ *item, struct error **e) (const struct config_item_ *item, struct error **e)
@ -1279,15 +1302,10 @@ static struct config_schema g_config_server[] =
.type = CONFIG_ITEM_STRING, .type = CONFIG_ITEM_STRING,
.validate = config_validate_nonjunk_string }, .validate = config_validate_nonjunk_string },
{ .name = "irc_host", { .name = "addresses",
.comment = "Address of the IRC server", .comment = "Addresses of the IRC network (e.g. \"irc.net:6667\")",
.type = CONFIG_ITEM_STRING, .type = CONFIG_ITEM_STRING_ARRAY,
.validate = config_validate_nonjunk_string }, .validate = config_validate_addresses },
{ .name = "irc_port",
.comment = "Port of the IRC server",
.type = CONFIG_ITEM_INTEGER,
.validate = config_validate_nonnegative,
.default_ = "6667" },
{ .name = "ssl", { .name = "ssl",
.comment = "Whether to use SSL/TLS", .comment = "Whether to use SSL/TLS",
@ -3303,9 +3321,24 @@ irc_on_connector_connected (void *user_data, int socket)
irc_finish_connection (s, socket); irc_finish_connection (s, socket);
} }
static void
irc_split_host_port (char *s, char **host, char **port)
{
char *colon = strchr (s, ':');
if (colon)
{
*colon = '\0';
*port = ++colon;
}
else
*port = "6667";
*host = s;
}
static bool static bool
irc_setup_connector (struct server *s, irc_setup_connector (struct server *s,
const char *host, const char *port, struct error **e) const struct str_vector *addresses, struct error **e)
{ {
struct connector *connector = xmalloc (sizeof *connector); struct connector *connector = xmalloc (sizeof *connector);
connector_init (connector, &s->ctx->poller); connector_init (connector, &s->ctx->poller);
@ -3319,33 +3352,28 @@ irc_setup_connector (struct server *s,
s->state = IRC_CONNECTING; s->state = IRC_CONNECTING;
s->connector = connector; s->connector = connector;
for (size_t i = 0; i < addresses->len; i++)
{
char *host, *port;
irc_split_host_port (addresses->vector[i], &host, &port);
if (!connector_add_target (connector, host, port, e)) if (!connector_add_target (connector, host, port, e))
{ {
irc_destroy_connector (s); irc_destroy_connector (s);
return false; return false;
} }
}
connector_step (connector); connector_step (connector);
return true; return true;
} }
static void static bool
irc_initiate_connect (struct server *s) irc_initiate_connect_socks (struct server *s,
const struct str_vector *addresses, struct error **e)
{ {
hard_assert (s->state == IRC_DISCONNECTED);
struct app_context *ctx = s->ctx; struct app_context *ctx = s->ctx;
const char *irc_host = get_config_string (ctx, "server.irc_host");
int64_t irc_port_int = get_config_integer (ctx, "server.irc_port");
if (!get_config_string (ctx, "server.irc_host"))
{
// No sense in trying to reconnect
buffer_send_error (ctx, s->buffer,
"No hostname specified in configuration");
return;
}
const char *socks_host = get_config_string (ctx, "server.socks_host"); const char *socks_host = get_config_string (ctx, "server.socks_host");
int64_t socks_port_int = get_config_integer (ctx, "server.socks_port"); int64_t socks_port_int = get_config_integer (ctx, "server.socks_port");
@ -3354,13 +3382,15 @@ irc_initiate_connect (struct server *s)
const char *socks_password = const char *socks_password =
get_config_string (ctx, "server.socks_password"); get_config_string (ctx, "server.socks_password");
char *irc_port = xstrdup_printf ("%" PRIi64, irc_port_int); if (!socks_host)
return false;
// FIXME: we only try the first address (still better than nothing)
char *irc_host, *irc_port;
irc_split_host_port (addresses->vector[0], &irc_host, &irc_port);
char *socks_port = xstrdup_printf ("%" PRIi64, socks_port_int); char *socks_port = xstrdup_printf ("%" PRIi64, socks_port_int);
// TODO: the SOCKS code needs a rewrite so that we don't block on it either
struct error *e = NULL;
if (socks_host)
{
char *address = format_host_port_pair (irc_host, irc_port); char *address = format_host_port_pair (irc_host, irc_port);
char *socks_address = format_host_port_pair (socks_host, socks_port); char *socks_address = format_host_port_pair (socks_host, socks_port);
buffer_send_status (ctx, s->buffer, buffer_send_status (ctx, s->buffer,
@ -3368,22 +3398,49 @@ irc_initiate_connect (struct server *s)
free (socks_address); free (socks_address);
free (address); free (address);
// TODO: the SOCKS code needs a rewrite so that we don't block on it either;
// perhaps it could act as a special kind of connector
struct error *error = NULL; struct error *error = NULL;
bool result = true;
int fd = socks_connect (socks_host, socks_port, irc_host, irc_port, int fd = socks_connect (socks_host, socks_port, irc_host, irc_port,
socks_username, socks_password, &error); socks_username, socks_password, &error);
if (fd != -1) if (fd != -1)
irc_finish_connection (s, fd); irc_finish_connection (s, fd);
else else
{ {
error_set (&e, "%s: %s", "SOCKS connection failed", error->message); error_set (e, "%s: %s", "SOCKS connection failed", error->message);
error_free (error); error_free (error);
result = false;
} }
}
else
irc_setup_connector (s, irc_host, irc_port, &e);
free (irc_port);
free (socks_port); free (socks_port);
return true;
}
static void
irc_initiate_connect (struct server *s)
{
hard_assert (s->state == IRC_DISCONNECTED);
struct app_context *ctx = s->ctx;
const char *addresses = get_config_string (ctx, "server.addresses");
if (!addresses || !addresses[strspn (addresses, ",")])
{
// No sense in trying to reconnect
buffer_send_error (ctx, s->buffer,
"No addresses specified in configuration");
return;
}
struct str_vector servers;
str_vector_init (&servers);
split_str_ignore_empty (addresses, ',', &servers);
struct error *e = NULL;
if (!irc_initiate_connect_socks (s, &servers, &e) && !e)
irc_setup_connector (s, &servers, &e);
str_vector_free (&servers);
if (e) if (e)
{ {

2
test
View File

@ -20,7 +20,7 @@ expect_after {
} }
# Connect to the daemon # Connect to the daemon
send "/set server.irc_host = \"localhost\"\n" send "/set server.addresses = \"localhost\"\n"
expect "Option changed" expect "Option changed"
send "/disconnect\n" send "/disconnect\n"
expect "]" expect "]"