Add clean ipv6+port parsing for listen()

personal/stbuehler/wip
Stefan Bühler 14 years ago
parent bb59776ea3
commit e8e60b3a04
  1. 11
      include/lighttpd/condition.h
  2. 74
      src/angel_fake.c
  3. 8
      src/condition.c
  4. 106
      src/condition_parsers.rl
  5. 4
      src/tests.c

@ -122,10 +122,11 @@ struct vrequest;
LI_API handler_t condition_check(struct vrequest *vr, condition *cond, gboolean *result);
/* parser */
/** parse an IPv4 (if netmask is not NULL with cidr netmask) */
LI_API gboolean parse_ipv4(const char *str, guint32 *ip, guint32 *netmask);
/** parse an IPv6 (if network is not NULL with cidr network) */
LI_API gboolean parse_ipv6(const char *str, guint8 *ip, guint *network);
LI_API GString* ipv6_tostring(const guint8 ip[16]);
/** parse an IPv4 (if netmask is not NULL with optional cidr netmask, if port is not NULL with optional port) */
LI_API gboolean 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 [...]) */
LI_API gboolean parse_ipv6(const char *str, guint8 *ip, guint *network, guint16 *port);
/** print the ip into dest, return dest */
LI_API GString* ipv6_tostring(GString *dest, const guint8 ip[16]);
#endif

