Parsers for ipv4/ipv6 für conditions

personal/stbuehler/wip
Stefan Bühler 14 years ago
parent c342be6424
commit aff9544b95
  1. 193
      src/condition.c
  2. 23
      src/condition.h
  3. 123
      src/condition_parsers.rl
  4. 14
      src/tests.c
  5. 1
      src/wscript

@ -3,12 +3,8 @@
static condition* condition_new(comp_operator_t op, condition_lvalue *lvalue);
static condition* cond_new_string(comp_operator_t op, condition_lvalue *lvalue, GString *str);
static condition* cond_new_socket(comp_operator_t op, condition_lvalue *lvalue, GString *str);
static condition* condition_new_from_string(comp_operator_t op, condition_lvalue *lvalue, GString *str);
static void condition_free(condition *c);
static gboolean condition_check_eval(server *srv, connection *con, condition *cond);
condition_lvalue* condition_lvalue_new(cond_lvalue_t type, GString *key) {
condition_lvalue *lvalue = g_slice_new0(condition_lvalue);
lvalue->type = type;
@ -38,92 +34,106 @@ static condition* condition_new(comp_operator_t op, condition_lvalue *lvalue) {
return c;
}
/* only EQ and NE */
static condition* cond_new_string(comp_operator_t op, condition_lvalue *lvalue, GString *str) {
condition *c = condition_new(op, lvalue);
switch (op) {
case CONFIG_COND_EQ: /** == */
case CONFIG_COND_NE: /** != */
c->rvalue.string = str;
break;
case CONFIG_COND_MATCH: /** =~ */
case CONFIG_COND_NOMATCH: /** !~ */
#ifdef HAVE_PCRE_H
/* TODO */
condition_free(c);
return NULL;
break;
#else
condition_free(c);
return NULL;
#endif
case CONFIG_COND_GT: /** > */
case CONFIG_COND_GE: /** >= */
case CONFIG_COND_LT: /** < */
case CONFIG_COND_LE: /** <= */
condition_free(c);
return NULL;
}
condition *c;
c = condition_new(op, lvalue);
c->rvalue.type = COND_VALUE_STRING;
c->rvalue.string = str;
return c;
}
static condition* cond_new_socket(comp_operator_t op, condition_lvalue *lvalue, GString *str) {
return cond_new_string(op, lvalue, str);
#ifdef HAVE_PCRE_H
/* only MATCH and NOMATCH */
static condition* cond_new_match(server *srv, comp_operator_t op, condition_lvalue *lvalue, GString *str) {
UNUSED(op); UNUSED(lvalue); UNUSED(str);
ERROR(srv, "%s", "pcre not supported for now");
/* TODO */
return NULL;
}
#endif
/* only IP and NOTIP */
static condition* cond_new_ip(server *srv, comp_operator_t op, condition_lvalue *lvalue, GString *str) {
UNUSED(op); UNUSED(lvalue); UNUSED(str);
ERROR(srv, "%s", "ip matching not supported for now");
/* TODO: parse str as socket addr */
return NULL;
}
static condition* condition_new_from_string(comp_operator_t op, condition_lvalue *lvalue, GString *str) {
switch (lvalue->type) {
case COMP_REQUEST_LOCALIP:
case COMP_REQUEST_REMOTEIP:
return cond_new_socket(op, lvalue, str);
case COMP_REQUEST_PATH:
case COMP_REQUEST_HOST:
case COMP_REQUEST_SCHEME:
case COMP_REQUEST_QUERY_STRING:
case COMP_REQUEST_METHOD:
case COMP_PHYSICAL_PATH:
case COMP_PHYSICAL_PATH_EXISTS:
case COMP_REQUEST_HEADER:
condition* condition_new_string(server *srv, comp_operator_t op, condition_lvalue *lvalue, GString *str) {
switch (op) {
case CONFIG_COND_EQ:
case CONFIG_COND_NE:
return cond_new_string(op, lvalue, str);
case COMP_PHYSICAL_SIZE:
case COMP_REQUEST_CONTENT_LENGTH:
// TODO: die with error
assert(NULL);
break;
case CONFIG_COND_MATCH:
case CONFIG_COND_NOMATCH:
#ifdef HAVE_PCRE_H
return cond_new_match(srv, op, lvalue, str);
#else
ERROR(srv, "compiled without pcre, cannot use '%s'", comp_op_to_string(op));
return NULL;
#endif
case CONFIG_COND_IP:
case CONFIG_COND_NOTIP:
return cond_new_ip(srv, op, lvalue, str);
case CONFIG_COND_GT:
case CONFIG_COND_GE:
case CONFIG_COND_LT:
case CONFIG_COND_LE:
ERROR(srv, "Cannot compare strings with '%s'", comp_op_to_string(op));
return NULL;
}
ERROR(srv, "Condition creation failed: %s %s '%s' (perhaps you compiled without pcre?)",
cond_lvalue_to_string(lvalue->type), comp_op_to_string(op),
str->str);
return NULL;
}
condition* condition_new_string(server *srv, comp_operator_t op, condition_lvalue *lvalue, GString *str) {
condition* condition_new_int(server *srv, comp_operator_t op, condition_lvalue *lvalue, gint64 i) {
condition *c;
if (NULL == (c = condition_new_from_string(op, lvalue, str))) {
ERROR(srv, "Condition creation failed: %s %s '%s' (perhaps you compiled without pcre?)",
cond_lvalue_to_string(lvalue->type), comp_op_to_string(op),
str->str);
switch (op) {
case CONFIG_COND_MATCH:
case CONFIG_COND_NOMATCH:
case CONFIG_COND_IP:
case CONFIG_COND_NOTIP:
ERROR(srv, "Cannot compare integers with '%s'", comp_op_to_string(op));
return NULL;
case CONFIG_COND_EQ:
case CONFIG_COND_NE:
case CONFIG_COND_GT:
case CONFIG_COND_GE:
case CONFIG_COND_LT:
case CONFIG_COND_LE:
c = condition_new(op, lvalue);
c->rvalue.type = COND_VALUE_INT;
c->rvalue.i = i;
return c;
}
return c;
ERROR(srv, "Condition creation failed: %s %s %"G_GINT64_FORMAT" (perhaps you compiled without pcre?)",
cond_lvalue_to_string(lvalue->type), comp_op_to_string(op),
i);
return NULL;
}
static void condition_free(condition *c) {
switch (c->rvalue.type) {
case COND_VALUE_INT:
case COND_VALUE_SOCKET_IPV4:
case COND_VALUE_SOCKET_IPV6:
/* nothing to free */
break;
case COND_VALUE_STRING:
if (c->op == CONFIG_COND_MATCH || c->op == CONFIG_COND_NOMATCH) {
g_string_free(c->rvalue.string, TRUE);
break;
#ifdef HAVE_PCRE_H
if (c->rvalue.regex) pcre_free(c->rvalue.regex);
if (c->rvalue.regex_study) pcre_free(c->rvalue.regex_study);
case COND_VALUE_REGEXP
if (c->rvalue.regex) pcre_free(c->rvalue.regex);
if (c->rvalue.regex_study) pcre_free(c->rvalue.regex_study);
#endif
} else {
g_string_free(c->rvalue.string, TRUE);
}
break;
case COND_VALUE_SOCKET_IPV4:
case COND_VALUE_SOCKET_IPV6:
/* nothing to free */
break;
}
g_slice_free(condition, c);
@ -145,13 +155,15 @@ void condition_release(server *srv, condition* c) {
const char* comp_op_to_string(comp_operator_t op) {
switch (op) {
case CONFIG_COND_EQ: return "==";
case CONFIG_COND_GE: return ">=";
case CONFIG_COND_GT: return ">";
case CONFIG_COND_LE: return "<=";
case CONFIG_COND_LT: return "<";
case CONFIG_COND_MATCH: return "=~";
case CONFIG_COND_NE: return "!=";
case CONFIG_COND_MATCH: return "=~";
case CONFIG_COND_NOMATCH: return "!~";
case CONFIG_COND_IP: return "=/";
case CONFIG_COND_NOTIP: return "!/";
case CONFIG_COND_GT: return ">";
case CONFIG_COND_GE: return ">=";
case CONFIG_COND_LT: return "<";
case CONFIG_COND_LE: return "<=";
}
return "<unkown>";
@ -176,11 +188,7 @@ const char* cond_lvalue_to_string(cond_lvalue_t t) {
return "<unknown>";
}
gboolean condition_check(server *srv, connection *con, condition *cond) {
/* TODO: implement cache */
return condition_check_eval(srv, con, cond);
}
/* COND_VALUE_STRING and COND_VALUE_REGEXP only */
static gboolean condition_check_eval_string(server *srv, connection *con, condition *cond) {
const char *value = NULL;
GString *tmp = NULL;
@ -226,22 +234,31 @@ static gboolean condition_check_eval_string(server *srv, connection *con, condit
}
if (value) switch (cond->op) {
case CONFIG_COND_EQ: /** == */
case CONFIG_COND_EQ:
result = 0 == strcmp(value, cond->rvalue.string->str);
break;
case CONFIG_COND_NE: /** != */
case CONFIG_COND_NE:
result = 0 != strcmp(value, cond->rvalue.string->str);
break;
case CONFIG_COND_MATCH: /** =~ */
case CONFIG_COND_NOMATCH: /** !~ */
case CONFIG_COND_MATCH:
case CONFIG_COND_NOMATCH:
#ifdef HAVE_PCRE_H
/* TODO: pcre */
ERROR(srv, "%s", "regexp match not supported yet");
#else
ERROR(srv, "compiled without pcre, cannot use '%s'", comp_op_to_string(cond->op));
#endif
break;
case CONFIG_COND_IP:
case CONFIG_COND_NOTIP:
case CONFIG_COND_GE:
case CONFIG_COND_GT:
case CONFIG_COND_LE:
case CONFIG_COND_LT:
assert(NULL);
ERROR(srv, "cannot compare string/regexp with '%s'", comp_op_to_string(cond->op));
break;
} else {
ERROR(srv, "couldn't get string value for '%s'", cond_lvalue_to_string(cond->lvalue->type));
}
if (tmp) g_string_free(tmp, TRUE);
@ -279,9 +296,12 @@ static gboolean condition_check_eval_int(server *srv, connection *con, condition
return (value >= cond->rvalue.i);
case CONFIG_COND_MATCH:
case CONFIG_COND_NOMATCH:
// TODO: die with error
assert(NULL);
case CONFIG_COND_IP:
case CONFIG_COND_NOTIP:
ERROR(srv, "cannot compare int with '%s'", comp_op_to_string(cond->op));
return FALSE;
} else {
ERROR(srv, "couldn't get int value for '%s'", cond_lvalue_to_string(cond->lvalue->type));
}
return FALSE;
@ -308,14 +328,19 @@ static gboolean ipv6_in_ipv4_net(const unsigned char *target, guint32 match, gui
}
#endif
static gboolean condition_check_eval(server *srv, connection *con, condition *cond) {
gboolean condition_check(server *srv, connection *con, condition *cond) {
switch (cond->rvalue.type) {
case COND_VALUE_STRING:
#ifdef HAVE_PCRE_H
case COND_VALUE_REGEXP:
#endif
return condition_check_eval_string(srv, con, cond);
case COND_VALUE_INT:
return condition_check_eval_int(srv, con, cond);
case COND_VALUE_SOCKET_IPV4:
case COND_VALUE_SOCKET_IPV6:
/* TODO: implement checks */
default:
return FALSE;
break;
}
return FALSE;
}

@ -20,10 +20,13 @@ typedef enum {
CONFIG_COND_EQ, /** == */
CONFIG_COND_NE, /** != */
/* only with strings (including socket name) */
/* only usable with pcre */
CONFIG_COND_MATCH, /** =~ */
CONFIG_COND_NOMATCH, /** !~ */
CONFIG_COND_IP,
CONFIG_COND_NOTIP,
/* only with int */
CONFIG_COND_GT, /** > */
CONFIG_COND_GE, /** >= */
@ -67,10 +70,8 @@ typedef enum {
#ifdef HAVE_PCRE_H
COND_VALUE_REGEXP,
#endif
COND_VALUE_SOCKET_IPV4 /** only match ip/netmask */
#ifdef HAVE_IPV6
,COND_VALUE_SOCKET_IPV6 /** only match ip/netmask */
#endif
COND_VALUE_SOCKET_IPV4, /** only match ip/netmask */
COND_VALUE_SOCKET_IPV6 /** only match ip/netmask */
} cond_rvalue_t;
struct condition_rvalue {
@ -88,13 +89,10 @@ struct condition_rvalue {
guint32 addr;
guint32 networkmask;
} ipv4;
#ifdef HAVE_IPV6
struct {
guint8 addr[16];
guint network;
} ipv6;
#endif
sock_addr addr;
};
#include "base.h"
@ -112,10 +110,8 @@ LI_API condition_lvalue* condition_lvalue_new(cond_lvalue_t type, GString *key);
LI_API void condition_lvalue_acquire(condition_lvalue *lvalue);
LI_API void condition_lvalue_release(condition_lvalue *lvalue);
LI_API condition* condition_new_string(server *srv, comp_operator_t op, condition_lvalue *lvalue, GString *str);
LI_API condition* condition_new_int(server *srv, comp_operator_t op, condition_lvalue *lvalue, gint i);
LI_API condition* condition_new_int(server *srv, comp_operator_t op, condition_lvalue *lvalue, gint64 i);
LI_API void condition_acquire(condition *c);
LI_API void condition_release(server *srv, condition* c);
@ -125,4 +121,9 @@ LI_API const char* cond_lvalue_to_string(cond_lvalue_t t);
LI_API gboolean condition_check(server *srv, connection *con, condition *cond);
/* parser */
LI_API gboolean parse_ipv4(const char *str, guint32 *ip, guint32 *netmask);
LI_API gboolean parse_ipv6(const char *str, guint8 *ip, guint *network);
LI_API GString* ipv6_tostring(const guint8 ip[16]);
#endif

@ -0,0 +1,123 @@
#include "settings.h"
#include <stdlib.h>
%%{
machine ipv4_parser;
action mark { mark = fpc; }
decint = (digit+) >mark %{ tmpval = strtol(mark, NULL, 10); };
octet = decint %{
if (tmpval > 255) 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 = htonl(~((1 << (32-tmpval)) - 1));
res = TRUE;
fbreak;
};
write data;
}%%
gboolean parse_ipv4(const char *str, guint32 *ip, guint32 *netmask) {
guint8 *data = (guint8*) ip;
const char *p = str, *mark;
gboolean res = FALSE;
int cs, tmpval, i = 0;
%% write init nocs;
cs = netmask ? ipv4_parser_en_ipv4_cidr : ipv4_parser_en_ipv4;
%% write exec noend;
return res;
}
%%{
machine ipv6_parser;
action mark { mark = fpc; }
decint = (digit+) >mark %{ tmpval = strtol(mark, NULL, 10); };
octet = decint %{
if (tmpval > 255) fbreak;
data[i++] = tmpval;
};
hexint = (xdigit{1,4}) >mark %{ tmpval = strtol(mark, NULL, 16); };
group = hexint %{
if (tmpval > 0xffff) fbreak;
};
pregroup = group %{
if (prec >= 8) fbreak;
predata[prec++] = htons(tmpval);
};
postgroup = group %{
if (postc >= 8) fbreak;
postdata[postc++] = htons(tmpval);
};
ipv4_data = octet "." octet "." octet "." octet;
pre_ipv4 = ipv4_data %{
if (prec > 6) fbreak;
predata[prec++] = *(guint16*) (data);
predata[prec++] = *(guint16*) (data+2);
};
post_ipv4 = ipv4_data %{
if (postc > 6) 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;
};
write data;
}%%
gboolean parse_ipv6(const char *str, guint8 *ip, guint *network) {
guint8 data[4] = {0,0,0,0};
guint16 *predata = (guint16*) ip, postdata[8];
size_t prec = 0, postc = 0;
const char *p = str, *mark;
gboolean res = FALSE, compressed = FALSE;
int cs, tmpval, i = 0;
%% write init nocs;
cs = network ? ipv6_parser_en_ipv6_cidr : ipv6_parser_en_ipv6;
%% write exec noend;
if (!res) return FALSE;
if (!compressed) return (prec == 8);
if (prec + postc > 7) return FALSE;
for ( ; prec < 8-postc; prec++) predata[prec] = 0;
for (postc = 0 ; prec < 8; prec++, postc++ ) predata[prec] = postdata[postc];
return TRUE;
}
GString* ipv6_tostring(const guint8 ip[16]) {
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]));
}
return s;
}

@ -37,6 +37,20 @@ int main() {
GTimeVal start, end;
gboolean result;
guint32 ip, netmask;
assert(parse_ipv4("10.0.3.8/24", &ip, &netmask));
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));
s = ipv6_tostring(ipv6);
printf("parsed ipv6: %s/%u\n", s->str, network);
return 0;
srv = server_new();
/* config parser test */

@ -12,6 +12,7 @@ common_source='''
chunk.c
chunk_parser.c
condition.c
condition_parsers.rl
config_parser.rl
http_headers.c
http_request_parser.rl

Loading…
Cancel
Save