// // Created by akp on 05/10/23. // #pragma once #include "firewall_types.c" #include #include #include #include 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; }