Re: smtpd: add support for cidr in hostname resolution for spf walk

2019-11-23 Thread Quentin Rameau
> Hello

Hi Eric,

> > Hello,
> > 
> > Here's a patch for smtpctl spf resolution, adding support for target
> > specified as a hostname + cidr.
> > 
> > Yes, SPF lets you specify targets like a:example.com/24.
> > 
> > Due to the async and recursive nature of DNS resolution in spfwalk.c,
> > it's kind of hard to pass data around without too much memory
> > allocation, so I decided to just pass cidr(s) as integer value instead
> > of keeping the original string and passing pointers around.
> > 
> > Comments welcome, thanks!  
> 
> I find it confusing that everything is stuffed into the sender struct,
> including the callback.  I don't really see the point. If you don't
> need to pass the target value over to the callback, there is no need
> to have it in the struct either, especially since the pointer is only
> valid at the time you call the lookup function.

Indeed, passing the target around isn't strictly needed, this is rather
a leftover from a previous implementation were both CIDRs were string
pointers pointing to the target.
 
> Another point, I don't understand how the parse_sender() function is
> supposed to work. Can you give examples with cidr4 and cidr6?

Per the SPF RFC[0], the "a" and "mx" mechanism can have an IPv4 cidr,
or an IPv6 cidr, or both like “a:example.com/24”, “a:example.com//64”,
or “a:example.com/24/64” which specifies both cidr for the IPv4 and cidr
for the IPv6 resolutions of the name.

And yes, those are actually used out there, like by my bank company.

So the function parses the target for a "/cidr4" and strtonum() it to
cidr4, then parses for another "/cidr6" and strtonum() to cidr6.

[0] https://tools.ietf.org/rfc/rfc7208.txt

Here's an updated patch, plus two independant fixes for smtpd.h
(missing header limits.h for PATH_MAX and imsg.h for img structure).

struct sender target removed
smtpd.h header added for xmalloc()
fixed parse_sender for CIDRs like "//num".

Index: usr.sbin/smtpd/smtpd.h
===
RCS file: /var/cvs/src/usr.sbin/smtpd/smtpd.h,v
retrieving revision 1.642
diff -u -r1.642 smtpd.h
--- usr.sbin/smtpd/smtpd.h  3 Nov 2019 23:58:51 -   1.642
+++ usr.sbin/smtpd/smtpd.h  23 Nov 2019 20:19:17 -
@@ -22,6 +22,8 @@
 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
 #endif
 
+#include 
+#include 
 #include 
 #include 
 #include 
Index: usr.sbin/smtpd/spfwalk.c
===
RCS file: /var/cvs/src/usr.sbin/smtpd/spfwalk.c,v
retrieving revision 1.15
diff -u -r1.15 spfwalk.c
--- usr.sbin/smtpd/spfwalk.c11 Nov 2019 17:20:25 -  1.15
+++ usr.sbin/smtpd/spfwalk.c23 Nov 2019 21:13:11 -
@@ -35,20 +35,28 @@
 #include 
 
 #define LINE_MAX 1024
+#include "smtpd.h"
 #include "smtpd-defines.h"
 #include "smtpd-api.h"
 #include "unpack_dns.h"
 #include "parser.h"
 
+struct sender {
+   void(*cb)(struct dns_rr *, struct sender *);
+   int   cidr4;
+   int   cidr6;
+};
+
 int spfwalk(int, struct parameter *);
 
-static voiddispatch_txt(struct dns_rr *);
-static voiddispatch_mx(struct dns_rr *);
-static voiddispatch_a(struct dns_rr *);
-static voiddispatch_(struct dns_rr *);
-static voidlookup_record(int, const char *, void (*)(struct dns_rr *));
+static voiddispatch_txt(struct dns_rr *, struct sender *);
+static voiddispatch_mx(struct dns_rr *, struct sender *);
+static voiddispatch_a(struct dns_rr *, struct sender *);
+static voiddispatch_(struct dns_rr *, struct sender *);
+static voidlookup_record(int, const char *, struct sender *);
 static voiddispatch_record(struct asr_result *, void *);
 static ssize_t parse_txt(const char *, size_t, char *, size_t);
