Parsers for ipv4/ipv6 für conditions
parent
c342be6424
commit
aff9544b95
199
src/condition.c
199
src/condition.c
|
@ -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);
|
||||
/* TODO: parse str as socket addr */
|
||||
#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
|
||||
|
||||
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:
|
||||
return cond_new_string(op, lvalue, str);
|
||||
case COMP_PHYSICAL_SIZE:
|
||||
case COMP_REQUEST_CONTENT_LENGTH:
|
||||
// TODO: die with error
|
||||
assert(NULL);
|
||||
break;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
|
||||
condition* condition_new_string(server *srv, comp_operator_t op, condition_lvalue *lvalue, GString *str) {
|
||||
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_EQ:
|
||||
case CONFIG_COND_NE:
|
||||
return cond_new_string(op, lvalue, str);
|
||||
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;
|
||||
}
|
||||
|
||||
return c;
|
||||
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_int(server *srv, comp_operator_t op, condition_lvalue *lvalue, gint64 i) {
|
||||
condition *c;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
14
src/tests.c
14
src/tests.c
|
@ -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…
Reference in New Issue