/* $Id: reportdef.c,v 1.13 2006/03/03 17:27:38 bhockney Exp $ */ /* (C) 2004-2006 by Bob Hockney * * * * report definition parser for wfwl_syslog * * * * This code is distributed under the terms of GNU GPL */ #include #include #include #include #include #include #include #include #include #include #include #include "reportdef.h" #include "main.h" #include "utils.h" #include "getdate.h" struct sort_order *sort = NULL; struct field_order *fields = NULL; struct selection *selection = NULL; struct fields_used fields_used; struct select_sum select_sum; extern struct options opt; unsigned int sort_count = 0; unsigned int column_count = 0; unsigned int selection_count = 0; /* Function to get string parameter. * * Leading whitespace is discarded. If the next character is a * single or double quote, then line is taken until last matching * quote is encountered, with embedded quotes left intact. * Anything trailing the last matching quote is discarded. * If no matching quote is found, then the rest of the line is * returned including the first quote character, but with trailing * whitespace discarded. Leading and trailing whitespace is * included if within matching quotes. */ char * str_parameter(char *string) { char *quote, *startquote = NULL, *endquote, *pnt, *ret; ret = string; while (*ret == ' ' || *ret == '\t') ++ret; quote = strdup(ret); if ( *ret == '"' || *ret == '\'') { startquote = ret; xstrncpy(quote, ret, 1 + 1); } else { *quote = '\0'; startquote = ret; } ++ret; endquote = strrchr(ret, *quote); pnt = ret; while ((endquote == NULL || *endquote == '\0' || endquote != pnt) && *pnt != '\n' && *pnt != '\0') ++pnt; /* When pnt reaches matching end quote, or end of string, terminator is * * written over current character. If matching quote was found, opening * * quote is overwritten with space. Trailing whitespace, if any, is * * discarded */ *pnt = '\0'; if ((endquote != NULL && (*quote == '"' || *quote == '\'') && endquote == pnt)) { *startquote = ' '; } else { --ret; } if (*endquote == '\0' || endquote != pnt) { --pnt; while (*pnt == ' ' || *pnt == '\t') { *pnt = '\0'; --pnt; } } free(quote); return ret; } unsigned long int num_parameter(char *string, int linenum) { char *pnt, *ret; ret = string; while (*ret == ' ' || *ret == '\t' || *ret == '"') ++ret; pnt = ret; while (*pnt != '\n' && *pnt != ' ' && *pnt != '"' && *pnt != ':' && *pnt != '#' && *pnt != ',' && *pnt != '\t' && *pnt != '\0') { if(!isxdigit((int)*pnt) && strncasecmp(pnt, "x", 1) != 0) if (opt.verbose >= VERBOSE_INFO) fprintf(stderr, "Error in report definition, line %d: %s not a number\n", linenum, pnt); ++pnt; } *pnt = '\0'; return strtoul(ret,NULL,0); } unsigned char yes_or_no(char *string, int linenum) { char *pnt, *orig; orig = strdup(string); while (*string == ' ' || *string == '\t' || *string == '"') ++string; pnt = string; while (*pnt != '\n' && *pnt != ' ' && *pnt != '"' && *pnt != '#' && *pnt != '\t' && *pnt != '\0') ++pnt; *pnt = '\0'; if((strncasecmp(string, "yes", 3) == 0) || (strncasecmp(string, "on", 2) == 0) || (strncasecmp(string, "true", 4) == 0)) { if (opt.verbose >= VERBOSE_DEBUG) fprintf(stderr, "%s was parsed to %s and evaluated to true\n", orig, string); free(orig); return YES; } else if((strncasecmp(string, "no", 2) == 0) || (strncasecmp(string, "off", 3) == 0) || (strncasecmp(string, "false", 5) == 0)) { if (opt.verbose >= VERBOSE_DEBUG) fprintf(stderr, "%s was parsed to %s and evaluated to false\n", orig, string); free(orig); return NO; } else { if (opt.verbose >= VERBOSE_DEBUG) fprintf(stderr, "Error in report definition, line %d, assuming 'true'\n", linenum); free(orig); return YES; } } int resolve_protobyname(char *proto) { struct protoent *protoent; proto = str_parameter(proto); protoent = getprotobyname(proto); if (protoent != NULL) { return (protoent->p_proto); } else { return 0; } } /* Adds an item to the fields list. */ struct field_order * add_column(struct field_order *new, char *key, char *parm, int linenum) { struct field_order *col, *list = NULL, *last = NULL, *this = NULL, *next = NULL; col = xmalloc(sizeof(struct field_order)); col->next = NULL; col->field = -1; col->keyname[0] = '\0'; list = new; if(strcmp("extra", key) == 0) goto ignore; if(strcmp("extra_value", key) == 0) goto ignore; if(strcmp("oob_earliest", key) == 0) goto ignore; if(strcmp("oob_latest", key) == 0) goto ignore; if(strcmp("oob_time_sec", key) == 0) goto ignore; if(strcmp("oob_time_usec", key) == 0) goto ignore; col->position = num_parameter(parm, linenum); if(strcmp("ahesp_spi", key) == 0) {fields_used.ahesp_spi = 1; col->field = COL_AHESP_SPI;} if(strcmp("count", key) == 0) {fields_used.count = 1; col->field = COL_COUNT;} if(strcmp("dport", key) == 0) {fields_used.dport = 1; col->field = COL_DESTPORT;} if(strcmp("ip_daddr", key) == 0) {fields_used.dhost = 1; col->field = COL_DESTHOST;} if(strcmp("earliest", key) == 0) {fields_used.earliest = 1; col->field = COL_START_TIME;} if(strcmp("icmp_code", key) == 0) {fields_used.icmp_code = 1; col->field = COL_ICMP_CODE;} if(strcmp("icmp_echoid", key) == 0) {fields_used.icmp_echoid = 1; col->field = COL_ICMP_ECHOID;} if(strcmp("icmp_echoseq", key) == 0) {fields_used.icmp_echoseq = 1; col->field = COL_ICMP_ECHOSEQ;} if(strcmp("icmp_fragmtu", key) == 0) {fields_used.icmp_mtu = 1; col->field = COL_ICMP_MTU;} if(strcmp("icmp_gateway", key) == 0) {fields_used.icmp_gw = 1; col->field = COL_ICMP_GATEWAY;} if(strcmp("icmp_type", key) == 0) {fields_used.icmp_type = 1; col->field = COL_ICMP_TYPE;} if(strcmp("ip_csum", key) == 0) {fields_used.csum = 1; col->field = COL_IP_CSUM;} if(strcmp("ip_fragoff", key) == 0) {fields_used.fragoff = 1; col->field = COL_IP_FRAGOFF;} if(strcmp("ip_id", key) == 0) {fields_used.ipid = 1; col->field = COL_IP_ID;} if(strcmp("ip_ihl", key) == 0) {fields_used.ihl = 1; col->field = COL_IP_IHL;} if(strcmp("ip_protocol", key) == 0) {fields_used.protocol = 1; col->field = COL_IP_PROTO;} if(strcmp("ip_saddr", key) == 0) {fields_used.shost = 1; col->field = COL_SOURCEHOST;} if(strcmp("ip_tos", key) == 0) {fields_used.tos = 1; col->field = COL_IP_TOS;} if(strcmp("ip_totlen", key) == 0) {fields_used.totlen = 1; col->field = COL_IP_LEN;} if(strcmp("ip_ttl", key) == 0) {fields_used.ttl = 1; col->field = COL_IP_TTL;} if(strcmp("latest", key) == 0) {fields_used.latest = 1; col->field = COL_END_TIME;} if(strcmp("local_hostname", key) == 0) {fields_used.hostname = 1; col->field = COL_LOCAL_HOST;} if(strcmp("local_time", key) == 0) {fields_used.local_time = 1; col->field = COL_LOCAL_TIME;} if(strcmp("oob_in", key) == 0) {fields_used.inif = 1; col->field = COL_IN_IF;} if(strcmp("oob_mark", key) == 0) {fields_used.fwmark = 1; col->field = COL_FWMARK;} if(strcmp("oob_out", key) == 0) {fields_used.outif = 1; col->field = COL_OUT_IF;} if(strcmp("oob_prefix", key) == 0) {fields_used.log_label = 1; col->field = COL_LOG_PREFIX;} if(strcmp("raw_mac", key) == 0) {fields_used.raw_mac = 1; col->field = COL_MAC;} if(strcmp("sport", key) == 0) {fields_used.sport = 1; col->field = COL_SOURCEPORT;} if(strcmp("tcp_ackseq", key) == 0) {fields_used.tcp_ack_seq = 1; col->field = COL_TCP_ACKSEQ;} if(strcmp("tcp_options", key) == 0) {fields_used.flags = 1; col->field = COL_TCP_OPTIONS;} if(strcmp("tcp_seq", key) == 0) {fields_used.tcp_seq = 1; col->field = COL_TCP_SEQ;} if(strcmp("tcp_urgp", key) == 0) {fields_used.tcp_urgp = 1; col->field = COL_TCP_URGP;} if(strcmp("tcp_window", key) == 0) {fields_used.tcp_window = 1; col->field = COL_TCP_WINDOW;} if(strcmp("udp_len", key) == 0) {fields_used.udp_len = 1; col->field = COL_UDP_LEN;} if(strcmp("id", key) == 0) {fields_used.udp_len = 1; col->field = COL_ID;} if(strcmp("dst_host", key) == 0) { fields_used.dhost_name = 1; col->field = COL_DESTHOST_NAME; opt.resolve_hosts |= CACHE_RESOLVE; } if(strcmp("src_host", key) == 0) { fields_used.shost_name = 1; col->field = COL_SOURCEHOST_NAME; opt.resolve_hosts |= CACHE_RESOLVE; } if(strcmp("dst_service", key) == 0) { fields_used.dst_service = 1; col->field = COL_DEST_SERVICE; opt.resolve_hosts |= CACHE_RESOLVE; } if(strcmp("src_service", key) == 0) { fields_used.src_service = 1; col->field = COL_SOURCE_SERVICE; opt.resolve_hosts |= CACHE_RESOLVE; } /* skip any unrecognized columns */ if (col->field == -1) { if (opt.verbose >= VERBOSE_WARNING) fprintf(stderr, "Skipping unrecognized column: %s\n", key); free(col); return list; } strcpy(col->keyname, key); column_count++; last = list; this = list; next = NULL; if (this != NULL) next = this->next; while (this != NULL) { if (opt.verbose >= VERBOSE_ERROR) { fprintf(stderr, "Inserting column %s into list: this->position = %i\n", key, this->position); } if (col->position > this->position) { last = this; this = last->next; if (this != NULL) next = this->next; continue; } else { break; } } if (opt.verbose >= VERBOSE_DEBUG) { if (last != NULL) fprintf(stderr, "Last->position: %i\n", last->position); if (this != NULL) fprintf(stderr, "This->position: %i\n", this->position); if (next != NULL) fprintf(stderr, "Next->position: %i\n", next->position); } if (list == NULL) { if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "First item seen; position: %i\n", col->position); } list = col; } else { /* not first item seen */ if (list->next == NULL) { if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "Second item seen; position: %i\n", col->position); } if (col->position > list->position) { list->next = col; } else { this = list; list = col; list->next = this; } } else { /* not second item seen */ if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "Not first or second item; position: %i\n", col->position); } if (this == NULL) { if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "this->position = NULL = place item last\n"); } last->next = col; col->next = NULL; } else { /* not last */ if (this->position == last->position) { if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "this->position = last->position = place item first\n"); } col->next = list; list = col; } else { /* not first */ if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "this->position > last->position = insert item\n"); } last->next = col; col->next = this; } /* end: else clause of test to place item first */ } /* end: else clause of test to place item last */ } /* end: else clause of test for second item seen */ } /* end: else clause of test for first item seen */ if (opt.verbose >= VERBOSE_WARNING) fprintf(stderr, "Added column %s at position %i\n", key, col->position); return list; ignore: if (opt.verbose >= VERBOSE_ERROR) { fprintf(stderr, "ignoring key = %s\n", key); } free(col); return list; } /* Adds an item to the criteria list. * * If selection->have_value = -1, then only invert flag was present * * and entry should not be used for sorting */ void add_selection(char *key, char *parm, int linenum, int mode) { struct selection *sel = NULL, *addl = NULL, *prior = NULL; int seen; static int fragoff_flags = 0; char *ptr = NULL, *ptrr, *parms; struct in_addr inp; parms = strdup(parm); sel = xmalloc(sizeof(struct selection)); sel->value = 0; sel->max_value = 0; *sel->svalue = '\0'; sel->netmask = -1; sel->invert = 0; sel->field = -1; sel->next = NULL; if(strcmp("icmp_code", key) == 0) {sel->field = SEL_ICMP_CODE; sel->type = SEL_NUM;} if(strcmp("icmp_type", key) == 0) {sel->field = SEL_ICMP_TYPE; sel->type = SEL_NUM;} if(strcmp("tcp_dport", key) == 0) {sel->field = SEL_TCP_DPORT; sel->type = SEL_NUM;} if(strcmp("tcp_sport", key) == 0) {sel->field = SEL_TCP_SPORT; sel->type = SEL_NUM;} if(strcmp("udp_dport", key) == 0) {sel->field = SEL_UDP_DPORT; sel->type = SEL_NUM;} if(strcmp("udp_sport", key) == 0) {sel->field = SEL_UDP_SPORT; sel->type = SEL_NUM;} if(strcmp("ip_tos", key) == 0) {sel->field = SEL_IP_TOS; sel->type = SEL_NUM;} if(strcmp("ip_ttl", key) == 0) {sel->field = SEL_IP_TTL; sel->type = SEL_NUM;} if(strcmp("ip_ihl", key) == 0) {sel->field = SEL_IP_IHL; sel->type = SEL_NUM;} if(strcmp("ip_totlen", key) == 0) {sel->field = SEL_IP_TOTLEN; sel->type = SEL_NUM;} if(strcmp("ip_id", key) == 0) {sel->field = SEL_IP_ID; sel->type = SEL_NUM;} if(strcmp("ip_csum", key) == 0) {sel->field = SEL_IP_CSUM; sel->type = SEL_NUM;} if(strcmp("ip_fragoff", key) == 0) {sel->field = SEL_IP_FRAGOFF; sel->type = SEL_NUM;} if(strcmp("oob_mark", key) == 0) {sel->field = SEL_OOB_MARK; sel->type = SEL_NUM;} if(strcmp("tcp_seq", key) == 0) {sel->field = SEL_TCP_SEQ; sel->type = SEL_NUM;} if(strcmp("tcp_ackseq", key) == 0) {sel->field = SEL_TCP_ACKSEQ; sel->type = SEL_NUM;} if(strcmp("tcp_window", key) == 0) {sel->field = SEL_TCP_WINDOW; sel->type = SEL_NUM;} if(strcmp("tcp_urgp", key) == 0) {sel->field = SEL_TCP_URGP; sel->type = SEL_NUM;} if(strcmp("icmp_echoid", key) == 0) {sel->field = SEL_ICMP_ECHOID; sel->type = SEL_NUM;} if(strcmp("icmp_echoseq", key) == 0) {sel->field = SEL_ICMP_ECHOSEQ; sel->type = SEL_NUM;} if(strcmp("icmp_fragmtu", key) == 0) {sel->field = SEL_ICMP_FRAGMTU; sel->type = SEL_NUM;} if(strcmp("udp_len", key) == 0) {sel->field = SEL_UDP_LEN; sel->type = SEL_NUM;} if(strcmp("ahesp_spi", key) == 0) {sel->field = SEL_AHESP_SPI; sel->type = SEL_NUM;} if(strcmp("ip_daddr", key) == 0) {sel->field = SEL_DESTHOST; sel->type = SEL_IPADDR;} if(strcmp("ip_saddr", key) == 0) {sel->field = SEL_SOURCEHOST; sel->type = SEL_IPADDR;} if(strcmp("local_hostname", key) == 0) {sel->field = SEL_LOCAL_HOST; sel->type = SEL_REGEX;} if(strcmp("oob_prefix", key) == 0) {sel->field = SEL_PREFIX; sel->type = SEL_REGEX;} if(strcmp("oob_in", key) == 0) {sel->field = SEL_IN_IF; sel->type = SEL_REGEX;} if(strcmp("oob_out", key) == 0) {sel->field = SEL_OUT_IF; sel->type = SEL_REGEX;} if(strcmp("icmp_gateway", key) == 0) {sel->field = SEL_ICMP_GATEWAY; sel->type = SEL_IPADDR;} if(strcmp("raw_mac", key) == 0) {sel->field = SEL_RAW_MAC; sel->type = SEL_REGEX;} if(strcmp("ip_protocol", key) == 0) {sel->field = SEL_PROTOCOL; sel->type = SEL_PROTO;} /* if w_id is set, then we are doing a packet report */ if(strcmp("id", key) == 0) {opt.packet = num_parameter(parm, linenum); free(sel); return;} /* These are aggregate values; add to select_sum and return */ if(strcmp("min_count", key) == 0) {select_sum.min_count = num_parameter(parm, linenum); free(sel); return;} if(strcmp("max_count", key) == 0) {select_sum.max_count = num_parameter(parm, linenum); free(sel); return;} if(strcmp("max_earliest", key) == 0) {select_sum.max_earliest = get_date(str_parameter(parm), NULL); free(sel); return;} if(strcmp("min_latest", key) == 0) {select_sum.min_latest = get_date(str_parameter(parm), NULL); free(sel); return;} /* don't do these for DEF_INVERT */ if (mode == DEF_WHERE) { if(strcmp("max_date", key) == 0) {sel->field = SEL_MAX_DATE; sel->type = SEL_DATE;} if(strcmp("min_date", key) == 0) {sel->field = SEL_MIN_DATE; sel->type = SEL_DATE;} if(strcmp("tcp_flags", key) == 0) {sel->field = SEL_TCP_FLAGS; sel->type = SEL_BOOLEAN;} if(strcmp("tcp_ack", key) == 0) {sel->field = SEL_TCP_ACK; sel->type = SEL_BOOLEAN;} if(strcmp("tcp_fin", key) == 0) {sel->field = SEL_TCP_FIN; sel->type = SEL_BOOLEAN;} if(strcmp("tcp_options", key) == 0) {sel->field = SEL_TCP_OPTS_EXACT; sel->type = SEL_BOOLEAN;} if(strcmp("tcp_psh", key) == 0) {sel->field = SEL_TCP_PSH; sel->type = SEL_BOOLEAN;} if(strcmp("tcp_rst", key) == 0) {sel->field = SEL_TCP_RST; sel->type = SEL_BOOLEAN;} if(strcmp("tcp_syn", key) == 0) {sel->field = SEL_TCP_SYN; sel->type = SEL_BOOLEAN;} if(strcmp("tcp_urg", key) == 0) {sel->field = SEL_TCP_URG; sel->type = SEL_BOOLEAN;} if(strcmp("ip_df", key) == 0) {sel->field = SEL_IP_DF; sel->type = SEL_NUM;} if(strcmp("ip_mf", key) == 0) {sel->field = SEL_IP_MF; sel->type = SEL_NUM;} } if(strcmp("extra", key) == 0) goto ignore; if(strcmp("oob_max_date", key) == 0) goto ignore; if(strcmp("oob_min_date", key) == 0) goto ignore; if(strcmp("oob_max_earliest", key) == 0) goto ignore; if(strcmp("oob_min_latest", key) == 0) goto ignore; if(strcmp("oob_time_usec", key) == 0) goto ignore; /* skip any unrecognized criterion */ if (sel->field == -1) { if (opt.verbose >= VERBOSE_WARNING) fprintf(stderr, "Skipping unrecognized criterion: %s\n", key); free(sel); return; } strcpy(sel->keyname, key); switch (mode) { case DEF_INVERT: sel->invert = yes_or_no(parm, linenum); break; case DEF_WHERE: switch (sel->type) { case SEL_PROTO: strcpy(sel->svalue, str_parameter(parm)); ptr = strpbrk((char *)&sel->svalue, ","); if (ptr != NULL) *ptr = '\0'; if(isdigit((unsigned char)*sel->svalue)) { sel->value = num_parameter(parm, linenum); ptrr = strpbrk((char *)&sel->svalue, ":"); if (ptrr == NULL) { sel->max_value = sel->value; } else { sel->max_value = num_parameter(++ptrr, linenum); } } else { sel->value = resolve_protobyname(sel->svalue); sel->max_value = sel->value; } if (ptr != NULL) parm = ++ptr; break; case SEL_NUM: strcpy((char *)&sel->svalue, str_parameter(parm)); sel->value = num_parameter(parm, linenum); ptr = strpbrk((char *)&sel->svalue, ","); if (ptr != NULL) *ptr = '\0'; ptrr = strpbrk((char *)&sel->svalue, ":"); if (ptrr == NULL) { sel->max_value = sel->value; } else { sel->max_value = num_parameter(++ptrr, linenum); } if (ptr != NULL) parm = ++ptr; break; case SEL_IPADDR: strcpy(sel->svalue, str_parameter(parm)); ptr = strpbrk((char *)&sel->svalue, ","); if (ptr != NULL) *ptr = '\0'; ptrr = strpbrk((char *)&sel->svalue, "/"); if (ptrr != NULL) { *ptrr = '\0'; if (strpbrk(++ptrr, ".") == NULL) { sel->netmask = ~(uint32_t)(pow(2, 32 - num_parameter(ptrr, linenum)) - 1); } else { sel->netmask = inet_network(str_parameter(ptrr)); } } if (ptr != NULL) parm = ++ptr; break; case SEL_REGEX: case SEL_DATE: strcpy(sel->svalue, str_parameter(parm)); break; case SEL_BOOLEAN: sel->value = yes_or_no(parm, linenum); break; default: /* error */ break; } /* end case DEF_WHERE */ break; default: /* error */ break; } /* end switch (mode) */ if (sel->field == SEL_TCP_FLAGS) { sel->value = 0; strcpy(sel->keyname, "tcp_flags"); } if (sel->field == SEL_TCP_SYN) { sel->value = TCP_SYN; strcpy(sel->keyname, "tcp_flags"); sel->field = SEL_TCP_FLAGS; } if (sel->field == SEL_TCP_ACK) { sel->value = TCP_ACK; strcpy(sel->keyname, "tcp_flags"); sel->field = SEL_TCP_FLAGS; } if (sel->field == SEL_TCP_FIN) { sel->value = TCP_FIN; strcpy(sel->keyname, "tcp_flags"); sel->field = SEL_TCP_FLAGS; } if (sel->field == SEL_TCP_RST) { sel->value = TCP_RST; strcpy(sel->keyname, "tcp_flags"); sel->field = SEL_TCP_FLAGS; } if (sel->field == SEL_TCP_URG) { sel->value = TCP_URG; strcpy(sel->keyname, "tcp_flags"); sel->field = SEL_TCP_FLAGS; } if (sel->field == SEL_TCP_PSH) { sel->value = TCP_PSH; strcpy(sel->keyname, "tcp_flags"); sel->field = SEL_TCP_FLAGS; } if (sel->field == SEL_TCP_OPTS_EXACT) { sel->value = TCP_OPTS_EXACT; strcpy(sel->keyname, "tcp_flags"); sel->field = SEL_TCP_FLAGS; } if (sel->field == SEL_IP_DF || sel->field == SEL_IP_MF) { fragoff_flags |= sel->value; sel->field = SEL_IP_FRAGOFF; strcpy(sel->keyname, "ip_fragoff"); } prior = selection; seen = 0; while (prior != NULL) { if (prior->field == sel->field) { seen = 1; if (mode == DEF_INVERT) { prior->invert = sel->invert; } if (sel->field == SEL_TCP_FLAGS) sel->value = sel->value | prior->value; if (sel->field == SEL_IP_FRAGOFF) { sel->value |= prior->value; sel->max_value |= prior->max_value; } if (mode == DEF_WHERE) { prior->have_value = 0; prior->value = sel->value; prior->max_value = sel->max_value; strcpy(prior->svalue, sel->svalue); sel->invert = prior->invert; /* for multiple add */ break; } } /* not seen */ prior = prior->next; } if (!seen) { selection_count++; } if (!seen && mode == DEF_INVERT) { sel->have_value = -1; } else { sel->have_value = 0; } if (!seen) { sel->next = selection; selection= sel; } if (opt.verbose >= VERBOSE_WARNING) { switch (mode) { case DEF_INVERT: fprintf(stderr, "Added invert flag for criterion: %s\n", key); break; case DEF_WHERE: if (sel->field == SEL_TCP_FLAGS) { fprintf(stderr, "Added criterion: %s\n", key); } else { inp.s_addr = htonl(sel->netmask); fprintf(stderr, "Added criterion: %s; Value: %lu; Max value: %lu; Svalue: %s; Netmask: %s\n", key, sel->value, sel->max_value, sel->svalue, inet_ntoa(inp)); } break; default: break; } } /* If multiple values are specified, add new selection criterion for each */ str_parameter(parms); /* drop quotes */ if (mode == DEF_WHERE && (sel->type == SEL_NUM || sel->type == SEL_IPADDR || sel->type == SEL_PROTO) && ptr != NULL ) { while (ptr != NULL) { addl = xmalloc(sizeof(struct selection)); addl->field = sel->field; strcpy(addl->keyname, sel->keyname); addl->type = sel->type; addl->invert = sel->invert; addl->have_value = sel->have_value; addl->netmask = -1; strcpy((char *)&addl->svalue, str_parameter(parm)); ptr = strpbrk((char *)&addl->svalue, ","); if (ptr != NULL) *ptr = '\0'; if (sel-> type == SEL_NUM) { addl->value = num_parameter(parm, linenum); ptrr = strpbrk((char *)&addl->svalue, ":"); if (ptrr == NULL) { addl->max_value = addl->value; } else { addl->max_value = num_parameter(++ptrr, linenum); } if (sel->field == SEL_IP_FRAGOFF) { addl->value |= fragoff_flags; addl->max_value |= fragoff_flags; } } else if (sel->type == SEL_IPADDR) { ptrr = strpbrk((char *)&addl->svalue, "/"); if (ptrr != NULL) { *ptrr = '\0'; if (strpbrk(++ptrr, ".") == NULL) { addl->netmask = ~(uint32_t)(pow(2, 32 - num_parameter(ptrr, linenum)) - 1); } else { addl->netmask = inet_network(str_parameter(ptrr)); } } } else if (sel->type == SEL_PROTO){ if(isdigit((unsigned char)*addl->svalue)) { addl->value = num_parameter(parm, linenum); ptrr = strpbrk((char *)&addl->svalue, ":"); if (ptrr == NULL) { addl->max_value = addl->value; } else { addl->max_value = num_parameter(++ptrr, linenum); } } else { addl->value = resolve_protobyname(addl->svalue); addl->max_value = addl->value; } } if (ptr != NULL) parm = ++ptr; if (opt.verbose >= VERBOSE_WARNING) { inp.s_addr = htonl(addl->netmask); fprintf(stderr, "Added criterion: %s; Value: %lu; Max value %lu; Svalue: %s; Netmask: %s\n", key, addl->value, addl->max_value, addl->svalue, inet_ntoa(inp)); } addl->next = selection; selection = addl; selection_count++; } } free(parms); if (seen) { free(sel); } return; ignore: if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "ignoring key = %s\n", key); } free(parms); free(sel); return; } /* Adds an item to the sort list. * * If sort_order->position = -35768, then only order flag was present * * and entry should not be used for sorting */ struct sort_order * add_sort(struct sort_order *new, char *key, char *parm, int linenum, int mode) { struct sort_order *srt = NULL, *have = NULL, *last = NULL, *this = NULL, *prev = NULL, *next = NULL, *list = NULL, *old = NULL; int seen; srt = xmalloc(sizeof(struct sort_order)); srt->next = NULL; srt->keyname[0] = '\0'; srt->order = 0; srt->position = 0; srt->field = -1; list = new; if(strcmp("ahesp_spi", key) == 0) srt->field = SORT_AHESP_SPI; if(strcmp("count", key) == 0) srt->field = SORT_COUNT; if(strcmp("dport", key) == 0) srt->field = SORT_DESTPORT; if(strcmp("earliest", key) == 0) srt->field = SORT_START_TIME; if(strcmp("icmp_code", key) == 0) srt->field = SORT_ICMP_CODE; if(strcmp("icmp_echoid", key) == 0) srt->field = SORT_ICMP_ECHOID; if(strcmp("icmp_echoseq", key) == 0) srt->field = SORT_ICMP_ECHOSEQ; if(strcmp("icmp_fragmtu", key) == 0) srt->field = SORT_ICMP_MTU; if(strcmp("icmp_gateway", key) == 0) srt->field = SORT_ICMP_GW; if(strcmp("icmp_type", key) == 0) srt->field = SORT_ICMP_TYPE; if(strcmp("ip_csum", key) == 0) srt->field = SORT_CSUM ; if(strcmp("ip_daddr", key) == 0) srt->field = SORT_DESTHOST; if(strcmp("ip_fragoff", key) == 0) srt->field = SORT_FRAGOFF; if(strcmp("ip_id", key) == 0) srt->field = SORT_IP_ID; if(strcmp("ip_ihl", key) == 0) srt->field = SORT_IP_IHL; if(strcmp("ip_protocol", key) == 0) srt->field = SORT_PROTOCOL; if(strcmp("ip_saddr", key) == 0) srt->field = SORT_SOURCEHOST; if(strcmp("ip_tos", key) == 0) srt->field = SORT_IP_TOS; if(strcmp("ip_totlen", key) == 0) srt->field = SORT_IP_TOTLEN; if(strcmp("ip_ttl", key) == 0) srt->field = SORT_IP_TTL; if(strcmp("latest", key) == 0) srt->field = SORT_END_TIME; if(strcmp("local_hostname", key) == 0) srt->field = SORT_LOCAL_HOST; if(strcmp("local_time", key) == 0) srt->field = SORT_LOCAL_TIME; if(strcmp("oob_in", key) == 0) srt->field = SORT_IN_IF; if(strcmp("oob_mark", key) == 0) srt->field = SORT_FWMARK; if(strcmp("oob_out", key) == 0) srt->field = SORT_OUT_IF; if(strcmp("oob_prefix", key) == 0) srt->field = SORT_PREFIX; if(strcmp("raw_mac", key) == 0) srt->field = SORT_MAC; if(strcmp("sport", key) == 0) srt->field = SORT_SOURCEPORT; if(strcmp("tcp_ackseq", key) == 0) srt->field = SORT_TCP_ACKSEQ; if(strcmp("tcp_options", key) == 0) srt->field = SORT_TCP_OPTS; if(strcmp("tcp_seq", key) == 0) srt->field = SORT_TCP_SEQ; if(strcmp("tcp_urgp", key) == 0) srt->field = SORT_TCP_URGP; if(strcmp("tcp_window", key) == 0) srt->field = SORT_TCP_WINDOW; if(strcmp("udp_len", key) == 0) srt->field = SORT_UDP_LEN; if(strcmp("id", key) == 0) srt->field = SORT_ID; if(strcmp("oob_time_sec", key) == 0) goto ignore; if(strcmp("oob_time_usec", key) == 0) goto ignore; if(strcmp("oob_earliest", key) == 0) goto ignore; if(strcmp("oob_latest", key) == 0) goto ignore; /* skip any unrecognized criterion */ if (srt->field == -1) { if (opt.verbose >= VERBOSE_WARNING) fprintf(stderr, "Skipping unrecognized sort field: %s\n", key); free(srt); return list; } strcpy(srt->keyname, key); switch (mode) { case DEF_SORT: srt->position = num_parameter(parm, linenum); break; case DEF_ORDER: if (yes_or_no(parm, linenum)){ srt->order = ORDER_DESCENDING; } break; default: /* error */ break; } have = list; seen = 0; while (have != NULL) { if (have->field == srt->field) { seen = 1; break; } else { prev = have; have = have->next; } } if (seen) { if (mode == DEF_SORT) { /* If we have seen key before and are doing sort position now, * break it out of list and treat it as new item to be inserted */ have->position = srt->position; /* if item is currently first in list, make second item first */ if (list == have) list = list->next; old = srt; srt = have; free(old); if (prev != NULL) { prev->next = have->next; } srt->next = NULL; } else { if (mode == DEF_ORDER) { have->order = srt->order; } else ; /* error */ } } else if (mode == DEF_ORDER) { srt->position = SORT_INV_NO_SORT; srt->next = list; list = srt; free(have); } if (mode == DEF_SORT) { sort_count++; last = list; this = list; next = NULL; if (this != NULL) next = this->next; while (this != NULL) { if (opt.verbose >= VERBOSE_ERROR) { fprintf(stderr, "Inserting sort field %s into list: this->position = %i\n", key, this->position); } if (srt->position < this->position) { last = this; this = last->next; if (this != NULL) next = this->next; continue; } else { break; } } if (opt.verbose >= VERBOSE_DEBUG) { if (last != NULL) fprintf(stderr, "Last->position: %i\n", last->position); if (this != NULL) fprintf(stderr, "This->position: %i\n", this->position); if (next != NULL) fprintf(stderr, "Next->position: %i\n", next->position); } if (list == NULL || (list->position == SORT_INV_NO_SORT && last->position == SORT_INV_NO_SORT)) { if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "First item seen; position: %i\n", srt->position); } if (list != NULL) srt->next = list; list = srt; } else { /* not first item seen */ if (list->next == NULL || (this == NULL && list->next->position == SORT_INV_NO_SORT \ && last != NULL && srt->position > last->position)) { if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "Second item seen; position: %i\n", srt->position); } if (srt->position < list->position) { if (list->next != NULL) srt->next = list->next; list->next = srt; } else { this = list; list = srt; list->next = this; } } else { /* not second item seen */ if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "Not first or second item; position: %i\n", srt->position); } if (this == NULL) { if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "this->position = NULL = place item last\n"); } last->next = srt; srt->next = NULL; } else { /* not last */ if (this->position == last->position) { if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "this->position = last->position = place item first\n"); } srt->next = list; list = srt; } else { /* not first */ if (opt.verbose >= VERBOSE_DEBUG) { fprintf(stderr, "this->position < last->position = insert item\n"); } last->next = srt; srt->next = this; } /* end: else clause of test to place item first */ } /* end: else clause of test to place item last */ } /* end: else clause of test for second item seen */ } /* end: else clause of test for first item seen */ } /* end: if(mode == DEF_SORT} */ if (opt.verbose >= VERBOSE_WARNING) { switch (mode) { case DEF_ORDER: fprintf(stderr, "Added descending flag for sort field %s\n", key); break; case DEF_SORT: fprintf(stderr, "Added sort field %s at position %i\n", key, srt->position); break; default: break; } } return list; ignore: if (opt.verbose >= VERBOSE_ERROR) { fprintf(stderr, "ignoring key = %s\n", key); } free(srt); return list; } /* parses single line from reportdef */ /* called with (linebuf, reportdef_filename, linenum) */ void parse_reportdef(char *input, char *reportdef, int linenum) { char *command, *key, *parm; unsigned int type, len = 0; while (*input == ' ' || *input == '\t') ++input; if (*input == '#' || *input == '\n') return; command = strdup(input); key = strdup(input); len = strcspn(command, " ="); xstrncpy(key, command, len + 1); parm = strpbrk(command, "="); parm++; type = DEF_NONE; if (strncmp(key, "w_", 2) == 0) type = DEF_WHERE; if (strncmp(key, "i_", 2) == 0) type = DEF_INVERT; if (strncmp(key, "s_", 2) == 0) type = DEF_SORT; if (strncmp(key, "o_", 2) == 0) type = DEF_ORDER; if (strncmp(key, "c_", 2) == 0) type = DEF_COLUMN; /* Ignore these */ if (strncmp(key, "h_", 2) == 0) type = DEF_NONE; if (strncmp(key, "l_", 2) == 0) type = DEF_NONE; if (type != DEF_NONE) { key++; key++; } /* Other options */ if (strncmp(key, "summarize", 9) == 0) type = DEF_SUM; if (strncmp(key, "populate_cache", 14) == 0) { opt.resolve_hosts |= CACHE_POPULATE; type = DEF_OPTION; } if (strncmp(key, "upd_hosts", 9) == 0) { opt.resolve_hosts |= CACHE_UPDATE; type = DEF_OPTION; } if (strncmp(key, "syslog_dir", 10) == 0) { strcpy(opt.pathname, str_parameter(parm)); type = DEF_OPTION; } if (strncmp(key, "db", 2) == 0) { strcpy(opt.db, str_parameter(parm)); type = DEF_OPTION; } if (strncmp(key, "mysql_server", 12) == 0) { strcpy(opt.mysql_server, str_parameter(parm)); type = DEF_OPTION; } if (strncmp(key, "mysql_wfwl_db", 13) == 0) { strcpy(opt.mysql_wfwl_db, str_parameter(parm)); type = DEF_OPTION; } if (strncmp(key, "mysql_user", 10) == 0) { strcpy(opt.mysql_user, str_parameter(parm)); type = DEF_OPTION; } if (strncmp(key, "mysql_pass", 10) == 0) { strcpy(opt.mysql_pass, str_parameter(parm)); type = DEF_OPTION; } if (strncmp(key, "pgsql_server", 12) == 0) { strcpy(opt.pgsql_server, str_parameter(parm)); type = DEF_OPTION; } if (strncmp(key, "pgsql_db", 8) == 0) { strcpy(opt.pgsql_db, str_parameter(parm)); type = DEF_OPTION; } if (strncmp(key, "pgsql_user", 10) == 0) { strcpy(opt.pgsql_user, str_parameter(parm)); type = DEF_OPTION; } if (strncmp(key, "pgsql_pass", 10) == 0) { strcpy(opt.pgsql_pass, str_parameter(parm)); type = DEF_OPTION; } if (strncmp(key, "pgsql_wfwl_schema", 17) == 0) { strcpy(opt.pgsql_wfwl_schema, str_parameter(parm)); type = DEF_OPTION; } if (strncmp(key, "pgsql_have_namespace", 20) == 0) { opt.pgsql_have_namespace = 1; type = DEF_OPTION; } if (type == DEF_OPTION) { if (opt.verbose >= VERBOSE_WARNING) { if (strncmp(key,"mysql_pass", 10) == 0||strncmp(key,"pgsql_pass", 10) == 0) { fprintf(stderr, "Added option: %s; Value: %s\n", key, "************"); } else { fprintf(stderr, "Added option: %s; Value: %s\n", key, strip_nl(str_parameter(parm))); } } } else if (type != DEF_NONE) { if (opt.verbose >= VERBOSE_ERROR) { fprintf(stderr, "Processing report definition line Type: %u; Key: %s; Parameter: %s", type, key, parm); } } else { /* type == DEF_NONE */ if (opt.verbose >= VERBOSE_ERROR) { fprintf(stderr, "Ignoring report definition line: %s", command); } } switch (type) { case DEF_SUM: if (yes_or_no(parm, linenum)) { opt.mode = LOG_SUMMARY; } else { opt.mode = LOG_DETAIL; } break; case DEF_WHERE: add_selection(key, parm, linenum, DEF_WHERE); break; case DEF_INVERT: add_selection(key, parm, linenum, DEF_INVERT); break; case DEF_SORT: sort = add_sort(sort, key, parm, linenum, DEF_SORT); break; case DEF_ORDER: sort = add_sort(sort, key, parm, linenum, DEF_ORDER); break; case DEF_COLUMN: fields = add_column(fields, key, parm, linenum); break; case DEF_OPTION: case DEF_NONE: break; default: /* error */ break; } free(command); return; } /* Entry point; called from main.c */ unsigned char read_reportdef(char *reportdef) { char buf[BUFSIZE], *name; FILE *fd; int linenum = 1, retval; struct stat info; struct sort_order *srt; name = strdup(reportdef); /* validate reportdef file */ if (strcmp(reportdef, "-")) { retval = stat(reportdef, &info); if (retval == -1) { fprintf(stderr, "Cannot open report definition file %s, exiting.\n", reportdef); return EXIT_FAILURE; } if (!S_ISREG(info.st_mode)) { fprintf(stderr, "%s is not a regular file, exiting.\n", reportdef); return EXIT_FAILURE; } if(opt.verbose >= VERBOSE_INFO) fprintf(stderr, "Processing report definition file '%s'\n", name); fd = fopen(name, "r"); if (fd == NULL) { fprintf(stderr, "fopen %s: %s\n", name, strerror(errno)); exit(EXIT_FAILURE); } } else { if(opt.verbose >= VERBOSE_INFO) fprintf(stderr, "Processing report definition from stdin\n"); fd = stdin; } /* initialize fields_used */ /* this is used for summarization testing. * * tests done against this static data is faster by a factor of at * * least two over iterating though field_order linked list for each line. */ fields_used.count = 0; fields_used.local_time = 0; fields_used.earliest = 0; fields_used.latest = 0; fields_used.hostname = 0; fields_used.log_label = 0; fields_used.protocol = 0; fields_used.totlen = 0; fields_used.shost = 0; fields_used.sport = 0; fields_used.dhost = 0; fields_used.dport = 0; fields_used.shost_name = 0; fields_used.dhost_name = 0; fields_used.src_service = 0; fields_used.dst_service = 0; fields_used.flags = 0; fields_used.raw_mac = 0; fields_used.fwmark = 0; fields_used.inif = 0; fields_used.outif = 0; fields_used.tos = 0; fields_used.ttl = 0; fields_used.ihl = 0; fields_used.csum = 0; fields_used.ipid = 0; fields_used.fragoff = 0; fields_used.tcp_seq = 0; fields_used.tcp_ack_seq = 0; fields_used.tcp_window = 0; fields_used.tcp_urgp = 0; fields_used.udp_len = 0; fields_used.icmp_type = 0; fields_used.icmp_code = 0; fields_used.icmp_echoid = 0; fields_used.icmp_echoseq = 0; fields_used.icmp_gw = 0; fields_used.icmp_mtu = 0; fields_used.ahesp_spi = 0; /* initialize select_sum */ select_sum.min_count = 0; select_sum.max_count = 0; select_sum.max_earliest = 0; select_sum.min_latest = 0; while (fgets(buf, BUFSIZE, fd)) { if (opt.verbose >= VERBOSE_DEBUG) fprintf(stderr, "Input line is: %s", buf); parse_reportdef(buf, name, linenum); linenum++; } if (opt.verbose >= VERBOSE_INFO) { fprintf(stderr, "Number of columns: %u\n", column_count); } if (opt.verbose >= VERBOSE_NOTICE) { struct field_order *col; col = xmalloc(sizeof(struct field_order)); col = fields; while (col != NULL) { fprintf(stderr, "COLUMNS: Key: %s; Field %i; Position: %i\n", col->keyname, col->field, col->position); col = col->next; } free(col); } if (opt.verbose >= VERBOSE_INFO) { fprintf(stderr, "Number of criteria: %u\n", selection_count); } if (opt.verbose >= VERBOSE_NOTICE) { struct selection *sel; struct in_addr in; sel = xmalloc(sizeof(struct selection)); sel = selection; while (sel != NULL) { in.s_addr = htonl(sel->netmask); fprintf(stderr, "SELECTION: Key: %s; Field %i; Value: %lu Max value: %lu; Svalue: %s; Netmask: %s; Invert: %i; Have Value: %i\n", sel->keyname, sel->field, sel->value, sel->max_value, sel->svalue, inet_ntoa(in), sel->invert, sel->have_value); sel = sel->next; } free(sel); if (select_sum.min_count !=0) fprintf(stderr, "SELECTION: Key: min_count; Value: %lu\n", select_sum.min_count); if (select_sum.max_count !=0) fprintf(stderr, "SELECTION: Key: max_count; Value: %lu\n", select_sum.max_count); if (select_sum.max_earliest !=0) fprintf(stderr, "SELECTION: Key: max_earliest; Value: %lu\n", (unsigned long int)select_sum.max_earliest); if (select_sum.min_latest !=0) fprintf(stderr, "SELECTION: Key: min_latest; Value: %lu\n", (unsigned long int)select_sum.min_latest); } if (opt.verbose >= VERBOSE_INFO) { fprintf(stderr, "Number of sorts: %u\n", sort_count); } if (opt.verbose >= VERBOSE_NOTICE) { srt = xmalloc(sizeof(struct sort_order)); srt = sort; while (srt != NULL) { fprintf(stderr, "SORT: Key: %s; Field %i; Position: %i Order: %i\n", srt->keyname, srt->field, srt->position, srt->order); srt = srt->next; } free(srt); } if ((strcmp(reportdef, "-"))) { if(opt.verbose >= VERBOSE_WARNING) fprintf(stderr, "Done processing report definition file '%s'\n", name); } else { if(opt.verbose >= VERBOSE_WARNING) fprintf(stderr, "Done processing report definition from stdin\n"); } free(name); retval = fclose(fd); if (retval == EOF) { perror("fclose"); exit(EXIT_FAILURE); } return EXIT_SUCCESS; }