Add clean ipv6+port parsing for listen()
This commit is contained in:
parent
bb59776ea3
commit
e8e60b3a04
|
@ -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]));
|
||||
for (i = 1; i < 8; i++) {
|
||||
g_string_append_printf(s, ":%" G_GINT16_MODIFIER "x", ntohs(data[i]));
|
||||
#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;
|
||||
}
|
||||
return s;
|
||||
#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(dest, ":%" G_GINT16_MODIFIER "x", ntohs(data[i]));
|
||||
}
|
||||
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…
Reference in New Issue