Browse Source

Add listen mask checks to angel

personal/stbuehler/wip
Stefan Bühler 13 years ago
parent
commit
007e5e040c
  1. 27
      include/lighttpd/angel_plugin_core.h
  2. 1
      include/lighttpd/ip_parsers.h
  3. 5
      include/lighttpd/utils.h
  4. 8
      src/angel/angel_plugin.c
  5. 158
      src/angel/angel_plugin_core.c
  6. 2
      src/angel/angel_proc.c
  7. 12
      src/common/ip_parsers.rl
  8. 30
      src/common/utils.c
  9. 5
      src/main/angel_fake.c
  10. 24
      src/main/condition.c

27
include/lighttpd/angel_plugin_core.h

@ -8,10 +8,37 @@ struct liPluginCoreConfig {
/* Load */
liInstanceConf *load_instconf;
gboolean load_failed;
GPtrArray *load_listen_masks;
/* Running */
liInstanceConf *instconf;
liInstance *inst;
GPtrArray *listen_masks;
};
typedef struct liPluginCoreListenMask liPluginCoreListenMask;
struct liPluginCoreListenMask {
enum {
LI_PLUGIN_CORE_LISTEN_MASK_IPV4,
LI_PLUGIN_CORE_LISTEN_MASK_IPV6,
LI_PLUGIN_CORE_LISTEN_MASK_UNIX
} type;
union {
struct {
guint32 addr;
guint32 networkmask;
guint16 port;
} ipv4;
struct {
guint8 addr[16];
guint network;
guint16 port;
} ipv6;
struct {
GString *path;
} unix_socket;
} value;
};
gboolean plugin_core_init(liServer *srv);

1
include/lighttpd/ip_parsers.h

@ -3,6 +3,7 @@
#include <lighttpd/settings.h>
/** optional parameters are set to default values (netmask all bits, port 0) */
/** parse an IPv4 (if netmask is not NULL with optional cidr netmask, if port is not NULL with optional port) */
LI_API gboolean li_parse_ipv4(const char *str, guint32 *ip, guint32 *netmask, guint16 *port);
/** parse an IPv6 (if network is not NULL with optional cidr network, if port is not NULL with optional port if the ip/cidr part is in [...]) */

5
include/lighttpd/utils.h

@ -64,6 +64,11 @@ LI_API liSocketAddress li_sockaddr_local_from_socket(gint fd);
LI_API liSocketAddress li_sockaddr_remote_from_socket(gint fd);
LI_API void li_sockaddr_clear(liSocketAddress *saddr);
LI_API gboolean ipv4_in_ipv4_net(guint32 target, guint32 match, guint32 networkmask);
LI_API gboolean ipv6_in_ipv6_net(const unsigned char *target, const guint8 *match, guint network);
LI_API gboolean ipv6_in_ipv4_net(const unsigned char *target, guint32 match, guint32 networkmask);
LI_API gboolean ipv4_in_ipv6_net(guint32 target, const guint8 *match, guint network);
LI_API void li_gstring_replace_char_with_str_len(GString *gstr, gchar c, gchar *str, guint len);
LI_API gboolean li_strncase_equal(GString *str, const gchar *s, guint len);

8
src/angel/angel_plugin.c

