/* * void pmap_teardown(void); * * Called by skplugin to tear down this plugin. */ static void pmap_teardown( void) { size_t i; pmap_data_t *pmap_data; if (pmap_vector) { for (i = 0; i < skVectorGetCount(pmap_vector); ++i) { ASSERT_RESULT(skVectorGetValue(&pmap_data, pmap_vector, i), int, 0); pmap_data_destroy(pmap_data); } skVectorDestroy(pmap_vector); pmap_vector = NULL; } }
/* * status = readerSetup(&out_daemon_mode, probe_vector, options); * * Invoked by input_mode_type->setup_fn(); */ static int readerSetup( fp_daemon_mode_t *is_daemon, const sk_vector_t *probe_vec, reader_options_t *options) { size_t count = skVectorGetCount(probe_vec); const char *netflow_file = options->pdu_file.netflow_file; skpc_probe_t *p; /* this function should only be called if we actually have probes * to process */ if (count == 0) { skAppPrintErr("readerSetup() called with zero length probe vector"); return 1; } if (count > 1) { skAppPrintErr("The " INPUT_MODE_TYPE_NAME "only supports one file-based probe."); return -1; } if (NULL != netflow_file) { /* Modify the probe to have the file name given on the command * line. */ if (0 == skVectorGetValue(&p, probe_vec, 0)) { if (skpcProbeSetFileSource(p, netflow_file)) { skAppPrintErr("Cannot change file source of probe"); return 1; } } } /* Not a deamon */ *is_daemon = FP_DAEMON_OFF; return 0; }
/* * Verify sensor by its class. Verify that the sensor supports the * type(s) of its probe(s). Verify that enough information is * present on the sensor to categorize a flow record. * * Invoked from rwflowpack by packlogic->verify_sensor_fn */ static int packLogicVerifySensor( skpc_sensor_t *sensor) { skpc_probe_t *probe; sk_vector_t *probe_vec; uint32_t count; /* There is a single class, so no per-class verification is * necessary. Make certain we have either snmp interface values * or ip-blocks depending on the type of probe(s) associated with * this sensor. */ /* get the probes for the sensor */ probe_vec = skVectorNew(sizeof(skpc_probe_t*)); if (probe_vec == NULL) { return -1; } count = skpcSensorGetProbes(sensor, probe_vec); /* this packing logic only supports a single probe per sensor */ if (count != 1) { skAppPrintErr(("Cannot verify sensor '%s':\n" "\tOnly one probe per sensor is supported" " by the packing-logic\n\tfile '%s'"), sensor->sensor_name, plugin_path); skVectorDestroy(probe_vec); return -1; } skVectorGetValue(&probe, probe_vec, 0); skVectorDestroy(probe_vec); /* make certain the probe's type is valid */ switch (probe->probe_type) { case PROBE_ENUM_NETFLOW_V5: case PROBE_ENUM_NETFLOW_V9: case PROBE_ENUM_IPFIX: /* expected probe types */ break; default: assert(skpcProbetypeEnumtoName(probe->probe_type)); skAppPrintErr(("Cannot verify sensor '%s':\n" "\tThe probe type '%s' is not supported in the" " packing-logic\n\tfile '%s'"), sensor->sensor_name, skpcProbetypeEnumtoName(probe->probe_type), plugin_path); return -1; } /* Verify that we have enough information to determine the * flowtype for every flow. These are the rules: * * 1. One of external-interface, external-ipblock, or * external-ipset must be specified. * * 2. You cannot mix interfaces, ipblocks, and ipsets, with the * excption that a null-interface which is always allowed. * * 3. Only one network may claim the remainder. * * 4. Using remainder for an ipblock or ipset requires that * another interface has set an IPblock or an IPset. */ switch (sensor->decider[NETWORK_EXTERNAL].nd_type) { case SKPC_UNSET: /* It is an error when neither SNMP interfaces nor IP-blocks * were specified for the external network. */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tMust specify %s-interface, %s-ipblock, or %s-ipset"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_EXTERNAL], net_names[NETWORK_EXTERNAL]); return -1; case SKPC_NEG_IPBLOCK: skAppPrintErr("Negated IPblock logic not implemented"); exit(EXIT_FAILURE); case SKPC_NEG_IPSET: skAppPrintErr("Negated IPset logic not implemented"); exit(EXIT_FAILURE); case SKPC_IPBLOCK: /* Fine as long as INTERNAL is either empty or also contains * IPblocks */ switch (sensor->decider[NETWORK_INTERNAL].nd_type) { case SKPC_UNSET: case SKPC_IPBLOCK: case SKPC_REMAIN_IPBLOCK: /* These are fine */ break; case SKPC_NEG_IPBLOCK: skAppPrintErr("Negated IPblock logic not implemented"); exit(EXIT_FAILURE); case SKPC_NEG_IPSET: skAppPrintErr("Negated IPset logic not implemented"); exit(EXIT_FAILURE); case SKPC_INTERFACE: case SKPC_REMAIN_INTERFACE: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-ipblock and %s-interface"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_INTERNAL]); return -1; case SKPC_IPSET: case SKPC_REMAIN_IPSET: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-ipblock and %s-ipset"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_INTERNAL]); return -1; } break; case SKPC_REMAIN_IPBLOCK: switch (sensor->decider[NETWORK_INTERNAL].nd_type) { case SKPC_UNSET: /* Accept for now, though this will be an error if * NETWORK_NULL does not define an IPblock */ break; case SKPC_NEG_IPBLOCK: skAppPrintErr("Negated IPblock logic not implemented"); exit(EXIT_FAILURE); case SKPC_NEG_IPSET: skAppPrintErr("Negated IPset logic not implemented"); exit(EXIT_FAILURE); case SKPC_REMAIN_IPBLOCK: /* Cannot have multiple things requesting "remainder" */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tOnly one network value may use 'remainder'"), sensor->sensor_name); return -1; case SKPC_IPBLOCK: /* This is fine */ break; case SKPC_INTERFACE: case SKPC_REMAIN_INTERFACE: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-ipblock and %s-interface"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_INTERNAL]); return -1; case SKPC_IPSET: case SKPC_REMAIN_IPSET: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-ipblock and %s-ipset"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_INTERNAL]); return -1; } break; case SKPC_INTERFACE: case SKPC_REMAIN_INTERFACE: /* Fine as long as INTERNAL and NULL are either empty or also * contain interfaces */ switch (sensor->decider[NETWORK_INTERNAL].nd_type) { case SKPC_UNSET: case SKPC_INTERFACE: case SKPC_REMAIN_INTERFACE: switch (sensor->decider[NETWORK_NULL].nd_type) { case SKPC_IPBLOCK: case SKPC_NEG_IPBLOCK: case SKPC_REMAIN_IPBLOCK: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-interface and %s-ipblock"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_NULL]); return -1; case SKPC_IPSET: case SKPC_NEG_IPSET: case SKPC_REMAIN_IPSET: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-interface and %s-ipset"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_NULL]); return -1; default: break; } break; case SKPC_IPBLOCK: case SKPC_NEG_IPBLOCK: case SKPC_REMAIN_IPBLOCK: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-interface and %s-ipblock"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_INTERNAL]); return -1; case SKPC_IPSET: case SKPC_NEG_IPSET: case SKPC_REMAIN_IPSET: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-interface and %s-ipset"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_INTERNAL]); return -1; } break; case SKPC_IPSET: /* Fine as long as INTERNAL is either empty or also contains * IPsets */ switch (sensor->decider[NETWORK_INTERNAL].nd_type) { case SKPC_UNSET: case SKPC_IPSET: case SKPC_REMAIN_IPSET: /* These are fine */ break; case SKPC_NEG_IPSET: skAppPrintErr("Negated IPset logic not implemented"); exit(EXIT_FAILURE); case SKPC_NEG_IPBLOCK: skAppPrintErr("Negated IPblock logic not implemented"); exit(EXIT_FAILURE); case SKPC_INTERFACE: case SKPC_REMAIN_INTERFACE: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-ipset and %s-interface"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_INTERNAL]); return -1; case SKPC_IPBLOCK: case SKPC_REMAIN_IPBLOCK: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-ipset and %s-ipblock"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_INTERNAL]); return -1; } break; case SKPC_REMAIN_IPSET: switch (sensor->decider[NETWORK_INTERNAL].nd_type) { case SKPC_UNSET: /* Accept for now, though this will be an error if * NETWORK_NULL does not define an IPset */ break; case SKPC_NEG_IPSET: skAppPrintErr("Negated IPset logic not implemented"); exit(EXIT_FAILURE); case SKPC_NEG_IPBLOCK: skAppPrintErr("Negated IPblock logic not implemented"); exit(EXIT_FAILURE); case SKPC_REMAIN_IPSET: /* Cannot have multiple things requesting "remainder" */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tOnly one network value may use 'remainder'"), sensor->sensor_name); return -1; case SKPC_IPSET: /* This is fine */ break; case SKPC_INTERFACE: case SKPC_REMAIN_INTERFACE: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-ipset and %s-interface"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_INTERNAL]); return -1; case SKPC_IPBLOCK: case SKPC_REMAIN_IPBLOCK: /* Bad mix */ skAppPrintErr(("Cannot verify sensor '%s':\n" "\tCannot mix %s-ipset and %s-ipblock"), sensor->sensor_name, net_names[NETWORK_EXTERNAL], net_names[NETWORK_INTERNAL]); return -1; } break; } return 0; }
/* * skplugin_err_t pmapfile_handler(const char *opt_arg, void *cbdata) * * Handler for --pmap-file option. Actually registers the filter and * fields. */ static skplugin_err_t pmapfile_handler( const char *opt_arg, void UNUSED(*cbdata)) { /* Whether we have seen any unnamed pmaps */ static int have_unnamed_pmap = 0; skPrefixMapErr_t map_error = SKPREFIXMAP_OK; skstream_t *stream = NULL; skPrefixMap_t *prefix_map = NULL; pmap_data_t *pmap_data = NULL; int ok; const char *filename; const char *sep; const char *mapname; char *prefixed_name = NULL; char *short_prefixed_name; size_t namelen = 0; size_t i; int rv = SKPLUGIN_ERR; skplugin_callbacks_t regdata; /* We can only have one pmap whenever we any any pmap without a * mapname. If we've seen one and enter this function a second * time, it is an error */ if (have_unnamed_pmap) { skAppPrintErr(("Invalid %s: You may use only one prefix map" " when you are\n" "\tusing a prefix map without specifying a mapname"), pmap_file_option); return SKPLUGIN_ERR; } /* Parse the argument into a field name and file name */ sep = strchr(opt_arg, ':'); if (NULL == sep) { /* We do not have a mapname. We'll check for one in the pmap * once we read it. */ mapname = NULL; filename = opt_arg; } else { /* A mapname was supplied on the command line */ if (sep == opt_arg) { skAppPrintErr("Invalid %s: Zero length mapnames are not allowed", pmap_file_option); return SKPLUGIN_ERR; } if (memchr(opt_arg, ',', sep - opt_arg) != NULL) { skAppPrintErr("Invalid %s: The mapname may not include a comma", pmap_file_option); return SKPLUGIN_ERR; } mapname = opt_arg; filename = sep + 1; namelen = sep - opt_arg; } ok = skpinOpenDataInputStream(&stream, SK_CONTENT_SILK, filename); if (ok == -1) { /* problem opening file */ skAppPrintErr("Failed to open the prefix map file '%s'", filename); return SKPLUGIN_ERR; } if (ok == 1) { /* master needs to process the file, since it may contain the * map name we use for creating switches */ if ((rv = skStreamCreate(&stream, SK_IO_READ, SK_CONTENT_SILK)) || (rv = skStreamBind(stream, filename)) || (rv = skStreamOpen(stream))) { skStreamPrintLastErr(stream, rv, &skAppPrintErr); skStreamDestroy(&stream); return SKPLUGIN_ERR; } /* the master can ignore the file for filtering */ ignore_prefix_map = 1; } map_error = skPrefixMapRead(&prefix_map, stream); if (SKPREFIXMAP_OK != map_error) { if (SKPREFIXMAP_ERR_IO == map_error) { skStreamPrintLastErr(stream, skStreamGetLastReturnValue(stream), &skAppPrintErr); } else { skAppPrintErr("Failed to read the prefix map file '%s': %s", opt_arg, skPrefixMapStrerror(map_error)); } skStreamDestroy(&stream); return SKPLUGIN_ERR; } skStreamDestroy(&stream); if (NULL == mapname) { /* No mapname was supplied on the command line. Check for a * mapname insided the pmap. */ mapname = skPrefixMapGetMapName(prefix_map); if (mapname) { /* The pmap supplied a mapname */ namelen = strlen(mapname); } else { /* No mapname. Accept for legacy purposes, unless we have * read any other pmaps */ have_unnamed_pmap = 1; if (skVectorGetCount(pmap_vector) != 0) { skAppPrintErr(("Invalid %s: You may use only one prefix map" " when you are\n" "\t using a prefix map without" " specifying a mapname"), pmap_file_option); goto END; } } } /* Allocate the pmap_data structure */ pmap_data = (pmap_data_t *)calloc(1, sizeof(*pmap_data)); if (pmap_data == NULL) { ERR_NO_MEM(pmap_data); rv = SKPLUGIN_ERR_FATAL; goto END; } /* pmap_data now "owns" prefix_map */ pmap_data->pmap = prefix_map; prefix_map = NULL; /* Cache the content type */ pmap_data->type = skPrefixMapGetContentType(pmap_data->pmap); /* Fill the direction structure for each direction */ pmap_data->sdir.dir = DIR_SOURCE; pmap_data->ddir.dir = DIR_DEST; pmap_data->adir.dir = DIR_ANY; pmap_data->sdir.data = pmap_data; pmap_data->ddir.data = pmap_data; pmap_data->adir.data = pmap_data; /* Record the path to the pmap file */ pmap_data->filepath = strdup(filename); if (NULL == pmap_data->filepath) { ERR_NO_MEM(pmap_data->filepath); rv = SKPLUGIN_ERR_FATAL; goto END; } if (mapname == NULL) { /* Pmap without a mapname. */ /* Add the proper legacy option names to the pmap_data structure */ switch (pmap_data->type) { case SKPREFIXMAP_CONT_ADDR_V4: case SKPREFIXMAP_CONT_ADDR_V6: pmap_data->sdir.filter_option = strdup(pmap_saddress_option); pmap_data->ddir.filter_option = strdup(pmap_daddress_option); pmap_data->adir.filter_option = strdup(pmap_aaddress_option); break; case SKPREFIXMAP_CONT_PROTO_PORT: pmap_data->sdir.filter_option = strdup(pmap_sport_proto_option); pmap_data->ddir.filter_option = strdup(pmap_dport_proto_option); pmap_data->adir.filter_option = strdup(pmap_aport_proto_option); break; } if ((pmap_data->sdir.filter_option == NULL) || (pmap_data->ddir.filter_option == NULL) || (pmap_data->adir.filter_option == NULL)) { ERR_NO_MEM(filter_option); rv = SKPLUGIN_ERR_FATAL; goto END; } pmap_data->mapname = strdup(pmap_title_val); pmap_data->sdir.field_name = strdup(pmap_title_sval); pmap_data->ddir.field_name = strdup(pmap_title_dval); if ((pmap_data->mapname == NULL) || (pmap_data->sdir.field_name == NULL) || (pmap_data->ddir.field_name == NULL)) { ERR_NO_MEM(field_name); rv = SKPLUGIN_ERR_FATAL; goto END; } } else { /* if (mapname == NULL) */ /* Create the field names*/ pmap_data->mapname = (char*)malloc(namelen + 1); if (NULL == pmap_data->mapname) { ERR_NO_MEM(pmap_data->mapname); rv = SKPLUGIN_ERR_FATAL; goto END; } strncpy(pmap_data->mapname, mapname, namelen); pmap_data->mapname[namelen] = '\0'; /* Allocate space for the [pmap-]{src-,dst-}<mapname> string */ prefixed_name = (char*)malloc(namelen + pmap_prefix_len + dir_name_len + 1); if (NULL == prefixed_name) { ERR_NO_MEM(prefixed_name); rv = SKPLUGIN_ERR_FATAL; goto END; } /* Copy in the pmap- prefix */ strncpy(prefixed_name, pmap_prefix, pmap_prefix_len); /* short name (for fields) starts at the {src-,dst-} */ short_prefixed_name = prefixed_name + pmap_prefix_len; /* add in the actual field name, and zero terminate it */ strncpy(short_prefixed_name + dir_name_len, mapname, namelen); short_prefixed_name[namelen + dir_name_len] = '\0'; /* Create the destination-themed names */ strncpy(short_prefixed_name, src_dir_name, dir_name_len); pmap_data->sdir.filter_option = strdup(prefixed_name); pmap_data->sdir.field_name = strdup(short_prefixed_name); if ((pmap_data->sdir.filter_option == NULL) || (pmap_data->sdir.field_name == NULL)) { ERR_NO_MEM(pmap_data->sdir); rv = SKPLUGIN_ERR_FATAL; goto END; } strncpy(short_prefixed_name, dst_dir_name, dir_name_len); pmap_data->ddir.filter_option = strdup(prefixed_name); pmap_data->ddir.field_name = strdup(short_prefixed_name); if ((pmap_data->ddir.filter_option == NULL) || (pmap_data->ddir.field_name == NULL)) { ERR_NO_MEM(pmap_data->ddir); rv = SKPLUGIN_ERR_FATAL; goto END; } strncpy(short_prefixed_name, any_dir_name, dir_name_len); pmap_data->adir.filter_option = strdup(prefixed_name); if (pmap_data->adir.filter_option == NULL) { ERR_NO_MEM(pmap_data->adir); rv = SKPLUGIN_ERR_FATAL; goto END; } /* Free the temporary name buffer */ free(prefixed_name); prefixed_name = NULL; } /* if (mapname == NULL) */ /* Verify unique field names */ for (i = 0; i < skVectorGetCount(pmap_vector); i++) { pmap_data_t *p; skVectorGetValue(&p, pmap_vector, i); if ((strcmp(pmap_data->mapname, p->mapname) == 0) || (strcmp(pmap_data->sdir.field_name, p->sdir.field_name) == 0) || (strcmp(pmap_data->ddir.field_name, p->ddir.field_name) == 0)) { skAppPrintErr(("Invalid %s: Multiple pmaps use the mapname '%s':\n" "\t%s\n\t%s"), pmap_file_option, pmap_data->mapname, p->filepath, pmap_data->filepath); rv = SKPLUGIN_ERR; goto END; } } /* Register fields and filter options */ memset(®data, 0, sizeof(regdata)); regdata.init = pmap_field_init; regdata.column_width = 0; regdata.bin_bytes = 4; regdata.rec_to_text = pmap_text_fn; regdata.rec_to_bin = pmap_bin_fn; regdata.bin_to_text = pmap_bin_to_text_fn; for (i = 0; i < 2; ++i) { directed_pmap_data_t *dir = ((0 == i) ? &pmap_data->sdir : &pmap_data->ddir); skpinRegField(&dir->field, dir->field_name, NULL, ®data, dir); skpinRegOption2(dir->filter_option, REQUIRED_ARG, NULL, &pmap_filter_help, &pmap_handle_filter_option, dir, 1, SKPLUGIN_FN_FILTER); } /* Register the "any" filter separately */ skpinRegOption2(pmap_data->adir.filter_option, REQUIRED_ARG, NULL, &pmap_filter_help, &pmap_handle_filter_option, &pmap_data->adir, 1, SKPLUGIN_FN_FILTER); if (skVectorAppendValue(pmap_vector, &pmap_data)) { rv = SKPLUGIN_ERR_FATAL; goto END; } rv = SKPLUGIN_OK; END: /* Free the temporary name buffer */ if (prefixed_name) { free(prefixed_name); } if (rv != SKPLUGIN_OK) { if (prefix_map) { skPrefixMapDelete(prefix_map); } if (pmap_data) { pmap_data_destroy(pmap_data); } } return (skplugin_err_t)rv; }
static skplugin_err_t parseFlowtypes( const char *opt_arg, void *v_bitmap) { static int registered_fields = 0; sk_bitmap_t **ft_bitmap; sksite_error_iterator_t *err_iter = NULL; sk_vector_t *ft_vec = NULL; sk_flowtype_id_t ft; int i = 0; skplugin_err_t err = SKPLUGIN_OK; int rv; assert(v_bitmap); ft_bitmap = (sk_bitmap_t**)v_bitmap; if (NULL != *ft_bitmap) { /* this is an attempt to re-create a list of flowtypes, which * could happen if the first pass was generated by parsing an * environment variable. clear the bitmap */ skBitmapClearAllBits(*ft_bitmap); } else if (skBitmapCreate(ft_bitmap, SK_MAX_NUM_FLOWTYPES)) { skAppPrintErr("Unable to create bitmap"); err = SKPLUGIN_ERR; goto END; } ft_vec = skVectorNew(sizeof(sk_flowtype_id_t)); if (NULL == ft_vec) { skAppPrintErr("Unable to create vector"); err = SKPLUGIN_ERR; goto END; } rv = sksiteParseFlowtypeList(ft_vec, opt_arg, NULL, NULL, NULL, NULL, &err_iter); if (rv) { if (rv < 0) { skAppPrintErr("Memory or internal error while parsing flowtypes"); } else if (1 == rv) { sksiteErrorIteratorNext(err_iter); skAppPrintErr("Invalid flowtypes '%s': %s", opt_arg, sksiteErrorIteratorGetMessage(err_iter)); assert(sksiteErrorIteratorNext(err_iter) == SK_ITERATOR_NO_MORE_ENTRIES); } else { skAppPrintErr("Invalid flowtypes '%s': Found multiple errors:", opt_arg); while (sksiteErrorIteratorNext(err_iter) == SK_ITERATOR_OK) { skAppPrintErr("%s", sksiteErrorIteratorGetMessage(err_iter)); } } err = SKPLUGIN_ERR; goto END; } if (0 == skVectorGetCount(ft_vec)) { skAppPrintErr("Invalid flowtypes '%s': No valid flowtypes found", opt_arg); err = SKPLUGIN_ERR; goto END; } for (i = 0; 0 == skVectorGetValue(&ft, ft_vec, i); ++i) { skBitmapSetBit(*ft_bitmap, ft); } if (incoming_flowtypes && outgoing_flowtypes && !registered_fields) { registered_fields = 1; err = skpinRegIPAddressField("int-ip", &internalIp, 0); if (SKPLUGIN_OK != err) { goto END; } err = skpinRegIPAddressField("ext-ip", &externalIp, 0); if (SKPLUGIN_OK != err) { goto END; } err = skpinRegIntField("int-port", 0, UINT16_MAX, &internalPort, 0); if (SKPLUGIN_OK != err) { goto END; } err = skpinRegIntField("ext-port", 0, UINT16_MAX, &externalPort, 0); if (SKPLUGIN_OK != err) { goto END; } } END: skVectorDestroy(ft_vec); sksiteErrorIteratorFree(err_iter); if (*ft_bitmap && err != SKPLUGIN_OK) { skBitmapDestroy(ft_bitmap); *ft_bitmap = NULL; } return err; }