From e7d75bd2751b9bf5a8a4fc15f67f94ae8eb539eb Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 8 Sep 2017 10:06:34 -0400 Subject: [PATCH] TT#19350 add option to manage iptables rules directly Change-Id: I8e2b47bf34206ed5cc369649bf8ef875ac271169 --- README.md | 29 +++ daemon/.ycm_extra_conf.py | 1 + daemon/Makefile | 14 +- daemon/call.c | 2 +- daemon/iptables.c | 378 ++++++++++++++++++++++++++++++++++++++ daemon/iptables.h | 17 ++ daemon/main.c | 5 + daemon/media_socket.c | 33 ++-- daemon/media_socket.h | 4 +- daemon/redis.c | 2 +- debian/control | 1 + lib/str.h | 11 ++ 12 files changed, 474 insertions(+), 23 deletions(-) create mode 100644 daemon/iptables.c create mode 100644 daemon/iptables.h diff --git a/README.md b/README.md index 0e368e47e..9f2df2621 100644 --- a/README.md +++ b/README.md @@ -115,10 +115,15 @@ There's 3 parts to *rtpengine*, which can be found in the respective subdirector - *PCRE* library - *XMLRPC-C* version 1.16.08 or higher - *hiredis* library + - *libiptc* library for iptables management (optional) The `Makefile` contains a few Debian-specific flags, which may have to removed for compilation to be successful. This will not affect operation in any way. + If you do not wish to (or cannot) compile the optional iptables management feature, the + `Makefile` also contains a switch to disable it. See the `--iptables-chain` option for + a description. + * `iptables-extension` Required for in-kernel packet forwarding. @@ -196,6 +201,7 @@ option and which are reproduced below: --recording-dir=FILE Spool directory where PCAP call recording data goes --recording-method=pcap|proc Strategy for call recording --recording-format=raw|eth PCAP file format for recorded calls. + --iptables-chain=STRING Add explicit firewall rules to this iptables chain Most of these options are indeed optional, with two exceptions. It's mandatory to specify at least one local IP address through `--interface`, and at least one of the `--listen-...` options must be given. @@ -550,6 +556,29 @@ The options are described in more detail below. When recording to pcap file in raw (default) format, there is no ethernet header. When set to eth, a fake ethernet header is added, making each package 14 bytes larger. +* --iptables-chain + + This option enables explicit management of an iptables chain. When enabled, *rtpengine* + takes control of the given iptables chain, which must exist already prior to starting + the daemon. Upon startup, *rtpengine* will flush the chain, and then add one `ACCEPT` + rule for each media port (RTP/RTCP) opened. Each rule will exactly match the individual + port and destination IP address, and will be created with the call ID as iptables comment. + The rule will be deleted when the port is closed. + + This option allows creating a firewall with a default `DROP` policy for the entire port + range used by *rtpengine* and then referencing the given iptables chain to only + selectively allow the ports actually in use. + + Note that this applies only to media ports, and does not apply to any other ports (such + as the control ports) used by *rtpengine*. + + Also note that the iptables API is not the most efficient one around and does not lend + itself to fast dynamic creation and deletion of rules. If you have a high call volume, + and especially many call attempts per second, you might experience significant + performance impact. This is not a shortcoming of *rtpengine* but rather of iptables + and its API implementation in the Linux kernel. In such a case, it is recommended to + add a static iptables rule for the entire media port range instead, and not use this option. + A typical command line (enabling both UDP and NG protocols) thus may look like: /usr/sbin/rtpengine --table=0 --interface=10.64.73.31 --interface=2001:db8::4f3:3d \ diff --git a/daemon/.ycm_extra_conf.py b/daemon/.ycm_extra_conf.py index 06920119c..cf64ff7da 100644 --- a/daemon/.ycm_extra_conf.py +++ b/daemon/.ycm_extra_conf.py @@ -25,6 +25,7 @@ flags = [ '-D__YCM=1', '-DRTPENGINE_VERSION="dummy"', '-DRE_PLUGIN_DIR="/usr/lib/rtpengine"', + '-DWITH_IPTABLES_OPTION', '-O2', '-fstack-protector', '--param=ssp-buffer-size=4', diff --git a/daemon/Makefile b/daemon/Makefile index dd09e07b0..a93bb134a 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -1,5 +1,7 @@ TARGET= rtpengine +with_iptables_option ?= yes + CFLAGS= -g -Wall -pthread -fno-strict-aliasing CFLAGS+= -std=c99 CFLAGS+= `pkg-config --cflags glib-2.0` @@ -12,6 +14,9 @@ CFLAGS+= `pkg-config xmlrpc_client --cflags 2> /dev/null || xmlrpc-c-config clie CFLAGS+= `pkg-config xmlrpc --cflags 2> /dev/null` CFLAGS+= `pkg-config xmlrpc_util --cflags 2> /dev/null` CFLAGS+= `pkg-config --cflags json-glib-1.0` +ifeq ($(with_iptables_option),yes) +CFLAGS+= `pkg-config --cflags libiptc` +endif CFLAGS+= -I. -I../kernel-module/ -I../lib/ CFLAGS+= -D_GNU_SOURCE @@ -22,6 +27,10 @@ CFLAGS+= -DRE_PLUGIN_DIR="\"/usr/lib/rtpengine\"" #CFLAGS+= -DTERMINATE_SDP_AT_BLANK_LINE #CFLAGS+= -DSTRICT_SDES_KEY_LIFETIME +ifeq ($(with_iptables_option),yes) +CFLAGS+= -DWITH_IPTABLES_OPTION +endif + LDFLAGS= -lm LDFLAGS+= `pkg-config --libs glib-2.0` LDFLAGS+= `pkg-config --libs gthread-2.0` @@ -37,13 +46,16 @@ LDFLAGS+= `pkg-config xmlrpc --libs 2> /dev/null` LDFLAGS+= `pkg-config xmlrpc_util --libs 2> /dev/null` LDFLAGS+= -lhiredis LDFLAGS+= `pkg-config --libs json-glib-1.0` +ifeq ($(with_iptables_option),yes) +LDFLAGS+= `pkg-config --libs libiptc` +endif include ../lib/lib.Makefile SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \ bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c \ crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c socket.c \ - media_socket.c homer.c recording.c statistics.c cdr.c ssrc.c + media_socket.c homer.c recording.c statistics.c cdr.c ssrc.c iptables.c LIBSRCS= loglib.c auxlib.c rtplib.c OBJS= $(SRCS:.c=.o) $(LIBSRCS:.c=.o) diff --git a/daemon/call.c b/daemon/call.c index ee266913a..622458520 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -742,7 +742,7 @@ static struct endpoint_map *__get_endpoint_map(struct call_media *media, unsigne alloc: if (num_ports > 16) return NULL; - if (get_consecutive_ports(&intf_sockets, num_ports, media->logical_intf)) + if (get_consecutive_ports(&intf_sockets, num_ports, media->logical_intf, &media->call->callid)) return NULL; __C_DBG("allocating stream_fds for %u ports", num_ports); diff --git a/daemon/iptables.c b/daemon/iptables.c new file mode 100644 index 000000000..84981113f --- /dev/null +++ b/daemon/iptables.c @@ -0,0 +1,378 @@ +#include "iptables.h" + +char *g_iptables_chain; +int (*iptables_add_rule)(const socket_t *local_sock, const str *comment); +int (*iptables_del_rule)(const socket_t *local_sock); + +#ifdef WITH_IPTABLES_OPTION + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aux.h" +#include "log.h" +#include "socket.h" +#include "str.h" + +#undef __ALIGN_KERNEL +#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof(x))(a) - 1) +#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) +#undef XT_ALIGN +#define XT_ALIGN(s) __ALIGN_KERNEL((s), __alignof__(struct _xt_align)) + +#ifndef XT_LOCK_NAME +#define XT_LOCK_NAME "/run/xtables.lock" +#endif + + +struct ipt_matches { + struct xt_entry_match udp_match; + struct xt_udp udp; + struct xt_entry_match comment_match; + struct xt_comment_info comment; + struct xt_standard_target target; +}; +struct ipv4_ipt_entry { + struct ipt_entry entry; + struct ipt_matches matches; +}; +struct ipv6_ipt_entry { + struct ip6t_entry entry; + struct ipt_matches matches; +}; + + +static mutex_t __xt_lock; +static int __xt_lock_fd = -1; + + + +static void xt_lock(void) { + mutex_lock(&__xt_lock); + + __xt_lock_fd = open(XT_LOCK_NAME, O_CREAT, 0600); + if (__xt_lock_fd == -1) { + ilog(LOG_WARN, "Could not open xtables lock file '%s': %s", XT_LOCK_NAME, strerror(errno)); + // as per iptables source code, continue anyway + return; + } + + if (flock(__xt_lock_fd, LOCK_EX)) { + ilog(LOG_WARN, "Failed to acquire lock file '%s': %s", XT_LOCK_NAME, strerror(errno)); + close(__xt_lock_fd); + __xt_lock_fd = -1; + } +} + +static void xt_unlock(void) { + if (__xt_lock_fd != -1) + close(__xt_lock_fd); + __xt_lock_fd = -1; + mutex_unlock(&__xt_lock); +} + +static void ip46tables_fill_matches(struct ipt_matches *matches, const socket_t *local_sock, + const str *comment) +{ + matches->target.target.u.user.target_size = XT_ALIGN(sizeof(struct xt_standard_target)); + strcpy(matches->target.target.u.user.name, "ACCEPT"); + + strcpy(matches->udp_match.u.user.name, "udp"); + matches->udp_match.u.match_size = XT_ALIGN(sizeof(struct xt_entry_match)) + XT_ALIGN(sizeof(struct xt_udp)); + matches->udp.dpts[0] = matches->udp.dpts[1] = local_sock->local.port; + matches->udp.spts[0] = 0; + matches->udp.spts[1] = 0xffff; + + matches->comment_match.u.match_size = XT_ALIGN(sizeof(struct xt_entry_match)) + + XT_ALIGN(sizeof(struct xt_comment_info)); + strcpy(matches->comment_match.u.user.name, "comment"); + if (comment) + str_ncpy(matches->comment.comment, sizeof(matches->comment.comment), comment); +} + +static void ip4_fill_entry(struct ipv4_ipt_entry *entry, const socket_t *local_sock, const str *comment) { + ZERO(*entry); + entry->entry.ip.proto = IPPROTO_UDP; + entry->entry.ip.dst = local_sock->local.address.u.ipv4; + memset(&entry->entry.ip.dmsk, 0xff, sizeof(entry->entry.ip.dmsk)); + entry->entry.target_offset = G_STRUCT_OFFSET(struct ipv4_ipt_entry, matches.target); + + ip46tables_fill_matches(&entry->matches, local_sock, comment); + + entry->entry.next_offset = entry->entry.target_offset + entry->matches.target.target.u.user.target_size; +} +static void ip6_fill_entry(struct ipv6_ipt_entry *entry, const socket_t *local_sock, const str *comment) { + ZERO(*entry); + entry->entry.ipv6.proto = IPPROTO_UDP; + entry->entry.ipv6.dst = local_sock->local.address.u.ipv6; + entry->entry.ipv6.flags |= IP6T_F_PROTO; + memset(&entry->entry.ipv6.dmsk, 0xff, sizeof(entry->entry.ipv6.dmsk)); + entry->entry.target_offset = G_STRUCT_OFFSET(struct ipv6_ipt_entry, matches.target); + + ip46tables_fill_matches(&entry->matches, local_sock, comment); + + entry->entry.next_offset = entry->entry.target_offset + entry->matches.target.target.u.user.target_size; +} + +static const char *ip4tables_add_rule(const socket_t *local_sock, const str *comment) { + struct xtc_handle *h; + struct ipv4_ipt_entry entry; + const char *err; + + xt_lock(); + + err = "could not initialize iptables"; + h = iptc_init("filter"); + if (!h) + goto err2; + + ip4_fill_entry(&entry, local_sock, comment); + + err = "failed to append iptables entry"; + if (!iptc_append_entry(g_iptables_chain, &entry.entry, h)) + goto err; + err = "failed to commit iptables changes"; + if (!iptc_commit(h)) + goto err; + + err = NULL; + +err: + iptc_free(h); +err2: + xt_unlock(); + return err; +} + +static const char *ip6tables_add_rule(const socket_t *local_sock, const str *comment) { + struct xtc_handle *h; + struct ipv6_ipt_entry entry; + const char *err; + + xt_lock(); + + err = "could not initialize ip6tables"; + h = ip6tc_init("filter"); + if (!h) + goto err2; + + ip6_fill_entry(&entry, local_sock, comment); + + err = "failed to append ip6tables entry"; + if (!ip6tc_append_entry(g_iptables_chain, &entry.entry, h)) + goto err; + err = "failed to commit ip6tables changes"; + if (!ip6tc_commit(h)) + goto err; + + err = NULL; + +err: + ip6tc_free(h); +err2: + xt_unlock(); + return err; +} + +static const char *ip4tables_del_rule(const socket_t *local_sock) { + struct xtc_handle *h; + struct ipv4_ipt_entry entry, mask; + const char *err; + + xt_lock(); + + err = "could not initialize iptables"; + h = iptc_init("filter"); + if (!h) + goto err2; + + ip4_fill_entry(&entry, local_sock, NULL); + + // match everything except the comment + memset(&mask, 0, sizeof(mask)); + memset(&mask.entry, 0xff, sizeof(mask.entry)); + memset(&mask.matches.udp_match, 0xff, sizeof(mask.matches.udp_match)); + memset(&mask.matches.udp, 0xff, sizeof(mask.matches.udp)); + memset(&mask.matches.comment_match, 0xff, sizeof(mask.matches.comment_match)); + memset(&mask.matches.target, 0xff, sizeof(mask.matches.target)); + + err = "failed to delete iptables entry"; + if (!iptc_delete_entry(g_iptables_chain, &entry.entry, (unsigned char *) &mask, h)) + goto err; + err = "failed to commit iptables changes"; + if (!iptc_commit(h)) + goto err; + + err = NULL; + +err: + iptc_free(h); +err2: + xt_unlock(); + return err; +} + +static const char *ip6tables_del_rule(const socket_t *local_sock) { + struct xtc_handle *h; + struct ipv6_ipt_entry entry, mask; + const char *err; + + xt_lock(); + + err = "could not initialize ip6tables"; + h = ip6tc_init("filter"); + if (!h) + goto err2; + + ip6_fill_entry(&entry, local_sock, NULL); + + // match everything except the comment + memset(&mask, 0, sizeof(mask)); + memset(&mask.entry, 0xff, sizeof(mask.entry)); + memset(&mask.matches.udp_match, 0xff, sizeof(mask.matches.udp_match)); + memset(&mask.matches.udp, 0xff, sizeof(mask.matches.udp)); + memset(&mask.matches.comment_match, 0xff, sizeof(mask.matches.comment_match)); + memset(&mask.matches.target, 0xff, sizeof(mask.matches.target)); + + err = "failed to delete ip6tables entry"; + if (!ip6tc_delete_entry(g_iptables_chain, &entry.entry, (unsigned char *) &mask, h)) + goto err; + err = "failed to commit ip6tables changes"; + if (!ip6tc_commit(h)) + goto err; + + err = NULL; + +err: + ip6tc_free(h); +err2: + xt_unlock(); + return err; +} + +static int __iptables_add_rule(const socket_t *local_sock, const str *comment) { + const char *err; + + switch (local_sock->family->af) { + case AF_INET: + err = ip4tables_add_rule(local_sock, comment); + break; + case AF_INET6: + err = ip6tables_add_rule(local_sock, comment); + break; + default: + err = "unsupported socket family"; + break; + }; + + if (err) + ilog(LOG_ERROR, "Error adding iptables rule (for '" STR_FORMAT "'): %s (%s)", + STR_FMT(comment), err, strerror(errno)); + + return 0; +} + + +static int __iptables_del_rule(const socket_t *local_sock) { + const char *err; + + switch (local_sock->family->af) { + case AF_INET: + err = ip4tables_del_rule(local_sock); + break; + case AF_INET6: + err = ip6tables_del_rule(local_sock); + break; + default: + err = "unsupported socket family"; + break; + }; + + if (err) + ilog(LOG_ERROR, "Error deleting iptables rule: %s (%s)", + err, strerror(errno)); + + return 0; +} + +#endif // WITH_IPTABLES_OPTION + +static int __iptables_stub(void) { + return 0; +} + + +void iptables_init(void) { + if (g_iptables_chain && !g_iptables_chain[0]) + g_iptables_chain = NULL; + + if (!g_iptables_chain) { + iptables_add_rule = (void *) __iptables_stub; + iptables_del_rule = (void *) __iptables_stub; + return; + } + +#ifdef WITH_IPTABLES_OPTION + + mutex_init(&__xt_lock); + iptables_add_rule = __iptables_add_rule; + iptables_del_rule = __iptables_del_rule; + + // flush chains + + const char *err; + struct xtc_handle *h; + + xt_lock(); + + err = "could not initialize iptables"; + h = iptc_init("filter"); + if (!h) + goto out; + err = "could not flush iptables chain"; + if (!iptc_flush_entries(g_iptables_chain, h)) + goto err2; + err = "could not commit iptables changes"; + if (!iptc_commit(h)) + goto err2; + iptc_free(h); + + err = "could not initialize ip6tables"; + h = ip6tc_init("filter"); + if (!h) + goto out; + err = "could not flush ip6tables chain"; + if (!ip6tc_flush_entries(g_iptables_chain, h)) + goto err1; + err = "could not commit iptables changes"; + if (!ip6tc_commit(h)) + goto err1; + ip6tc_free(h); + + err = NULL; + goto out; + +err1: + ip6tc_free(h); + goto out; +err2: + iptc_free(h); + goto out; +out: + xt_unlock(); + if (err) + ilog(LOG_ERROR, "Failed to flush iptables chain: %s (%s)", err, strerror(errno)); + +#endif // WITH_IPTABLES_OPTION + +} diff --git a/daemon/iptables.h b/daemon/iptables.h new file mode 100644 index 000000000..77f988550 --- /dev/null +++ b/daemon/iptables.h @@ -0,0 +1,17 @@ +#ifndef _IPTABLES_H_ +#define _IPTABLES_H_ + + +#include "socket.h" +#include "str.h" + + +extern char *g_iptables_chain; + +void iptables_init(void); +extern int (*iptables_add_rule)(const socket_t *local_sock, const str *comment); +extern int (*iptables_del_rule)(const socket_t *local_sock); + + + +#endif diff --git a/daemon/main.c b/daemon/main.c index d4c738c48..43b91eb90 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -33,6 +33,7 @@ #include "recording.h" #include "auxlib.h" #include "rtcp.h" +#include "iptables.h" @@ -292,6 +293,9 @@ static void options(int *argc, char ***argv) { { "recording-dir", 0, 0, G_OPTION_ARG_STRING, &spooldir, "Directory for storing pcap and metadata files", "FILE" }, { "recording-method",0, 0, G_OPTION_ARG_STRING, &rec_method, "Strategy for call recording", "pcap|proc" }, { "recording-format",0, 0, G_OPTION_ARG_STRING, &rec_format, "File format for stored pcap files", "raw|eth" }, +#ifdef WITH_IPTABLES_OPTION + { "iptables-chain",0,0, G_OPTION_ARG_STRING, &g_iptables_chain,"Add explicit firewall rules to this iptables chain","STRING" }, +#endif { NULL, } }; @@ -478,6 +482,7 @@ static void init_everything() { ice_init(); crypto_init_main(); interfaces_init(&interfaces); + iptables_init(); } diff --git a/daemon/media_socket.c b/daemon/media_socket.c index 42779b3f8..f4434dd1f 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -22,6 +22,7 @@ #include "rtplib.h" #include "rtcplib.h" #include "ssrc.h" +#include "iptables.h" #ifndef PORT_RANDOM_MIN @@ -598,16 +599,7 @@ struct local_intf *get_any_interface_address(const struct logical_intf *lif, soc -/* XXX family specific? unify? */ -static int get_port6(socket_t *r, unsigned int port, struct intf_spec *spec) { - if (open_socket(r, SOCK_DGRAM, port, &spec->local_address.addr)) - return -1; - socket_timestamping(r); - return 0; -} - -static int get_port(socket_t *r, unsigned int port, struct intf_spec *spec) { - int ret; +static int get_port(socket_t *r, unsigned int port, struct intf_spec *spec, const str *label) { struct port_pool *pp; __C_DBG("attempting to open port %u", port); @@ -620,14 +612,15 @@ static int get_port(socket_t *r, unsigned int port, struct intf_spec *spec) { } __C_DBG("port %d locked", port); - ret = get_port6(r, port, spec); - - if (ret) { + if (open_socket(r, SOCK_DGRAM, port, &spec->local_address.addr)) { __C_DBG("couldn't open port %d", port); bit_array_clear(pp->ports_used, port); - return ret; + return -1; } + iptables_add_rule(r, label); + socket_timestamping(r); + g_atomic_int_dec_and_test(&pp->free_ports); __C_DBG("%d free ports remaining on interface %s", pp->free_ports, sockaddr_print_buf(&spec->local_address.addr)); @@ -640,6 +633,8 @@ static void release_port(socket_t *r, struct intf_spec *spec) { __C_DBG("trying to release port %u", port); + iptables_del_rule(r); + if (close_socket(r) == 0) { __C_DBG("port %u is released", port); bit_array_clear(spec->port_pool.ports_used, port); @@ -657,7 +652,7 @@ static void free_port(socket_t *r, struct intf_spec *spec) { /* puts list of socket_t into "out" */ int __get_consecutive_ports(GQueue *out, unsigned int num_ports, unsigned int wanted_start_port, - struct intf_spec *spec) + struct intf_spec *spec, const str *label) { int i, cycle = 0; socket_t *sk; @@ -712,7 +707,7 @@ int __get_consecutive_ports(GQueue *out, unsigned int num_ports, unsigned int wa goto release_restart; } - if (get_port(sk, port++, spec)) + if (get_port(sk, port++, spec, label)) goto release_restart; } break; @@ -739,7 +734,9 @@ fail: } /* puts a list of "struct intf_list" into "out", containing socket_t list */ -int get_consecutive_ports(GQueue *out, unsigned int num_ports, const struct logical_intf *log) { +int get_consecutive_ports(GQueue *out, unsigned int num_ports, const struct logical_intf *log, + const str *label) +{ GList *l; struct intf_list *il; const struct local_intf *loc; @@ -750,7 +747,7 @@ int get_consecutive_ports(GQueue *out, unsigned int num_ports, const struct logi il = g_slice_alloc0(sizeof(*il)); il->local_intf = loc; g_queue_push_tail(out, il); - if (G_LIKELY(!__get_consecutive_ports(&il->list, num_ports, 0, loc->spec))) { + if (G_LIKELY(!__get_consecutive_ports(&il->list, num_ports, 0, loc->spec, label))) { // success - found available ports on local interfaces, so far continue; } diff --git a/daemon/media_socket.h b/daemon/media_socket.h index abdac21ae..bf20efa2b 100644 --- a/daemon/media_socket.h +++ b/daemon/media_socket.h @@ -82,8 +82,8 @@ INLINE void set_tos(socket_t *s, unsigned int tos) { s->family->tos(s, tos); } int __get_consecutive_ports(GQueue *out, unsigned int num_ports, unsigned int wanted_start_port, - struct intf_spec *spec); -int get_consecutive_ports(GQueue *out, unsigned int num_ports, const struct logical_intf *log); + struct intf_spec *spec, const str *); +int get_consecutive_ports(GQueue *out, unsigned int num_ports, const struct logical_intf *log, const str *); struct stream_fd *stream_fd_new(socket_t *fd, struct call *call, const struct local_intf *lif); void free_intf_list(struct intf_list *il); diff --git a/daemon/redis.c b/daemon/redis.c index ecfd0b298..de0233d41 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1083,7 +1083,7 @@ static int redis_sfds(struct call *c, struct redis_list *sfds) { goto err; err = "failed to open ports"; - if (__get_consecutive_ports(&q, 1, port, loc->spec)) + if (__get_consecutive_ports(&q, 1, port, loc->spec, &c->callid)) goto err; err = "no port returned"; sock = g_queue_pop_head(&q); diff --git a/debian/control b/debian/control index d1bbd4402..feeadfd7f 100644 --- a/debian/control +++ b/debian/control @@ -15,6 +15,7 @@ Build-Depends: libevent-dev (>= 2.0), libglib2.0-dev (>= 2.30), libhiredis-dev, + libiptc-dev | iptables-dev, libjson-glib-dev, libpcap0.8-dev | libpcap-dev, libpcre3-dev, diff --git a/lib/str.h b/lib/str.h index 4880ff6c1..36fb8a156 100644 --- a/lib/str.h +++ b/lib/str.h @@ -72,6 +72,8 @@ INLINE int str_to_i(str *s, int def); INLINE uint str_to_ui(str *s, int def); /* extracts the first/next token into "new_token" and modifies "ori_and_remainer" in place */ INLINE int str_token(str *new_token, str *ori_and_remainder, int sep); +/* copy a string to a regular C string buffer, limiting the max size */ +INLINE char *str_ncpy(char *dst, size_t bufsize, const str *src); /* asprintf() analogs */ #define str_sprintf(fmt, a...) __str_sprintf(STR_MALLOC_PADDING fmt, a) @@ -322,6 +324,15 @@ INLINE int str_uri_encode(char *out, const str *in) { return str_uri_encode_len(out, in->s, in->len); } +INLINE char *str_ncpy(char *dst, size_t bufsize, const str *src) { + size_t to_copy = src->len; + if (to_copy >= bufsize) + to_copy = bufsize - 1; + memcpy(dst, src->s, to_copy); + dst[to_copy] = 0; + return dst; +} + /* Generates a hex string representing n random bytes. len(rand_str) = 2*num_bytes + 1 */ char *rand_hex_str(char *rand_str, int num_bytes);