205 lines
5.1 KiB
C
205 lines
5.1 KiB
C
//
|
|
// Created by akp on 05/10/23.
|
|
//
|
|
#pragma once
|
|
|
|
#include "firewall_types.c"
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
enum ParseState {
|
|
ParseState_ip1,
|
|
ParseState_ip2,
|
|
ParseState_port1,
|
|
ParseState_port2,
|
|
};
|
|
|
|
// parse_ip parses an IP address line starting at startptr. Returns the ending
|
|
// position of the pointer in line. Returns NULL on failure.
|
|
char *parse_ip(char *startptr, struct IP *ip) {
|
|
char *endptr = startptr;
|
|
|
|
for (int i = 0; i < 4; i += 1) {
|
|
long n = strtol(endptr, &endptr, 10);
|
|
if (n > UCHAR_MAX || n < 0)
|
|
return NULL;
|
|
|
|
ip->val[i] = n;
|
|
|
|
if (i != 3) {
|
|
if (*endptr != '.' || *(endptr + 1) == '\0') {
|
|
return NULL;
|
|
} else {
|
|
endptr += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
endptr += 1;
|
|
|
|
return endptr;
|
|
}
|
|
|
|
// parse_port parses a port starting at startptr. Returns the ending position of
|
|
// the pointer in line. Returns NULL on failure.
|
|
char *parse_port(char *startptr, Port *port) {
|
|
char *end;
|
|
long n = strtol(startptr, &end, 10);
|
|
if (n > USHRT_MAX || n < 0)
|
|
return NULL;
|
|
*port = n;
|
|
return end;
|
|
}
|
|
|
|
// parse_rule parses a rule out a string, line. The result is saved to result,
|
|
// which must be allocated by the caller. The return value is the ending
|
|
// position of the rule in the string.
|
|
char *parse_rule(char line[], struct Rule *result) {
|
|
enum ParseState state = ParseState_ip1;
|
|
|
|
result->ip.end = NULL;
|
|
result->port.end = NULL;
|
|
|
|
int ptr = 0;
|
|
for (;;) {
|
|
switch (state) {
|
|
case ParseState_ip1: {
|
|
char *end = parse_ip(line + ptr, &result->ip.start);
|
|
if (end == NULL) {
|
|
goto fail;
|
|
}
|
|
ptr = (int)(end - line);
|
|
|
|
switch (line[ptr - 1]) {
|
|
case '-':
|
|
state = ParseState_ip2;
|
|
break;
|
|
case ' ':
|
|
state = ParseState_port1;
|
|
break;
|
|
default:
|
|
goto fail;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ParseState_ip2: {
|
|
result->ip.end = (struct IP *)malloc(sizeof(struct IP));
|
|
|
|
char *end = parse_ip(line + ptr, result->ip.end);
|
|
if (end == NULL) {
|
|
goto fail;
|
|
}
|
|
ptr = (int)(end - line);
|
|
|
|
state = ParseState_port1;
|
|
break;
|
|
}
|
|
case ParseState_port1: {
|
|
char *end = parse_port(line + ptr, &result->port.start);
|
|
if (end == NULL) {
|
|
goto fail;
|
|
}
|
|
ptr = (int)(end - line);
|
|
|
|
if (line[ptr] == '-') {
|
|
ptr += 1;
|
|
state = ParseState_port2;
|
|
} else {
|
|
goto finished_parsing;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ParseState_port2: {
|
|
result->port.end = (Port *)malloc(sizeof(Port));
|
|
|
|
char *end = parse_port(line + ptr, result->port.end);
|
|
if (end == NULL) {
|
|
goto fail;
|
|
}
|
|
ptr = (int)(end - line);
|
|
|
|
goto finished_parsing;
|
|
}
|
|
}
|
|
}
|
|
|
|
finished_parsing:
|
|
|
|
if (result->ip.end != NULL) {
|
|
if (compare_ip(&result->ip.start, result->ip.end) == 1) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if (result->port.end != NULL) {
|
|
if (akpa_numcmp(result->port.start, *result->port.end) == 1) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
return line + ptr;
|
|
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
#define MAX_RULES 128
|
|
|
|
// parse_file parses a set of rules from the file named fname. The memory used
|
|
// by the return value should be freed by the caller. If the return value is
|
|
// NULL, an error was encoutered and echo'd.
|
|
struct Rule **parse_file(char fname[], size_t *num_parsed_rules) {
|
|
int is_stdin = strcmp(fname, "--") == 0;
|
|
|
|
FILE *fp = NULL;
|
|
|
|
if (is_stdin == 1) {
|
|
fp = stdin;
|
|
} else {
|
|
fp = fopen(fname, "r");
|
|
if (fp == NULL) {
|
|
perror("Read input file");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
struct Rule **parsed_rules = malloc(sizeof(struct Rule *) * MAX_RULES);
|
|
*num_parsed_rules = 0;
|
|
|
|
char *line = NULL;
|
|
size_t line_size = 0;
|
|
while (getline(&line, &line_size, fp) != -1) {
|
|
if (*num_parsed_rules == MAX_RULES) {
|
|
fprintf(stderr, "Too many rules\n");
|
|
return NULL;
|
|
}
|
|
struct Rule *rule = (struct Rule *)malloc(sizeof(struct Rule));
|
|
char *endptr = parse_rule(line, rule);
|
|
if (endptr == NULL || *endptr != '\n') {
|
|
fprintf(stderr, "Ill formed rule: %s\n", line);
|
|
return NULL;
|
|
}
|
|
parsed_rules[*num_parsed_rules] = rule;
|
|
(*num_parsed_rules) += 1;
|
|
}
|
|
if (!feof(fp)) { // When we get here, getline has always returned -1 but may
|
|
// or may not have reached EOF.
|
|
// If it hasn't reached EOF, an error was thrown.
|
|
perror("Read line");
|
|
return NULL;
|
|
}
|
|
free(line);
|
|
|
|
if (is_stdin != 1) {
|
|
if (fclose(fp) != 0) {
|
|
perror("Close input file");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return parsed_rules;
|
|
}
|