This repository has been archived on 2025-07-20. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
ossp/firewall_parser.c

203 lines
5.1 KiB
C

//
// Created by akp on 05/10/23.
//
#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;
}