+static int parse_sender(char *, struct sender *);
 
 int ip_v4 = 0;
 int ip_v6 = 0;
@@ -59,6 +67,7 @@
 int
 spfwalk(int argc, struct parameter *argv)
 {
+   struct sendersdr;
const char  *ip_family = NULL;
char*line = NULL;
size_t   linesize = 0;
@@ -81,12 +90,15 @@
dict_init();
event_init();
 
+   sdr.cidr4 = sdr.cidr6 = -1;
+   sdr.cb = dispatch_txt;
+
while ((linelen = getline(, , stdin)) != -1) {
while (linelen-- > 0 && isspace(line[linelen]))
line[linelen] = '\0';
 
if (linelen > 0)
-   lookup_record(T_TXT, line, dispatch_txt);
+   lookup_record(T_TXT, line, );
}
 
free(line);
@@ -100,20 +112,23 @@
 }
 
 void
-lookup_record(int type, const char *record, void (*cb)(struct dns_rr *))
+lookup_record(int type, const char *record, struct sender *sdr)
 {
struct asr_query *as;
+   struct sender *nsdr;
 
as = res_query_async(record, C_IN, type, NULL);
if (as == NULL)
err(1, "res_query_async");
-   event_asr_run(as, dispatch_record, cb);
+   nsdr 

Re: smtpd: add support for cidr in hostname resolution for spf walk

2019-11-23 Thread Eric Faurot
On Tue, Nov 12, 2019 at 02:16:25PM +0100, Quentin Rameau wrote:

Hello

> Hello,
> 
> Here's a patch for smtpctl spf resolution, adding support for target
> specified as a hostname + cidr.
> 
> Yes, SPF lets you specify targets like a:example.com/24.
> 
> Due to the async and recursive nature of DNS resolution in spfwalk.c,
> it's kind of hard to pass data around without too much memory
> allocation, so I decided to just pass cidr(s) as integer value instead
> of keeping the original string and passing pointers around.
> 
> Comments welcome, thanks!

I find it confusing that everything is stuffed into the sender struct,
including the callback.  I don't really see the point. If you don't
need to pass the target value over to the callback, there is no need
to have it in the struct either, especially since the pointer is only
valid at the time you call the lookup function.

Another point, I don't understand how the parse_sender() function is
supposed to work. Can you give examples with cidr4 and cidr6?

Eric.

> 
> Index: usr.sbin/smtpd/spfwalk.c
> ===
> RCS file: /var/cvs/src/usr.sbin/smtpd/spfwalk.c,v
> retrieving revision 1.15
> diff -u -r1.15 spfwalk.c
> --- usr.sbin/smtpd/spfwalk.c  11 Nov 2019 17:20:25 -  1.15
> +++ usr.sbin/smtpd/spfwalk.c  12 Nov 2019 12:43:25 -
> @@ -40,15 +40,24 @@
>  #include "unpack_dns.h"
>  #include "parser.h"
>  
> +struct sender {
> + void(*cb)(struct dns_rr *, struct sender *);
> + char *target;
> + int   cidr4;
> + int   cidr6;
> +};
> +
> +void *xmalloc(size_t);
>  int spfwalk(int, struct parameter *);
>  
> -static void  dispatch_txt(struct dns_rr *);
> -static void  dispatch_mx(struct dns_rr *);
> -static void  dispatch_a(struct dns_rr *);
> -static void  dispatch_(struct dns_rr *);
> -static void  lookup_record(int, const char *, void (*)(struct dns_rr *));
> +static void  dispatch_txt(struct dns_rr *, struct sender *);
> +static void  dispatch_mx(struct dns_rr *, struct sender *);
> +static void  dispatch_a(struct dns_rr *, struct sender *);
> +static void  dispatch_(struct dns_rr *, struct sender *);
> +static void  lookup_record(int, struct sender *);
>  static void  dispatch_record(struct asr_result *, void *);
>  static ssize_t   parse_txt(const char *, size_t, char *, size_t);
> +static int   parse_sender(struct sender *);
>  
>  int ip_v4 = 0;
>  int ip_v6 = 0;
> @@ -59,6 +68,7 @@
>  int
>  spfwalk(int argc, struct parameter *argv)
>  {
> + struct sendersdr;
>   const char  *ip_family = NULL;
>   char*line = NULL;
>   size_t   linesize = 0;
> @@ -81,12 +91,17 @@
>   dict_init();
>   event_init();
>  
> + sdr.cidr4 = sdr.cidr6 = -1;
> + sdr.cb = dispatch_txt;
> +
>   while ((linelen = getline(, , stdin)) != -1) {
>   while (linelen-- > 0 && isspace(line[linelen]))
>   line[linelen] = '\0';
>  
> - if (linelen > 0)
> - lookup_record(T_TXT, line, dispatch_txt);
> + if (linelen > 0) {
> + sdr.target = line;
> + lookup_record(T_TXT, );
> + }
>   }
>  
>   free(line);
> @@ -100,20 +115,23 @@
>  }
>  
>  void
> -lookup_record(int type, const char *record, void (*cb)(struct dns_rr *))
> +lookup_record(int type, struct sender *sdr)
>  {
>   struct asr_query *as;
> + struct sender *nsdr;
>  
> - as = res_query_async(record, C_IN, type, NULL);
> + as = res_query_async(sdr->target, C_IN, type, NULL);
>   if (as == NULL)
>   err(1, "res_query_async");
> - event_asr_run(as, dispatch_record, cb);
> + nsdr = xmalloc(sizeof(*nsdr));
> + *nsdr = *sdr;
> + event_asr_run(as, dispatch_record, (void *)nsdr);
>  }
>  
>  void
>  dispatch_record(struct asr_result *ar, void *arg)
>  {
> - void (*cb)(struct dns_rr *) = arg;
> + struct sender *sdr = arg;
>   struct unpack pack;
>   struct dns_header h;
>   struct dns_query q;
> @@ -121,7 +139,7 @@
>  
>   /* best effort */
>   if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA)
> - return;
> + goto end;
>  
>   unpack_init(, ar->ar_data, ar->ar_datalen);
>   unpack_header(, );
> @@ -130,19 +148,22 @@
>   for (; h.ancount; h.ancount--) {
>   unpack_rr(, );
>   /**/
> - cb();
> + sdr->cb(, sdr);
>   }
> +end:
> + free(sdr);
>  }
>  
>  void
> -dispatch_txt(struct dns_rr *rr)
> +dispatch_txt(struct dns_rr *rr, struct sender *sdr)
>  {
> + char buf[4096];
> + char *argv[512];
> + char buf2[512];
> + struct sender lsdr;
>   struct in6_addr ina;
> -char buf[4096];
> -char buf2[512];
> -char *in = buf;
> -char *argv[512];
> -char **ap = argv;
> + char **ap = argv;
> + char *in = buf;
>   char 

smtpd: add support for cidr in hostname resolution for spf walk

2019-11-12 Thread Quentin Rameau
Hello,

Here's a patch for smtpctl spf resolution, adding support for target
specified as a hostname + cidr.

Yes, SPF lets you specify targets like a:example.com/24.

Due to the async and recursive nature of DNS resolution in spfwalk.c,
it's kind of hard to pass data around without too much memory
allocation, so I decided to just pass cidr(s) as integer value instead
of keeping the original string and passing pointers around.

Comments welcome, thanks!

Index: usr.sbin/smtpd/spfwalk.c
===
RCS file: /var/cvs/src/usr.sbin/smtpd/spfwalk.c,v
retrieving revision 1.15
diff -u -r1.15 spfwalk.c
--- usr.sbin/smtpd/spfwalk.c11 Nov 2019 17:20:25 -  1.15
+++ usr.sbin/smtpd/spfwalk.c12 Nov 2019 12:43:25 -
@@ -40,15 +40,24 @@
 #include "unpack_dns.h"
 #include "parser.h"
 
+struct sender {
+   void(*cb)(struct dns_rr *, struct sender *);
+   char *target;
+   int   cidr4;
+   int   cidr6;
+};
+
+void *xmalloc(size_t);
 int spfwalk(int, struct parameter *);
 
-static voiddispatch_txt(struct dns_rr *);
-static voiddispatch_mx(struct dns_rr *);
-static voiddispatch_a(struct dns_rr *);
-static voiddispatch_(struct dns_rr *);
-static voidlookup_record(int, const char *, void (*)(struct dns_rr *));
+static voiddispatch_txt(struct dns_rr *, struct sender *);
+static voiddispatch_mx(struct dns_rr *, struct sender *);
+static voiddispatch_a(struct dns_rr *, struct sender *);
+static voiddispatch_(struct dns_rr *, struct sender *);
+static voidlookup_record(int, struct sender *);
 static voiddispatch_record(struct asr_result *, void *);
 static ssize_t parse_txt(const char *, size_t, char *, size_t);
+static int parse_sender(struct sender *);
 
 int ip_v4 = 0;
 int ip_v6 = 0;
@@ -59,6 +68,7 @@
 int
 spfwalk(int argc, struct parameter *argv)
 {
+   struct sendersdr;
const char  *ip_family = NULL;
char*line = NULL;
size_t   linesize = 0;
@@ -81,12 +91,17 @@
dict_init();
event_init();
 
+   sdr.cidr4 = sdr.cidr6 = -1;
+   sdr.cb = dispatch_txt;
+
while ((linelen = getline(, , stdin)) != -1) {
while (linelen-- > 0 && isspace(line[linelen]))
line[linelen] = '\0';
 
-   if (linelen > 0)
-   lookup_record(T_TXT, line, dispatch_txt);
+   if (linelen > 0) {
+   sdr.target = line;
+   lookup_record(T_TXT, );
+   }
}
 
free(line);
@@ -100,20 +115,23 @@
 }
 
 void
-lookup_record(int type, const char *record, void (*cb)(struct dns_rr *))
+lookup_record(int type, struct sender *sdr)
 {
struct asr_query *as;
+   struct sender *nsdr;
 
-   as = res_query_async(record, C_IN, type, NULL);
+   as = res_query_async(sdr->target, C_IN, type, NULL);
if (as == NULL)
err(1, "res_query_async");
-   event_asr_run(as, dispatch_record, cb);
+   nsdr = xmalloc(sizeof(*nsdr));
+   *nsdr = *sdr;
+   event_asr_run(as, dispatch_record, (void *)nsdr);
 }
 
 void
 dispatch_record(struct asr_result *ar, void *arg)
 {
-   void (*cb)(struct dns_rr *) = arg;
+   struct sender *sdr = arg;
struct unpack pack;
struct dns_header h;
struct dns_query q;
@@ -121,7 +139,7 @@
 
/* best effort */
if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA)
-   return;
+   goto end;
 
unpack_init(, ar->ar_data, ar->ar_datalen);
unpack_header(, );
@@ -130,19 +148,22 @@
for (; h.ancount; h.ancount--) {
unpack_rr(, );
/**/
-   cb();
+   sdr->cb(, sdr);
}
+end:
+   free(sdr);
 }
 
 void
-dispatch_txt(struct dns_rr *rr)
+dispatch_txt(struct dns_rr *rr, struct sender *sdr)
 {
+   char buf[4096];
+   char *argv[512];
+   char buf2[512];
+   struct sender lsdr;
struct in6_addr ina;
-char buf[4096];
-char buf2[512];
-char *in = buf;
-char *argv[512];
-char **ap = argv;
+   char **ap = argv;
+   char *in = buf;
char *end;
ssize_t n;
 
@@ -173,6 +194,8 @@
if (**ap == '+' || **ap == '?')
(*ap)++;
 
+   lsdr.cidr4 = lsdr.cidr6 = -1;
+
if (strncasecmp("ip4:", *ap, 4) == 0) {
if ((ip_v4 == 1 || ip_both == 1) &&
inet_net_pton(AF_INET, *(ap) + 4,
@@ -190,35 +213,55 @@
if (strcasecmp("a", *ap) == 0) {
print_dname(rr->rr_dname, buf2, sizeof(buf2));
buf2[strlen(buf2) - 1] = '\0';
-   lookup_record(T_A, buf2, dispatch_a);
-   lookup_record(T_, buf2, dispatch_);
+