#define BUILD_MATCH #define MODULE_DATATYPE struct ipt_tcp #define MODULE_NAME "tcp" #define __USE_GNU #include "../module_iface.h" #include #include #include #include #include #include typedef struct { char *name; unsigned int value; } FlagList; FlagList tcp_flags[] = { {"FIN", 1 << 0}, {"SYN", 1 << 1}, {"RST", 1 << 2}, {"PSH", 1 << 3}, {"ACK", 1 << 4}, {"URG", 1 << 5}, {"ALL", 0x3f}, {"NONE", 0}, }; static void setup(void *myinfo, unsigned int *nfcache) { MODULE_DATATYPE *info = (void *)((MODULE_ENTRYTYPE *)myinfo)->data; info->spts[0] = info->dpts[0] = 0; info->spts[1] = info->dpts[1] = USHRT_MAX; } static bool parse_ports_sv(SV *portval, u_int16_t *ports, int *inv, const char *protocol_name) { struct servent *service; int val; *inv = FALSE; if(SvIOK(portval)) { val = SvIV(portval); if(val < 0 || val > USHRT_MAX) { SET_ERRSTR("Port number out of range"); return(FALSE); } ports[0] = ports[1] = val; } else if(SvPOK(portval)) { STRLEN len; char *temp = SvPV(portval, len); char *text = NULL, *sep = NULL, *base = NULL; base = text = malloc(len + 1); strncpy(text, temp, len); text[len] = '\0'; if(*text == INVCHAR) { *inv = TRUE; text++; } sep = strchr(text, ':'); if(sep == text) ports[0] = 0; else { if(sep) *sep = '\0'; if((service = getservbyname(text, protocol_name))) ports[0] = ntohs(service->s_port); else { val = strtoul(text, &temp, 10); if((text + strlen(text) > temp) || val < 0 || val > USHRT_MAX) { SET_ERRSTR("Unable to parse '%s'", text); free(base); return(FALSE); } ports[0] = val; } if(temp) *temp = ':'; } if(sep) { text = ++sep; if(*text == '\0') ports[1] = USHRT_MAX; else { if((service = getservbyname(text, protocol_name))) ports[1] = ntohs(service->s_port); else { val = strtoul(text, &temp, 10); if((text + strlen(text) > temp) || val < 0 || val > USHRT_MAX) { SET_ERRSTR("Unable to parse '%s'", text); free(base); return(FALSE); } ports[1] = val; } } } else ports[1] = ports[0]; free(base); } else { SET_ERRSTR("Must be passed as integer or string"); return(FALSE); } return(TRUE); } static bool assemble_mask_from_arrayref(SV *arrayref, FlagList *flags, int nflags, u_int8_t *mask) { SV **svp, *sv; char *temp, *str; int i, j; STRLEN len; if(!SvROK(arrayref) || !(sv = SvRV(arrayref)) || SvTYPE(sv) != SVt_PVAV) return(FALSE); for(i = 0; i <= av_len((AV *)sv); i++) { svp = av_fetch((AV *)sv, i, FALSE); if(!svp || !SvPOK(*svp)) return(FALSE); temp = SvPV(*svp, len); str = malloc(len + 1); strncpy(str, temp, len); str[len] = '\0'; for(j = 0; j < nflags; j++) { if(!strcmp(str, flags[j].name)) { *mask |= flags[j].value; break; } } free(str); if(j == nflags) return(FALSE); } return(TRUE); } static int parse_field(char *field, SV *value, void *myinfo, unsigned int *nfcache, struct ipt_entry *entry, int *flags) { MODULE_DATATYPE *info = (void *)(*(MODULE_ENTRYTYPE **)myinfo)->data; char *temp, *text, *base; SV **svp; int val, inv; STRLEN len; if(!strcmp(field, "source-port")) { if(!parse_ports_sv(value, info->spts, &inv, "tcp")) { char *temp = strdup(SvPV_nolen(ERROR_SV)); SET_ERRSTR("%s: %s", field, temp); free(temp); return(FALSE); } if(inv) info->invflags |= IPT_TCP_INV_SRCPT; *nfcache |= NFC_IP_SRC_PT; return(TRUE); } else if(!strcmp(field, "destination-port")) { if(!parse_ports_sv(value, info->dpts, &inv, "tcp")) { char *temp = strdup(SvPV_nolen(ERROR_SV)); SET_ERRSTR("%s: %s", field, temp); free(temp); return(FALSE); } if(inv) info->invflags |= IPT_TCP_INV_DSTPT; *nfcache |= NFC_IP_DST_PT; return(TRUE); } else if(!strcmp(field, "tcp-flags")) { if(!SvROK(value) || !(value = SvRV(value)) || SvTYPE(value) != SVt_PVHV) { SET_ERRSTR("%s: Must be passed as a hash ref", field); return(FALSE); } if(hv_fetch((HV *)value, "inv", 3, FALSE)) info->invflags |= IPT_TCP_INV_FLAGS; if(!(svp = hv_fetch((HV *)value, "mask", 4, FALSE)) || !assemble_mask_from_arrayref(*svp, tcp_flags, sizeof(tcp_flags) / sizeof(FlagList), &info->flg_mask)) { SET_ERRSTR("%s: Unable to parse mask flags", field); return(FALSE); } if(!(svp = hv_fetch((HV *)value, "comp", 4, FALSE)) || !assemble_mask_from_arrayref(*svp, tcp_flags, sizeof(tcp_flags) / sizeof(FlagList), &info->flg_cmp)) { SET_ERRSTR("%s: Unable to parse compare flags", field); return(FALSE); } *nfcache |= NFC_IP_TCPFLAGS; return(TRUE); } else if(!strcmp(field, "tcp-option")) { if(SvIOK(value)) val = SvIV(value); else if(SvPOK(value)) { temp = SvPV(value, len); base = text = malloc(len + 1); strncpy(text, temp, len); text[len] = '\0'; if(*text == INVCHAR) { info->invflags |= IPT_TCP_INV_OPTION; text++; } temp = NULL; val = strtoul(text, &temp, 10); if((temp - text) < strlen(text)) { SET_ERRSTR("%s: Unable to parse option number", field); free(base); return(FALSE); } free(base); } else { SET_ERRSTR("%s: Must be passed as integer or string", field); return(FALSE); } if(val < 0 || val > 0xFF) { SET_ERRSTR("%s: Value out of range", field); return(FALSE); } info->option = val; *nfcache |= NFC_IP_PROTO_UNKNOWN; return(TRUE); } return(FALSE); } static SV *build_flag_list_from_mask(u_int8_t mask, FlagList *flags, int nflags) { int i; AV *av = newAV(); for(i = 0; i < nflags; i++) { if(flags[i].value & mask) av_push(av, newSVpv(flags[i].name, 0)); } return((SV *)newRV_noinc((SV *)av)); } static SV *build_sv_from_portrange(u_int16_t *ports, bool inv) { char *temp, *temp2; struct servent *service; SV *sv; if((service = getservbyport(htons(ports[0]), "tcp"))) temp = strdup(service->s_name); else { if(ports[0] == ports[1] && !inv) return(newSViv(ports[0])); asprintf(&temp, "%u", ports[0]); } if(ports[0] != ports[1]) { if((service = getservbyport(htons(ports[1]), "tcp"))) asprintf(&temp2, "%s:%s", temp, service->s_name); else asprintf(&temp2, "%s:%u", temp, ports[1]); free(temp); temp = temp2; } if(inv) { asprintf(&temp2, "%c%s", INVCHAR, temp); free(temp); temp = temp2; } sv = newSVpv(temp, 0); free(temp); return(sv); } static void get_fields(HV *ent_hash, void *myinfo, struct ipt_entry *entry) { MODULE_DATATYPE *info = (void *)((MODULE_ENTRYTYPE *)myinfo)->data; char *temp; SV *sv; if(info->spts[0] != 0 || info->spts[1] != USHRT_MAX) { sv = build_sv_from_portrange(info->spts, info->invflags & IPT_TCP_INV_SRCPT); hv_store(ent_hash, "source-port", 11, sv, 0); } if(info->dpts[0] != 0 || info->dpts[1] != USHRT_MAX) { sv = build_sv_from_portrange(info->dpts, info->invflags & IPT_TCP_INV_DSTPT); hv_store(ent_hash, "destination-port", 16, sv, 0); } if(info->flg_mask || info->flg_cmp) { HV *hv = newHV(); if(info->flg_mask) hv_store(hv, "mask", 4, build_flag_list_from_mask(info->flg_mask, tcp_flags, (sizeof(tcp_flags) / sizeof(FlagList)) - 2), 0); if(info->flg_cmp) hv_store(hv, "comp", 4, build_flag_list_from_mask(info->flg_cmp, tcp_flags, (sizeof(tcp_flags) / sizeof(FlagList)) - 2), 0); if(info->invflags & IPT_TCP_INV_FLAGS) hv_store(hv, "inv", 3, newSViv(1), 0); hv_store(ent_hash, "tcp-flags", 9, newRV_noinc((SV *)hv), 0); } if(info->option) { if(info->invflags & IPT_TCP_INV_OPTION) { asprintf(&temp, "%c%u", INVCHAR, info->option); sv = newSVpv(temp, 0); free(temp); } else sv = newSViv(info->option); hv_store(ent_hash, "tcp-option", 10, sv, 0); } } static ModuleDef _module = { .type = MODULE_TYPE, .name = MODULE_NAME, .size = IPT_ALIGN(sizeof(MODULE_DATATYPE)), .size_uspace = IPT_ALIGN(sizeof(MODULE_DATATYPE)), .setup = setup, .parse_field = parse_field, .get_fields = get_fields, }; ModuleDef *init(void) { return(&_module); } /* vim: ts=4 */