@ -8,40 +8,15 @@ int angel_fake_listen(server *srv, GString *str) {
#ifdef HAVE_IPV6
guint8 ipv6[16];
#endif
GString *ipstr;
guint16 port = 80;
if (str->str[0] == '[') {
/* ipv6 with port */
gchar *pos = g_strrstr(str->str, "]");
if (NULL == pos) {
ERROR(srv, "%s", "listen: bogus ipv6 format");
return -1;
}
if (pos[1] == ':') {
port = atoi(&pos[2]);
}
ipstr = g_string_new_len(&str->str[1], pos - &str->str[1]);
} else {
/* no brackets, search for :port */
gchar *pos = g_strrstr(str->str, ":");
if (NULL != pos) {
ipstr = g_string_new_len(str->str, pos - str->str);
port = atoi(&pos[1]);
} else {
/* no port, just plain ipv4 or ipv6 address */
ipstr = g_string_new_len(GSTR_LEN(str));
}
}
if (parse_ipv4(ipstr->str, &ipv4, NULL)) {
if (parse_ipv4(str->str, &ipv4, NULL, &port)) {
int s, v;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ipv4;
addr.sin_port = htons(port);
g_string_free(ipstr, TRUE);
if (-1 == (s = socket(AF_INET, SOCK_STREAM, 0))) {
ERROR(srv, "Couldn't open socket: %s", g_strerror(errno));
return -1;
@ -65,15 +40,46 @@ int angel_fake_listen(server *srv, GString *str) {
TRACE(srv, "listen to ipv4: '%s' port: %d", inet_ntoa(*(struct in_addr*)&ipv4), port);
return s;
#ifdef HAVE_IPV6
} else if (parse_ipv6(ipstr->str, ipv6, NULL)) {
/* TODO: IPv6 */
g_string_free(ipstr, TRUE);
ERROR(srv, "%s", "IPv6 not supported yet");
return -1;
} else if (parse_ipv6(str->str, ipv6, NULL, &port)) {
GString *ipv6_str = g_string_sized_new(0);
int s, v;
struct sockaddr_in6 addr;
ipv6_tostring(ipv6_str, ipv6);
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, ipv6, 16);
addr.sin6_port = htons(port);
if (-1 == (s = socket(AF_INET6, SOCK_STREAM, 0))) {
ERROR(srv, "Couldn't open socket: %s", g_strerror(errno));
g_string_free(ipv6_str, TRUE);
return -1;
}
v = 1;
if (-1 == setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v))) {
close(s);
ERROR(srv, "Couldn't setsockopt(SO_REUSEADDR) to '%s': %s", ipv6_str->str, g_strerror(errno));
g_string_free(ipv6_str, TRUE);
return -1;
}
if (-1 == bind(s, (struct sockaddr*)&addr, sizeof(addr))) {
close(s);
ERROR(srv, "Couldn't bind socket to '%s': %s", ipv6_str->str, g_strerror(errno));
g_string_free(ipv6_str, TRUE);
return -1;
}
if (-1 == listen(s, 1000)) {
close(s);
ERROR(srv, "Couldn't listen on '%s': %s", ipv6_str->str, g_strerror(errno));
g_string_free(ipv6_str, TRUE);
return -1;
}
TRACE(srv, "listen to ipv6: '%s' port: %d", ipv6_str->str, port);
g_string_free(ipv6_str, TRUE);
return s;
#endif
} else {
ERROR(srv, "Invalid ip: '%s'", ipstr->str);
g_string_free(ipstr, TRUE);
ERROR(srv, "Invalid ip: '%s'", str->str);
return -1;
}
}
@ -85,7 +91,7 @@ gboolean angel_fake_log(server *srv, GString *str) {
ssize_t written;
UNUSED(srv);
g_string_prepend(str, "fake: ");
/* g_string_prepend(str, "fake: "); */
buf = str->str;
len = str->len;

@ -1,12 +1,12 @@
#include <lighttpd/base.h>
static gboolean condition_parse_ip(condition_rvalue *val, const char *txt) {
if (parse_ipv4(txt, &val->ipv4.addr, NULL)) {
if (parse_ipv4(txt, &val->ipv4.addr, NULL, NULL)) {
val->type = COND_VALUE_SOCKET_IPV4;
val->ipv4.networkmask = 0xFFFFFFFF;
return TRUE;
}
if (parse_ipv6(txt, val->ipv6.addr, NULL)) {
if (parse_ipv6(txt, val->ipv6.addr, NULL, NULL)) {
val->type = COND_VALUE_SOCKET_IPV6;
val->ipv6.network = 128;
return TRUE;
@ -15,11 +15,11 @@ static gboolean condition_parse_ip(condition_rvalue *val, const char *txt) {
}
static gboolean condition_parse_ip_net(condition_rvalue *val, const char *txt) {
if (parse_ipv4(txt, &val->ipv4.addr, &val->ipv4.networkmask)) {
if (parse_ipv4(txt, &val->ipv4.addr, &val->ipv4.networkmask, NULL)) {
val->type = COND_VALUE_SOCKET_IPV4;
return TRUE;
}
if (parse_ipv6(txt, val->ipv6.addr, &val->ipv6.network)) {
if (parse_ipv6(txt, val->ipv6.addr, &val->ipv6.network, NULL)) {
val->type = COND_VALUE_SOCKET_IPV6;
return TRUE;
}

@ -4,38 +4,48 @@
%%{
machine ipv4_parser;
action mark { mark = fpc; }
action dc_start { tmpval = 0; }
action dc_step { tmpval = 10*tmpval + (fc - '0'); }
decint = (digit+) >mark %{ tmpval = strtol(mark, NULL, 10); };
decint = (digit digit**) >dc_start $dc_step;
octet = decint %{
if (tmpval > 255) fbreak;
if (tmpval > 255) { res = FALSE; fbreak; }
data[i++] = tmpval;
};
ipv4_data = octet "." octet "." octet "." octet;
ipv4 := ipv4_data (space|0) @{ res = TRUE; };
ipv4_cidr := ipv4_data "/" decint (space|0) @{
if (tmpval > 32) fbreak;
netmask = "/" decint %{
if (tmpval > 32) { res = FALSE; fbreak; }
*netmask = htonl(~((1 << (32-tmpval)) - 1));
res = TRUE;
fbreak;
};
port = ":" decint %{
if (tmpval > 65535) { res = FALSE; fbreak; }
*port = tmpval;
};
# so we don't need pe/eof vars
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;
write data;
}%%
gboolean parse_ipv4(const char *str, guint32 *ip, guint32 *netmask) {
gboolean parse_ipv4(const char *str, guint32 *ip, guint32 *netmask, guint16 *port) {
guint8 *data = (guint8*) ip;
const char *p = str, *mark = NULL;
gboolean res = FALSE;
const char *p = str;
gboolean res = TRUE;
int cs, tmpval = 0, i = 0;
%% write init nocs;
cs = netmask ? ipv4_parser_en_ipv4_cidr : ipv4_parser_en_ipv4;
cs = netmask
? (port ? ipv4_parser_en_ipv4_socket_cidr : ipv4_parser_en_ipv4_cidr)
: (port ? ipv4_parser_en_ipv4_socket : ipv4_parser_en_ipv4);
%% write exec noend;
return res;
return res && cs >= ipv4_parser_first_final;
}
%%{
@ -43,63 +53,77 @@ gboolean parse_ipv4(const char *str, guint32 *ip, guint32 *netmask) {
action mark { mark = fpc; }
decint = (digit+) >mark %{ tmpval = strtol(mark, NULL, 10); };
action dc_start { tmpval = 0; }
action dc_step { tmpval = 10*tmpval + (fc - '0'); }
decint = (digit digit**) >dc_start $dc_step;
octet = decint %{
if (tmpval > 255) fbreak;
if (tmpval > 255) { res = FALSE; fbreak; }
data[i++] = tmpval;
};
network = "/" decint %{
if (tmpval > 128) { res = FALSE; fbreak; }
*network = tmpval;
};
port = ":" decint %{
if (tmpval > 65535) { res = FALSE; fbreak; }
*port = tmpval;
};
hexint = (xdigit{1,4}) >mark %{ tmpval = strtol(mark, NULL, 16); };
group = hexint %{
if (tmpval > 0xffff) fbreak;
if (tmpval > 0xffff) { res = FALSE; fbreak; }
};
pregroup = group %{
if (prec >= 8) fbreak;
if (prec >= 8) { res = FALSE; fbreak; }
predata[prec++] = htons(tmpval);
};
postgroup = group %{
if (postc >= 8) fbreak;
if (postc >= 8) { res = FALSE; fbreak; }
postdata[postc++] = htons(tmpval);
};
ipv4_data = octet "." octet "." octet "." octet;
pre_ipv4 = ipv4_data %{
if (prec > 6) fbreak;
if (prec > 6) { res = FALSE; fbreak; }
predata[prec++] = *(guint16*) (data);
predata[prec++] = *(guint16*) (data+2);
};
post_ipv4 = ipv4_data %{
if (postc > 6) fbreak;
if (postc > 6) { res = FALSE; fbreak; }
postdata[postc++] = *(guint16*) (data);
postdata[postc++] = *(guint16*) (data+2);
};
ipv6_data = ((pregroup ":")+ | ":") ((":" @ { compressed = TRUE; } (postgroup ":")* (postgroup | post_ipv4)?) | (pregroup | pre_ipv4));
ipv6 := ipv6_data (space|0) @{ res = TRUE; };
ipv6_cidr := ipv6_data "/" decint (space|0) @{
if (tmpval > 128) fbreak;
*network = tmpval;
res = TRUE;
fbreak;
};
# so we don't need pe/eof vars
end = (space | 0) @{ cs = ipv6_parser_first_final; fbreak; };
ipv6 := ( ( ipv6_data ) | ( "[" ipv6_data "]" ) ) end;
ipv6_cidr := ( ( ipv6_data network? ) | ( "[" ipv6_data network? "]" ) ) end;
ipv6_socket := ( ( ipv6_data ) | ( "[" ipv6_data "]" port? ) ) end;
ipv6_socket_cidr := ( ( ipv6_data network? ) | ( "[" ipv6_data network? "]" port?) ) end;
write data;
}%%
gboolean parse_ipv6(const char *str, guint8 *ip, guint *network) {
gboolean parse_ipv6(const char *str, guint8 *ip, guint *network, guint16 *port) {
guint8 data[4] = {0,0,0,0};
guint16 *predata = (guint16*) ip, postdata[8];
size_t prec = 0, postc = 0;
const char *p = str, *mark = NULL;
gboolean res = FALSE, compressed = FALSE;
gboolean res = TRUE, compressed = FALSE;
int cs, tmpval = 0, i = 0;
%% write init nocs;
cs = network ? ipv6_parser_en_ipv6_cidr : ipv6_parser_en_ipv6;
cs = network
? (port ? ipv6_parser_en_ipv6_socket_cidr : ipv6_parser_en_ipv6_cidr)
: (port ? ipv6_parser_en_ipv6_socket : ipv6_parser_en_ipv6);
%% write exec noend;
if (!res) return FALSE;
if (!res || cs < ipv6_parser_first_final) return FALSE;
if (!compressed) return (prec == 8);
if (prec + postc > 7) return FALSE;
for ( ; prec < 8-postc; prec++) predata[prec] = 0;
@ -108,14 +132,22 @@ gboolean parse_ipv6(const char *str, guint8 *ip, guint *network) {
return TRUE;
}
GString* ipv6_tostring(const guint8 ip[16]) {
GString* ipv6_tostring(GString *dest, const guint8 ip[16]) {
#define IPV6_TEMPLATE "ffff:ffff:ffff:ffff:ffff:ffff:abc.def.ghi.jkl"
guint16 *data = (guint16*) ip;
size_t i;
GString *s = g_string_sized_new(0);
g_string_printf(s, "%" G_GINT16_MODIFIER "x", ntohs(data[0]));
#ifdef HAVE_INET_NTOP
g_string_set_size(dest, sizeof(IPV6_TEMPLATE)-1);
if (inet_ntop(AF_INET6, ip, dest->str, dest->len)) {
g_string_set_size(dest, strlen(dest->str));
return dest;
}
#endif
g_string_truncate(dest, 0);
g_string_printf(dest, "%" G_GINT16_MODIFIER "x", ntohs(data[0]));
for (i = 1; i < 8; i++) {
g_string_append_printf(s, ":%" G_GINT16_MODIFIER "x", ntohs(data[i]));
g_string_append_printf(dest, ":%" G_GINT16_MODIFIER "x", ntohs(data[i]));
}
return s;
return dest;
}

@ -38,14 +38,14 @@ int main() {
server *srv;
guint32 ip, netmask;
assert(parse_ipv4("10.0.3.8/24", &ip, &netmask));
assert(parse_ipv4("10.0.3.8/24", &ip, &netmask, NULL));
printf("parsed ip: %s\n", inet_ntoa(*(struct in_addr*) &ip));
printf("parsed netmask: %s\n", inet_ntoa(*(struct in_addr*) &netmask));
guint8 ipv6[16];
guint network;
GString *s;
assert(parse_ipv6("::ffff:192.168.0.1/80", ipv6, &network));
assert(parse_ipv6("::ffff:192.168.0.1/80", ipv6, &network, NULL));
s = ipv6_tostring(ipv6);
printf("parsed ipv6: %s/%u\n", s->str, network);

Loading…
Cancel
Save