From 40d94ca1dcd8ee0c6df4a33707e224dbe6a684b3 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Tue, 10 Apr 2018 19:00:22 +0200 Subject: libnftables: Support buffering output and error When integrating libnftables into Python code using ctypes module, having to use a FILE pointer for output becomes a show-stopper. Therefore make Python hackers' lives (a little) less painful by providing convenience functions to setup buffering output and error streams using fopencookie() and retrieving the buffers. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- include/nftables.h | 9 +++ include/nftables/nftables.h | 7 +++ src/libnftables.c | 137 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) diff --git a/include/nftables.h b/include/nftables.h index 1b368971..5c181be5 100644 --- a/include/nftables.h +++ b/include/nftables.h @@ -7,6 +7,13 @@ #include #include +struct cookie { + char *buf; + size_t buflen; + size_t pos; + FILE *orig_fp; +}; + struct output_ctx { unsigned int numeric; unsigned int stateless; @@ -15,6 +22,8 @@ struct output_ctx { unsigned int echo; FILE *output_fp; FILE *error_fp; + struct cookie *output_cookie; + struct cookie *error_cookie; }; struct nft_cache { diff --git a/include/nftables/nftables.h b/include/nftables/nftables.h index 1e930682..652e0ca9 100644 --- a/include/nftables/nftables.h +++ b/include/nftables/nftables.h @@ -57,7 +57,14 @@ bool nft_ctx_output_get_echo(struct nft_ctx *ctx); void nft_ctx_output_set_echo(struct nft_ctx *ctx, bool val); FILE *nft_ctx_set_output(struct nft_ctx *ctx, FILE *fp); +int nft_ctx_buffer_output(struct nft_ctx *ctx); +int nft_ctx_unbuffer_output(struct nft_ctx *ctx); +const char *nft_ctx_get_output_buffer(struct nft_ctx *ctx); + FILE *nft_ctx_set_error(struct nft_ctx *ctx, FILE *fp); +int nft_ctx_buffer_error(struct nft_ctx *ctx); +int nft_ctx_unbuffer_error(struct nft_ctx *ctx); +const char *nft_ctx_get_error_buffer(struct nft_ctx *ctx); int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path); void nft_ctx_clear_include_paths(struct nft_ctx *ctx); diff --git a/src/libnftables.c b/src/libnftables.c index d6622d51..73363e3a 100644 --- a/src/libnftables.c +++ b/src/libnftables.c @@ -177,11 +177,148 @@ struct nft_ctx *nft_ctx_new(uint32_t flags) return ctx; } +static void free_cookie(struct cookie *cookie) +{ + if (cookie) { + free(cookie->buf); + free(cookie); + } +} + +static ssize_t cookie_write(void *cptr, const char *buf, size_t buflen) +{ + struct cookie *cookie = cptr; + + if (!cookie->buflen) { + cookie->buflen = buflen + 1; + cookie->buf = xmalloc(cookie->buflen); + } else if (cookie->pos + buflen >= cookie->buflen) { + size_t newlen = cookie->buflen * 2; + + while (newlen <= cookie->pos + buflen) + newlen *= 2; + + cookie->buf = xrealloc(cookie->buf, newlen); + cookie->buflen = newlen; + } + memcpy(cookie->buf + cookie->pos, buf, buflen); + cookie->pos += buflen; + cookie->buf[cookie->pos] = '\0'; + + return buflen; +} + +static int init_cookie(struct cookie **cpptr, FILE **fp) +{ + struct cookie *cookie; + cookie_io_functions_t cookie_fops = { + .write = cookie_write, + }; + FILE *cookie_fp; + + if (!cpptr || !fp) + return 1; + + cookie = *cpptr; + + if (cookie) { /* just rewind buffer */ + if (cookie->buflen) { + cookie->pos = 0; + cookie->buf[0] = '\0'; + } + return 0; + } + + cookie = xzalloc(sizeof(*cookie)); + cookie->orig_fp = *fp; + + cookie_fp = fopencookie(cookie, "w", cookie_fops); + if (!cookie_fp) { + free(cookie); + return 1; + } + + *cpptr = cookie; + *fp = cookie_fp; + return 0; +} + +int nft_ctx_buffer_output(struct nft_ctx *ctx) +{ + struct output_ctx *octx = &ctx->output; + + return init_cookie(&octx->output_cookie, &octx->output_fp); +} + +int nft_ctx_unbuffer_output(struct nft_ctx *ctx) +{ + if (!ctx->output.output_cookie) + return 1; + + ctx->output.output_fp = ctx->output.output_cookie->orig_fp; + free_cookie(ctx->output.output_cookie); + ctx->output.output_cookie = NULL; + return 0; +} + +int nft_ctx_buffer_error(struct nft_ctx *ctx) +{ + struct output_ctx *octx = &ctx->output; + + return init_cookie(&octx->error_cookie, &octx->error_fp); +} + +int nft_ctx_unbuffer_error(struct nft_ctx *ctx) +{ + if (!ctx->output.error_cookie) + return 1; + + ctx->output.error_fp = ctx->output.error_cookie->orig_fp; + free_cookie(ctx->output.error_cookie); + ctx->output.error_cookie = NULL; + return 0; +} + +static const char *get_cookie_buffer(struct cookie *cookie, FILE *cookie_fp) +{ + if (!cookie) + return NULL; + + fflush(cookie_fp); + + /* This is a bit tricky: Rewind the buffer for future use and return + * the old content at the same time. + * Therefore just reset buffer position, don't change it's content. And + * return an empty string if buffer position is zero. */ + + if (!cookie->buflen || !cookie->pos) + return ""; + + cookie->pos = 0; + return cookie->buf; +} + +const char *nft_ctx_get_output_buffer(struct nft_ctx *ctx) +{ + struct output_ctx *octx = &ctx->output; + + return get_cookie_buffer(octx->output_cookie, octx->output_fp); +} + +const char *nft_ctx_get_error_buffer(struct nft_ctx *ctx) +{ + struct output_ctx *octx = &ctx->output; + + return get_cookie_buffer(octx->error_cookie, octx->error_fp); +} + void nft_ctx_free(struct nft_ctx *ctx) { if (ctx->nf_sock) netlink_close_sock(ctx->nf_sock); + free_cookie(ctx->output.output_cookie); + free_cookie(ctx->output.error_cookie); iface_cache_release(); cache_release(&ctx->cache); nft_ctx_clear_include_paths(ctx); -- cgit v1.2.3