#include <sys_defs.h>
#include <sys/socket.h>
#include <ctype.h>
#include <string.h>
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <ip_match.h>
#define IP_MATCH_CODE_OPEN '['
#define IP_MATCH_CODE_CLOSE ']'
#define IP_MATCH_CODE_OVAL 'N'
#define IP_MATCH_CODE_RANGE 'R'
#define IP_MATCH_CODE_EOF '\0'
#define IP_MATCH_CODE_ERR 256
#define STR vstring_str
#define LEN VSTRING_LEN
char *ip_match_save(const VSTRING *byte_codes)
{
char *dst;
dst = mymalloc(LEN(byte_codes));
return (memcpy(dst, STR(byte_codes), LEN(byte_codes)));
}
char *ip_match_dump(VSTRING *printable, const char *byte_codes)
{
const char *myname = "ip_match_dump";
const unsigned char *bp;
int octet_count = 0;
int ch;
if (*byte_codes != AF_INET)
msg_panic("%s: malformed byte-code header", myname);
VSTRING_RESET(printable);
bp = (const unsigned char *) byte_codes + 1;
for (;;) {
if ((ch = *bp++) == IP_MATCH_CODE_OVAL) {
vstring_sprintf_append(printable, "%d", *bp);
bp += 1;
}
else if (ch == IP_MATCH_CODE_OPEN) {
vstring_sprintf_append(printable, "[");
for (;;) {
if ((ch = *bp++) == IP_MATCH_CODE_RANGE) {
vstring_sprintf_append(printable, "%d..%d", bp[0], bp[1]);
bp += 2;
}
else if (ch == IP_MATCH_CODE_OVAL) {
vstring_sprintf_append(printable, "%d", *bp);
bp += 1;
}
else if (ch == IP_MATCH_CODE_CLOSE) {
break;
}
else {
msg_panic("%s: unexpected byte code (decimal %d) "
"after \"%s\"", myname, ch, STR(printable));
}
if (*bp != IP_MATCH_CODE_CLOSE)
vstring_sprintf_append(printable, ";");
}
vstring_sprintf_append(printable, "]");
}
else {
msg_panic("%s: unexpected byte code (decimal %d) after \"%s\"",
myname, ch, STR(printable));
}
if (++octet_count == 4) {
if (*bp != 0)
msg_panic("%s: unexpected byte code (decimal %d) after \"%s\"",
myname, ch, STR(printable));
return (STR(printable));
}
if (*bp == 0)
msg_panic("%s: truncated byte code after \"%s\"",
myname, STR(printable));
vstring_sprintf_append(printable, ".");
}
}
static char *ip_match_print_code_prefix(const char *byte_codes, size_t len)
{
static VSTRING *printable = 0;
const char *fmt;
const char *bp;
if (printable == 0)
printable = vstring_alloc(100);
else
VSTRING_RESET(printable);
fmt = (*byte_codes == AF_INET ? "%d " : "%02x ");
for (bp = byte_codes; bp < byte_codes + len; bp++)
vstring_sprintf_append(printable, fmt, *(const unsigned char *) bp);
return (STR(printable));
}
int ip_match_execute(const char *byte_codes, const char *addr_bytes)
{
const char *myname = "ip_match_execute";
const unsigned char *bp;
const unsigned char *ap;
int octet_count = 0;
int ch;
int matched;
if (*byte_codes != AF_INET)
msg_panic("%s: malformed byte-code header (decimal %d)",
myname, *(const unsigned char *) byte_codes);
bp = (const unsigned char *) byte_codes + 1;
ap = (const unsigned char *) addr_bytes;
for (octet_count = 0; octet_count < 4; octet_count++, ap++) {
if ((ch = *bp++) == IP_MATCH_CODE_OVAL) {
if (*ap == *bp)
bp += 1;
else
return (0);
}
else if (ch == IP_MATCH_CODE_OPEN) {
matched = 0;
for (;;) {
if ((ch = *bp++) == IP_MATCH_CODE_RANGE) {
if (!matched)
matched = (*ap >= bp[0] && *ap <= bp[1]);
bp += 2;
}
else if (ch == IP_MATCH_CODE_OVAL) {
if (!matched)
matched = (*ap == *bp);
bp += 1;
}
else if (ch == IP_MATCH_CODE_CLOSE) {
break;
}
else {
size_t len = (const char *) bp - byte_codes - 1;
msg_panic("%s: unexpected byte code (decimal %d) "
"after \"%s\"", myname, ch,
ip_match_print_code_prefix(byte_codes, len));
}
}
if (matched == 0)
return (0);
}
else {
size_t len = (const char *) bp - byte_codes - 1;
msg_panic("%s: unexpected byte code (decimal %d) after \"%s\"",
myname, ch, ip_match_print_code_prefix(byte_codes, len));
}
}
return (1);
}
static int ip_match_next_token(char **pstart, char **psaved_start, int *poval)
{
unsigned char *cp;
int oval;
int type;
#define IP_MATCH_RETURN_TOK(next, type) \
do { *pstart = (char *) (next); return (type); } while (0)
#define IP_MATCH_RETURN_TOK_VAL(next, type, oval) do { \
*poval = (oval); IP_MATCH_RETURN_TOK((next), type); \
} while (0)
*psaved_start = *pstart;
cp = (unsigned char *) *pstart;
if (ISDIGIT(*cp)) {
oval = *cp - '0';
type = IP_MATCH_CODE_OVAL;
for (cp += 1; ISDIGIT(*cp); cp++) {
oval *= 10;
oval += *cp - '0';
if (oval > 255)
type = IP_MATCH_CODE_ERR;
}
IP_MATCH_RETURN_TOK_VAL(cp, type, oval);
} else {
IP_MATCH_RETURN_TOK(*cp ? cp + 1 : cp, *cp);
}
}
static void PRINTFLIKE(5, 6) ipmatch_print_parse_error(VSTRING *reply,
char *start,
char *here,
char *next,
const char *fmt,...)
{
va_list ap;
int start_width;
int here_width;
va_start(ap, fmt);
vstring_vsprintf(reply, fmt, ap);
va_end(ap);
if (start != 0) {
start_width = here - start;
here_width = next - here;
vstring_sprintf_append(reply, " at \"%.*s>%.*s<%s\"",
start_width, start_width == 0 ? "" : start,
here_width, here_width == 0 ? "" : here, next);
}
}
char *ip_match_parse(VSTRING *byte_codes, char *pattern)
{
int octet_count;
char *saved_cp;
char *cp;
int token_type;
int look_ahead;
int oval;
int saved_oval;
#define FIND_TERMINATOR(start, cp) do { \
int _level = 0; \
for (cp = (start) ; *cp; cp++) { \
if (*cp == '[') _level++; \
if (*cp != ']') continue; \
if (--_level == 0) break; \
} \
} while (0)
if (*pattern == '[') {
FIND_TERMINATOR(pattern, cp);
if (cp[0] == 0) {
vstring_sprintf(byte_codes, "missing \"]\" character");
return (STR(byte_codes));
}
if (cp[1] == 0) {
*cp = 0;
pattern += 1;
}
}
if (*pattern == 0) {
vstring_sprintf(byte_codes, "empty address pattern");
return (STR(byte_codes));
}
VSTRING_RESET(byte_codes);
VSTRING_ADDCH(byte_codes, AF_INET);
octet_count = 0;
cp = pattern;
for (;;) {
switch (token_type = ip_match_next_token(&cp, &saved_cp, &oval)) {
case IP_MATCH_CODE_OVAL:
VSTRING_ADDCH(byte_codes, IP_MATCH_CODE_OVAL);
VSTRING_ADDCH(byte_codes, oval);
break;
case IP_MATCH_CODE_OPEN:
VSTRING_ADDCH(byte_codes, IP_MATCH_CODE_OPEN);
for (;;) {
token_type = ip_match_next_token(&cp, &saved_cp, &oval);
if (token_type == IP_MATCH_CODE_OVAL) {
saved_oval = oval;
look_ahead = ip_match_next_token(&cp, &saved_cp, &oval);
if (look_ahead == '.') {
if (ip_match_next_token(&cp, &saved_cp, &oval) == '.'
&& ip_match_next_token(&cp, &saved_cp, &oval)
== IP_MATCH_CODE_OVAL
&& saved_oval <= oval) {
VSTRING_ADDCH(byte_codes, IP_MATCH_CODE_RANGE);
VSTRING_ADDCH(byte_codes, saved_oval);
VSTRING_ADDCH(byte_codes, oval);
look_ahead =
ip_match_next_token(&cp, &saved_cp, &oval);
} else {
ipmatch_print_parse_error(byte_codes, pattern,
saved_cp, cp,
"numeric range error");
return (STR(byte_codes));
}
}
else {
VSTRING_ADDCH(byte_codes, IP_MATCH_CODE_OVAL);
VSTRING_ADDCH(byte_codes, saved_oval);
}
token_type = look_ahead;
if (token_type == ';') {
continue;
} else if (token_type == IP_MATCH_CODE_CLOSE) {
break;
} else {
ipmatch_print_parse_error(byte_codes, pattern,
saved_cp, cp,
"need \";\" or \"%c\"",
IP_MATCH_CODE_CLOSE);
return (STR(byte_codes));
}
} else {
ipmatch_print_parse_error(byte_codes, pattern, saved_cp, cp,
"need decimal number 0..255");
return (STR(byte_codes));
}
}
VSTRING_ADDCH(byte_codes, IP_MATCH_CODE_CLOSE);
break;
default:
ipmatch_print_parse_error(byte_codes, pattern, saved_cp, cp,
"need decimal number 0..255 or \"%c\"",
IP_MATCH_CODE_OPEN);
return (STR(byte_codes));
}
octet_count += 1;
if (octet_count == 4) {
if (*cp != 0) {
(void) ip_match_next_token(&cp, &saved_cp, &oval);
ipmatch_print_parse_error(byte_codes, pattern, saved_cp, cp,
"garbage after pattern");
return (STR(byte_codes));
}
VSTRING_ADDCH(byte_codes, 0);
return (0);
}
if (ip_match_next_token(&cp, &saved_cp, &oval) != '.') {
ipmatch_print_parse_error(byte_codes, pattern, saved_cp, cp,
"need \".\"");
return (STR(byte_codes));
}
}
}
#ifdef TEST
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <vstream.h>
#include <vstring_vstream.h>
#include <stringops.h>
int main(int argc, char **argv)
{
VSTRING *byte_codes = vstring_alloc(100);
VSTRING *line_buf = vstring_alloc(100);
char *bufp;
char *err;
char *user_pattern;
char *user_address;
int echo_input = !isatty(0);
while (vstring_fgets_nonl(line_buf, VSTREAM_IN)) {
bufp = STR(line_buf);
if (echo_input) {
vstream_printf("> %s\n", bufp);
vstream_fflush(VSTREAM_OUT);
}
if (*bufp == '#')
continue;
if ((user_pattern = mystrtok(&bufp, " \t")) == 0)
continue;
if ((err = ip_match_parse(byte_codes, user_pattern)) != 0) {
vstream_printf("Error: %s\n", err);
} else {
vstream_printf("Code: %s\n",
ip_match_dump(line_buf, STR(byte_codes)));
}
vstream_fflush(VSTREAM_OUT);
while ((user_address = mystrtok(&bufp, " \t")) != 0) {
struct in_addr netw_addr;
switch (inet_pton(AF_INET, user_address, &netw_addr)) {
case 1:
vstream_printf("Match %s: %s\n", user_address,
ip_match_execute(STR(byte_codes),
(char *) &netw_addr.s_addr) ?
"yes" : "no");
break;
case 0:
vstream_printf("bad address syntax: %s\n", user_address);
break;
case -1:
vstream_printf("%s: %m\n", user_address);
break;
}
vstream_fflush(VSTREAM_OUT);
}
}
vstring_free(line_buf);
vstring_free(byte_codes);
exit(0);
}
#endif