@ -165,18 +165,18 @@ gboolean plugins_config_load(liServer *srv, const gchar *filename) {
}
}
ERROR(srv, "%s", "activate");
INFO(srv, "%s", "activate");
/* activate new config */
for (i = ps->load_plugins->len; i-- > 0; ) {
liPlugin *p = g_ptr_array_index(ps->load_plugins, i);
ERROR(srv, "activate: %s", p->name);
INFO(srv, "activate: %s", p->name);
if (p->handle_activate_config) {
p->handle_activate_config(srv, p);
}
}
ERROR(srv, "%s", "done");
INFO(srv, "%s", "done");
{ /* swap the arrays */
GPtrArray *tmp = ps->load_plugins; ps->load_plugins = ps->plugins; ps->plugins = tmp;
@ -205,7 +205,7 @@ void plugins_handle_item(liServer *srv, GString *itemname, liValue *hash) {
liPlugins *ps = &srv->plugins;
server_item *si;
#if 1
#if 0
/* debug items */
{
GString *tmp = li_value_to_string(hash);

158
src/angel/angel_plugin_core.c

@ -99,22 +99,115 @@ static const liPluginItemOption core_instance_options[] = {
{ NULL, 0, 0 }
};
static void core_listen_mask_free(liPluginCoreListenMask *mask) {
switch (mask->type) {
case LI_PLUGIN_CORE_LISTEN_MASK_IPV4:
case LI_PLUGIN_CORE_LISTEN_MASK_IPV6:
break;
case LI_PLUGIN_CORE_LISTEN_MASK_UNIX:
g_string_free(mask->value.unix_socket.path, TRUE);
break;
}
g_slice_free(liPluginCoreListenMask, mask);
}
static void core_listen_parse(liServer *srv, liPlugin *p, liValue **options) {
liPluginCoreConfig *config = (liPluginCoreConfig*) p->data;
gboolean have_type = FALSE;
liPluginCoreListenMask *mask = g_slice_new0(liPluginCoreListenMask);
if (options[0]) { /* ip */
if (have_type) goto only_one_type;
have_type = TRUE;
if (li_parse_ipv4(options[0]->data.string->str, &mask->value.ipv4.addr, &mask->value.ipv4.networkmask, &mask->value.ipv4.port)) {
mask->type = LI_PLUGIN_CORE_LISTEN_MASK_IPV4;
} else if (li_parse_ipv6(options[0]->data.string->str, mask->value.ipv6.addr, &mask->value.ipv6.network, &mask->value.ipv6.port)) {
mask->type = LI_PLUGIN_CORE_LISTEN_MASK_IPV6;
} else {
ERROR(srv, "couldn't parse ip/network:port in listen mask '%s'", options[0]->data.string->str);
config->load_failed = FALSE;
g_slice_free(liPluginCoreListenMask, mask);
return;
}
}
if (options[1]) { /* unix */
if (have_type) goto only_one_type;
have_type = TRUE;
mask->type = LI_PLUGIN_CORE_LISTEN_MASK_UNIX;
mask->value.unix_socket.path = g_string_new_len(GSTR_LEN(options[2]->data.string));
}
if (!have_type) {
ERROR(srv, "%s", "no options found in listen mask");
config->load_failed = FALSE;
g_slice_free(liPluginCoreListenMask, mask);
return;
}
g_ptr_array_add(config->load_listen_masks, mask);
return;
only_one_type:
ERROR(srv, "%s", "you can only use one of 'ip' and 'unix' in listen masks");
config->load_failed = FALSE;
g_slice_free(liPluginCoreListenMask, mask);
return;
}
static const liPluginItemOption core_listen_options[] = {
{ "ip", LI_VALUE_STRING, 0 },
{ "unix", LI_VALUE_STRING, 0 },
{ NULL, 0, 0 }
};
static const liPluginItem core_items[] = {
{ "instance", core_instance_parse, core_instance_options },
{ "listen", core_listen_parse, core_listen_options },
{ NULL, NULL, NULL }
};
static int do_listen(liServer *srv, GString *str) {
static int do_listen(liServer *srv, liPluginCoreConfig *config, GString *str) {
guint32 ipv4;
#ifdef HAVE_IPV6
guint8 ipv6[16];
#endif
guint16 port = 80;
guint16 port;
guint i;
liPluginCoreListenMask *mask;
if (li_parse_ipv4(str->str, &ipv4, NULL, &port)) {
int s, v;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
if (!port) port = 80;
if (config->listen_masks->len) {
for (i = 0; i < config->listen_masks->len; i++) {
mask = g_ptr_array_index(config->listen_masks, i);
switch (mask->type) {
case LI_PLUGIN_CORE_LISTEN_MASK_IPV4:
if (!ipv4_in_ipv4_net(ipv4, mask->value.ipv4.addr, mask->value.ipv4.networkmask)) continue;
if ((mask->value.ipv4.port != port) && (mask->value.ipv4.port != 0 || (port != 80 && port != 443))) continue;
break;
case LI_PLUGIN_CORE_LISTEN_MASK_IPV6:
if (!ipv4_in_ipv6_net(ipv4, mask->value.ipv6.addr, mask->value.ipv6.network)) continue;
if ((mask->value.ipv6.port != port) && (mask->value.ipv6.port != 0 || (port != 80 && port != 443))) continue;
break;
case LI_PLUGIN_CORE_LISTEN_MASK_UNIX:
continue;
}
break;
}
if (i == config->listen_masks->len) {
ERROR(srv, "listen to socket '%s' not allowed", str->str);
return -1;
}
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ipv4;
addr.sin_port = htons(port);
@ -146,6 +239,30 @@ static int do_listen(liServer *srv, GString *str) {
int s, v;
struct sockaddr_in6 addr;
li_ipv6_tostring(ipv6_str, ipv6);
if (!port) port = 80;
if (config->listen_masks->len) {
for (i = 0; i < config->listen_masks->len; i++) {
mask = g_ptr_array_index(config->listen_masks, i);
switch (mask->type) {
case LI_PLUGIN_CORE_LISTEN_MASK_IPV4:
if (!ipv6_in_ipv4_net(ipv6, mask->value.ipv4.addr, mask->value.ipv4.networkmask)) continue;
if ((mask->value.ipv4.port != port) && (mask->value.ipv4.port != 0 || (port != 80 && port != 443))) continue;
break;
case LI_PLUGIN_CORE_LISTEN_MASK_IPV6:
if (!ipv6_in_ipv6_net(ipv6, mask->value.ipv6.addr, mask->value.ipv6.network)) continue;
if ((mask->value.ipv6.port != port) && (mask->value.ipv6.port != 0 || (port != 80 && port != 443))) continue;
break;
case LI_PLUGIN_CORE_LISTEN_MASK_UNIX:
continue;
}
break;
}
if (i == config->listen_masks->len) {
ERROR(srv, "listen to socket '%s' not allowed", str->str);
return -1;
}
}
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
@ -185,6 +302,7 @@ static int do_listen(liServer *srv, GString *str) {
g_string_free(ipv6_str, TRUE);
return s;
#endif
/* TODO: listen unix socket */
} else {
ERROR(srv, "Invalid ip: '%s'", str->str);
return -1;
@ -195,13 +313,13 @@ static void core_listen(liServer *srv, liInstance *i, liPlugin *p, gint32 id, GS
GError *err = NULL;
gint fd;
GArray *fds;
UNUSED(p);
liPluginCoreConfig *config = (liPluginCoreConfig*) p->data;
DEBUG(srv, "core_listen(%i) '%s'", id, data->str);
if (-1 == id) return; /* ignore simple calls */
fd = do_listen(srv, data);
fd = do_listen(srv, config, data);
if (-1 == fd) {
GString *error = g_string_sized_new(0);
@ -242,6 +360,7 @@ static void core_reached_state(liServer *srv, liInstance *i, liPlugin *p, gint32
static void core_clean(liServer *srv, liPlugin *p);
static void core_free(liServer *srv, liPlugin *p) {
liPluginCoreConfig *config = (liPluginCoreConfig*) p->data;
guint i;
core_clean(srv, p);
@ -255,10 +374,19 @@ static void core_free(liServer *srv, liPlugin *p) {
li_instance_release(config->inst);
config->inst = NULL;
}
for (i = 0; i < config->listen_masks->len; i++) {
core_listen_mask_free(g_ptr_array_index(config->listen_masks, i));
}
g_ptr_array_free(config->listen_masks, TRUE);
g_ptr_array_free(config->load_listen_masks, TRUE);
config->listen_masks = NULL;
config->load_listen_masks = NULL;
}
static void core_clean(liServer *srv, liPlugin *p) {
liPluginCoreConfig *config = (liPluginCoreConfig*) p->data;
guint i;
UNUSED(srv);
if (config->load_instconf) {
@ -266,6 +394,11 @@ static void core_clean(liServer *srv, liPlugin *p) {
config->load_instconf = NULL;
}
for (i = 0; i < config->load_listen_masks->len; i++) {
core_listen_mask_free(g_ptr_array_index(config->load_listen_masks, i));
}
g_ptr_array_set_size(config->load_listen_masks, 0);
config->load_failed = FALSE;
}
@ -277,6 +410,8 @@ static gboolean core_check(liServer *srv, liPlugin *p) {
static void core_activate(liServer *srv, liPlugin *p) {
liPluginCoreConfig *config = (liPluginCoreConfig*) p->data;
GPtrArray *tmp_ptrarray;
guint i;
if (config->instconf) {
li_instance_conf_release(config->instconf);
@ -289,19 +424,27 @@ static void core_activate(liServer *srv, liPlugin *p) {
config->inst = NULL;
}
for (i = 0; i < config->listen_masks->len; i++) {
core_listen_mask_free(g_ptr_array_index(config->listen_masks, i));
}
g_ptr_array_set_size(config->listen_masks, 0);
config->instconf = config->load_instconf;
config->load_instconf = NULL;
tmp_ptrarray = config->load_listen_masks; config->load_listen_masks = config->listen_masks; config->listen_masks = tmp_ptrarray;
if (config->instconf) {
config->inst = li_server_new_instance(srv, config->instconf);
li_instance_set_state(config->inst, LI_INSTANCE_RUNNING);
ERROR(srv, "%s", "Starting instance");
}
}
static gboolean core_init(liServer *srv, liPlugin *p) {
liPluginCoreConfig *config;
UNUSED(srv);
p->data = g_slice_new0(liPluginCoreConfig);
p->data = config = g_slice_new0(liPluginCoreConfig);
p->items = core_items;
p->handle_free = core_free;
@ -309,6 +452,9 @@ static gboolean core_init(liServer *srv, liPlugin *p) {
p->handle_check_config = core_check;
p->handle_activate_config = core_activate;
config->listen_masks = g_ptr_array_new();
config->load_listen_masks = g_ptr_array_new();
g_hash_table_insert(p->angel_callbacks, "listen", (gpointer)(intptr_t)core_listen);
g_hash_table_insert(p->angel_callbacks, "reached-state", (gpointer)(intptr_t)core_reached_state);

2
src/angel/angel_proc.c

@ -15,7 +15,7 @@ static void read_pipe(liServer *srv, liErrorPipe *epipe, gboolean flush) {
if (ioctl(epipe->fds[0], FIONREAD, &toread) || toread == 0) {
toread = 256;
} else {
if (toread > max_read) toread = max_read;
if (toread < 0 || toread > max_read) toread = max_read;
}
buf = g_string_sized_new(toread);

12
src/common/ip_parsers.rl

@ -25,9 +25,9 @@
end = (space | 0) @{ cs = ipv4_parser_first_final; fbreak; };
ipv4 := ipv4_data end;
ipv4_cidr := ipv4_data netmask end;
ipv4_socket := ipv4_data port end;
ipv4_socket_cidr := ipv4_data netmask port end;
ipv4_cidr := ipv4_data netmask? end;
ipv4_socket := ipv4_data port? end;
ipv4_socket_cidr := ipv4_data netmask? port? end;
write data;
}%%
@ -38,6 +38,9 @@ gboolean li_parse_ipv4(const char *str, guint32 *ip, guint32 *netmask, guint16 *
gboolean res = TRUE;
int cs, tmpval = 0, i = 0;
if (netmask) *netmask = 0xffffffffu;
if (port) *port = 0;
%% write init nocs;
cs = netmask
@ -116,6 +119,9 @@ gboolean li_parse_ipv6(const char *str, guint8 *ip, guint *network, guint16 *por
gboolean res = TRUE, compressed = FALSE;
int cs, tmpval = 0, i = 0;
if (network) *network = 128;
if (port) *port = 0;
%% write init nocs;
cs = network

30
src/common/utils.c

@ -583,7 +583,7 @@ liSocketAddress li_sockaddr_from_string(GString *str, guint tcp_default_port) {
#ifdef HAVE_IPV6
guint8 ipv6[16];
#endif
guint16 port = tcp_default_port;
guint16 port;
liSocketAddress saddr = { 0, NULL };
#ifdef HAVE_SYS_UN_H
@ -595,7 +595,7 @@ liSocketAddress li_sockaddr_from_string(GString *str, guint tcp_default_port) {
} else
#endif
if (li_parse_ipv4(str->str, &ipv4, NULL, &port)) {
if (!port) return saddr;
if (!port) port = tcp_default_port;
saddr.len = sizeof(struct sockaddr_in);
saddr.addr = (liSockAddr*) g_slice_alloc0(saddr.len);
saddr.addr->ipv4.sin_family = AF_INET;
@ -604,7 +604,7 @@ liSocketAddress li_sockaddr_from_string(GString *str, guint tcp_default_port) {
#ifdef HAVE_IPV6
} else
if (li_parse_ipv6(str->str, ipv6, NULL, &port)) {
if (!port) return saddr;
if (!port) port = tcp_default_port;
saddr.len = sizeof(struct sockaddr_in6);
saddr.addr = (liSockAddr*) g_slice_alloc0(saddr.len);
saddr.addr->ipv6.sin6_family = AF_INET6;
@ -653,6 +653,30 @@ void li_sockaddr_clear(liSocketAddress *saddr) {
saddr->len = 0;
}
gboolean ipv4_in_ipv4_net(guint32 target, guint32 match, guint32 networkmask) {
return (target & networkmask) == (match & networkmask);
}
gboolean ipv6_in_ipv6_net(const unsigned char *target, const guint8 *match, guint network) {
guint8 mask = network % 8;
if (0 != memcmp(target, match, network / 8)) return FALSE;
if (!mask) return TRUE;
mask = ~(((guint) 1 << (8-mask)) - 1);
return (target[network / 8] & mask) == (match[network / 8] & mask);
}
gboolean ipv6_in_ipv4_net(const unsigned char *target, guint32 match, guint32 networkmask) {
static const guint8 ipv6match[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
if (!ipv6_in_ipv6_net(target, ipv6match, 96)) return FALSE;
return ipv4_in_ipv4_net(*(guint32*)(target+12), match, networkmask);
}
gboolean ipv4_in_ipv6_net(guint32 target, const guint8 *match, guint network) {
guint8 ipv6[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
*(guint32*) (ipv6+12) = target;
return ipv6_in_ipv6_net(ipv6, match, network);
}
/* unused */
void li_gstring_replace_char_with_str_len(GString *gstr, gchar c, gchar *str, guint len) {
for (guint i = 0; i < gstr->len; i++) {

5
src/main/angel_fake.c

@ -9,11 +9,13 @@ int angel_fake_listen(liServer *srv, GString *str) {
#ifdef HAVE_IPV6
guint8 ipv6[16];
#endif
guint16 port = 80;
guint16 port;
if (li_parse_ipv4(str->str, &ipv4, NULL, &port)) {
int s, v;
struct sockaddr_in addr;
if (!port) port = 80;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ipv4;
@ -47,6 +49,7 @@ int angel_fake_listen(liServer *srv, GString *str) {
struct sockaddr_in6 addr;
li_ipv6_tostring(ipv6_str, ipv6);
if (!port) port = 80;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, ipv6, 16);

24
src/main/condition.c

@ -505,30 +505,6 @@ static liHandlerResult li_condition_check_eval_int(liVRequest *vr, liCondition *
return LI_HANDLER_GO_ON;
}
static gboolean ipv4_in_ipv4_net(guint32 target, guint32 match, guint32 networkmask) {
return (target & networkmask) == (match & networkmask);
}
static gboolean ipv6_in_ipv6_net(const unsigned char *target, const guint8 *match, guint network) {
guint8 mask = network % 8;
if (0 != memcmp(target, match, network / 8)) return FALSE;
if (!mask) return TRUE;
mask = ~(((guint) 1 << (8-mask)) - 1);
return (target[network / 8] & mask) == (match[network / 8] & mask);
}
static gboolean ipv6_in_ipv4_net(const unsigned char *target, guint32 match, guint32 networkmask) {
static const guint8 ipv6match[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
if (!ipv6_in_ipv6_net(target, ipv6match, 96)) return FALSE;
return ipv4_in_ipv4_net(*(guint32*)(target+12), match, networkmask);
}
static gboolean ipv4_in_ipv6_net(guint32 target, const guint8 *match, guint network) {
guint8 ipv6[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
*(guint32*) (ipv6+12) = target;
return ipv6_in_ipv6_net(ipv6, match, network);
}
static gboolean ip_in_net(liConditionRValue *target, liConditionRValue *network) {
if (target->type == LI_COND_VALUE_SOCKET_IPV4) {
if (network->type == LI_COND_VALUE_SOCKET_IPV4) {

Loading…
Cancel
Save