]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add input flag NFT_CTX_INPUT_NO_DNS to avoid blocking
authorThomas Haller <thaller@redhat.com>
Fri, 18 Aug 2023 09:40:37 +0000 (11:40 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Thu, 24 Aug 2023 07:01:45 +0000 (09:01 +0200)
getaddrinfo() blocks while trying to resolve the name. Blocking the
caller of the library is in many cases undesirable. Also, while
reconfiguring the firewall, it's not clear that resolving names via
the network will work or makes sense.

Add a new input flag NFT_CTX_INPUT_NO_DNS to opt-out from getaddrinfo()
and only accept plain IP addresses.

We could also use AI_NUMERICHOST with getaddrinfo() instead of
inet_pton(). By parsing via inet_pton(), we are better aware of
what we expect and can generate a better error message in case of
failure.

Signed-off-by: Thomas Haller <thaller@redhat.com>
Reviewed-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
doc/libnftables.adoc
include/datatype.h
include/nftables.h
include/nftables/libnftables.h
src/datatype.c
src/evaluate.c

index a0d3521e5e7acc4921b02844a7822e059d2ca21f..62de75f3fa225bdee3bdc92f994a08ad3d3cb76d 100644 (file)
@@ -84,7 +84,15 @@ The *nft_ctx_set_dry_run*() function sets the dry-run setting in 'ctx' to the va
 === nft_ctx_input_get_flags() and nft_ctx_input_set_flags()
 The flags setting controls the input format.
 
-Currently no flags are implemented.
+----
+enum {
+        NFT_CTX_INPUT_NO_DNS = (1 << 0),
+};
+----
+
+NFT_CTX_INPUT_NO_DNS::
+       Avoid resolving IP addresses with blocking getaddrinfo(). In that case,
+       only plain IP addresses are accepted.
 
 The *nft_ctx_input_get_flags*() function returns the input flags setting's value in 'ctx'.
 
index 4b59790b67f9780a66588d731dd123eac6e9ee18..be5c6d1b4011e18f7e11691509ab53f9f97d1862 100644 (file)
@@ -182,6 +182,7 @@ struct datatype *dtype_clone(const struct datatype *orig_dtype);
 
 struct parse_ctx {
        struct symbol_tables    *tbl;
+       const struct input_ctx  *input;
 };
 
 extern struct error_record *symbol_parse(struct parse_ctx *ctx,
index 7d35a95a89de603c459443eba7fbd2a36d50e21d..666a17ae4dab14f3eed42b5bdd7a66e8d90f7c43 100644 (file)
@@ -27,6 +27,11 @@ struct input_ctx {
        unsigned int flags;
 };
 
+static inline bool nft_input_no_dns(const struct input_ctx *ictx)
+{
+       return ictx->flags & NFT_CTX_INPUT_NO_DNS;
+}
+
 struct output_ctx {
        unsigned int flags;
        union {
index 9a05d3c4b90dbb6cd305ff104323578ecea363fe..e109805f32a1c0c28bca838aeaa7b76f295bb676 100644 (file)
@@ -48,6 +48,10 @@ enum nft_optimize_flags {
 uint32_t nft_ctx_get_optimize(struct nft_ctx *ctx);
 void nft_ctx_set_optimize(struct nft_ctx *ctx, uint32_t flags);
 
+enum {
+       NFT_CTX_INPUT_NO_DNS            = (1 << 0),
+};
+
 unsigned int nft_ctx_input_get_flags(struct nft_ctx *ctx);
 unsigned int nft_ctx_input_set_flags(struct nft_ctx *ctx, unsigned int flags);
 
index 381320eaf8422faae6243ac36fe8bc24d3c0dcb1..64396db82a7e52e027f3c9a9d61048361e73d2f2 100644 (file)
@@ -600,27 +600,33 @@ static struct error_record *ipaddr_type_parse(struct parse_ctx *ctx,
                                              const struct expr *sym,
                                              struct expr **res)
 {
-       struct addrinfo *ai, hints = { .ai_family = AF_INET,
-                                      .ai_socktype = SOCK_DGRAM};
-       struct in_addr *addr;
-       int err;
+       struct in_addr addr;
 
-       err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
-       if (err != 0)
-               return error(&sym->location, "Could not resolve hostname: %s",
-                            gai_strerror(err));
+       if (nft_input_no_dns(ctx->input)) {
+               if (inet_pton(AF_INET, sym->identifier, &addr) != 1)
+                       return error(&sym->location, "Invalid IPv4 address");
+       } else {
+               struct addrinfo *ai, hints = { .ai_family = AF_INET,
+                                              .ai_socktype = SOCK_DGRAM};
+               int err;
 
-       if (ai->ai_next != NULL) {
+               err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
+               if (err != 0)
+                       return error(&sym->location, "Could not resolve hostname: %s",
+                                    gai_strerror(err));
+
+               if (ai->ai_next != NULL) {
+                       freeaddrinfo(ai);
+                       return error(&sym->location,
+                                    "Hostname resolves to multiple addresses");
+               }
+               addr = ((struct sockaddr_in *)ai->ai_addr)->sin_addr;
                freeaddrinfo(ai);
-               return error(&sym->location,
-                            "Hostname resolves to multiple addresses");
        }
 
-       addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr;
        *res = constant_expr_alloc(&sym->location, &ipaddr_type,
                                   BYTEORDER_BIG_ENDIAN,
-                                  sizeof(*addr) * BITS_PER_BYTE, addr);
-       freeaddrinfo(ai);
+                                  sizeof(addr) * BITS_PER_BYTE, &addr);
        return NULL;
 }
 
@@ -659,27 +665,33 @@ static struct error_record *ip6addr_type_parse(struct parse_ctx *ctx,
                                               const struct expr *sym,
                                               struct expr **res)
 {
-       struct addrinfo *ai, hints = { .ai_family = AF_INET6,
-                                      .ai_socktype = SOCK_DGRAM};
-       struct in6_addr *addr;
-       int err;
+       struct in6_addr addr;
 
-       err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
-       if (err != 0)
-               return error(&sym->location, "Could not resolve hostname: %s",
-                            gai_strerror(err));
+       if (nft_input_no_dns(ctx->input)) {
+               if (inet_pton(AF_INET6, sym->identifier, &addr) != 1)
+                       return error(&sym->location, "Invalid IPv6 address");
+       } else {
+               struct addrinfo *ai, hints = { .ai_family = AF_INET6,
+                                              .ai_socktype = SOCK_DGRAM};
+               int err;
 
-       if (ai->ai_next != NULL) {
+               err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
+               if (err != 0)
+                       return error(&sym->location, "Could not resolve hostname: %s",
+                                    gai_strerror(err));
+
+               if (ai->ai_next != NULL) {
+                       freeaddrinfo(ai);
+                       return error(&sym->location,
+                                    "Hostname resolves to multiple addresses");
+               }
+               addr = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
                freeaddrinfo(ai);
-               return error(&sym->location,
-                            "Hostname resolves to multiple addresses");
        }
 
-       addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
        *res = constant_expr_alloc(&sym->location, &ip6addr_type,
                                   BYTEORDER_BIG_ENDIAN,
-                                  sizeof(*addr) * BITS_PER_BYTE, addr);
-       freeaddrinfo(ai);
+                                  sizeof(addr) * BITS_PER_BYTE, &addr);
        return NULL;
 }
 
index bdd3c651ea2fb7bc98fdf2952eaff84b0205c90d..2b158aee720bd7726f932b32a404b887237bee85 100644 (file)
@@ -277,7 +277,10 @@ static int flowtable_not_found(struct eval_ctx *ctx, const struct location *loc,
  */
 static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr)
 {
-       struct parse_ctx parse_ctx = { .tbl = &ctx->nft->output.tbl, };
+       struct parse_ctx parse_ctx = {
+               .tbl    = &ctx->nft->output.tbl,
+               .input  = &ctx->nft->input,
+       };
        struct error_record *erec;
        struct table *table;
        struct set *set;
@@ -3450,7 +3453,10 @@ static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
 
 static int stmt_evaluate_reject_icmp(struct eval_ctx *ctx, struct stmt *stmt)
 {
-       struct parse_ctx parse_ctx = { .tbl = &ctx->nft->output.tbl, };
+       struct parse_ctx parse_ctx = {
+               .tbl    = &ctx->nft->output.tbl,
+               .input  = &ctx->nft->input,
+       };
        struct error_record *erec;
        struct expr *code;