Description: Add support for ipv6 Origin: http://wilmer.gaast.net/blog/archives/61-spamass-milter-and-IPv6.html Bug: #569483 Author: Wilmer van der Gaast Forwarded: no Index: spamass-milter/spamass-milter.cpp =================================================================== --- spamass-milter.orig/spamass-milter.cpp 2012-06-21 13:03:25.000000000 -0700 +++ spamass-milter/spamass-milter.cpp 2012-06-21 13:03:42.000000000 -0700 @@ -88,6 +88,7 @@ #include "subst_poll.h" #endif #include +#include // C++ includes #include @@ -678,6 +679,8 @@ { struct context *sctx; int rv; + struct sockaddr_in localhost; + debug(D_FUNC, "mlfi_connect: enter"); @@ -687,10 +690,15 @@ { /* not a socket; probably a local user calling sendmail directly */ /* set to 127.0.0.1 */ - sctx->connect_ip.s_addr = htonl(INADDR_LOOPBACK); + strcpy(sctx->connect_ip, "127.0.0.1"); + localhost.sin_family = AF_INET; + localhost.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + hostaddr = (struct sockaddr*) &localhost; } else { - sctx->connect_ip = ((struct sockaddr_in *) hostaddr)->sin_addr; + getnameinfo(hostaddr, sizeof(struct sockaddr_in6), + sctx->connect_ip, 63, NULL, 0, NI_NUMERICHOST); + debug(D_FUNC, "Remote address: %s", sctx->connect_ip); } sctx->assassin = NULL; sctx->helo = NULL; @@ -704,10 +712,10 @@ } /* debug(D_ALWAYS, "ZZZ set private context to %p", sctx); */ - if (ip_in_networklist(sctx->connect_ip, &ignorenets)) + if (ip_in_networklist(hostaddr, &ignorenets)) { debug(D_NET, "%s is in our ignore list - accepting message", - inet_ntoa(sctx->connect_ip)); + sctx->connect_ip); debug(D_FUNC, "mlfi_connect: exit ignore"); return SMFIS_ACCEPT; } @@ -779,7 +787,7 @@ return SMFIS_TEMPFAIL; }; - assassin->set_connectip(string(inet_ntoa(sctx->connect_ip))); + assassin->set_connectip(string(sctx->connect_ip)); // Store a pointer to the assassin object in our context struct sctx->assassin = assassin; @@ -2033,69 +2041,119 @@ { char *tnet = strsep(&token, "/"); char *tmask = token; - struct in_addr net, mask; + struct in_addr net; + struct in6_addr net6; if (list->num_nets % 10 == 0) - list->nets = (struct net*)realloc(list->nets, sizeof(*list->nets) * (list->num_nets + 10)); + list->nets = (union net*)realloc(list->nets, sizeof(*list->nets) * (list->num_nets + 10)); - if (!inet_aton(tnet, &net)) + if (inet_pton(AF_INET, tnet, &net)) { - fprintf(stderr, "Could not parse \"%s\" as a network\n", tnet); - exit(1); - } + struct in_addr mask; + + if (tmask) + { + if (strchr(tmask, '.') == NULL) + { + /* CIDR */ + unsigned int bits; + int ret; + ret = sscanf(tmask, "%u", &bits); + if (ret != 1 || bits > 32) + { + fprintf(stderr,"%s: bad CIDR value", tmask); + exit(1); + } + mask.s_addr = htonl(~((1L << (32 - bits)) - 1) & 0xffffffff); + } else if (!inet_pton(AF_INET6, tmask, &mask)) + { + fprintf(stderr, "Could not parse \"%s\" as a netmask\n", tmask); + exit(1); + } + } else + mask.s_addr = 0xffffffff; - if (tmask) + { + char *snet = strdup(inet_ntoa(net)); + debug(D_MISC, "Adding %s/%s to network list", snet, inet_ntoa(mask)); + free(snet); + } + + net.s_addr = net.s_addr & mask.s_addr; + list->nets[list->num_nets].net4.af = AF_INET; + list->nets[list->num_nets].net4.network = net; + list->nets[list->num_nets].net4.netmask = mask; + list->num_nets++; + } else if (inet_pton(AF_INET6, tnet, &net6)) { - if (strchr(tmask, '.') == NULL) + int mask; + + if (tmask) { - /* CIDR */ - unsigned int bits; - int ret; - ret = sscanf(tmask, "%u", &bits); - if (ret != 1 || bits > 32) + if (sscanf(tmask, "%d", &mask) != 1 || mask > 128) { fprintf(stderr,"%s: bad CIDR value", tmask); exit(1); } - mask.s_addr = htonl(~((1L << (32 - bits)) - 1) & 0xffffffff); - } else if (!inet_aton(tmask, &mask)) - { - fprintf(stderr, "Could not parse \"%s\" as a netmask\n", tmask); - exit(1); - } + } else + mask = 128; + + list->nets[list->num_nets].net6.af = AF_INET6; + list->nets[list->num_nets].net6.network = net6; + list->nets[list->num_nets].net6.netmask = mask; + list->num_nets++; } else - mask.s_addr = 0xffffffff; - { - char *snet = strdup(inet_ntoa(net)); - debug(D_MISC, "Adding %s/%s to network list", snet, inet_ntoa(mask)); - free(snet); + fprintf(stderr, "Could not parse \"%s\" as a network\n", tnet); + exit(1); } - net.s_addr = net.s_addr & mask.s_addr; - list->nets[list->num_nets].network = net; - list->nets[list->num_nets].netmask = mask; - list->num_nets++; } free(string); } -int ip_in_networklist(struct in_addr ip, struct networklist *list) +int ip_in_networklist(struct sockaddr *addr, struct networklist *list) { int i; if (list->num_nets == 0) return 0; - - debug(D_NET, "Checking %s against:", inet_ntoa(ip)); + + //debug(D_NET, "Checking %s against:", inet_ntoa(ip)); for (i = 0; i < list->num_nets; i++) { - debug(D_NET, "%s", inet_ntoa(list->nets[i].network)); - debug(D_NET, "/%s", inet_ntoa(list->nets[i].netmask)); - if ((ip.s_addr & list->nets[i].netmask.s_addr) == list->nets[i].network.s_addr) - { - debug(D_NET, "Hit!"); - return 1; + if (list->nets[i].net.af == AF_INET && addr->sa_family == AF_INET) + { + struct in_addr ip = ((struct sockaddr_in *)addr)->sin_addr; + + debug(D_NET, "%s", inet_ntoa(list->nets[i].net4.network)); + debug(D_NET, "/%s", inet_ntoa(list->nets[i].net4.netmask)); + if ((ip.s_addr & list->nets[i].net4.netmask.s_addr) == list->nets[i].net4.network.s_addr) + { + debug(D_NET, "Hit!"); + return 1; + } + } else if (list->nets[i].net.af == AF_INET6 && addr->sa_family == AF_INET6) + { + u_int8_t *ip = ((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr; + int mask, j; + + mask = list->nets[i].net6.netmask; + for (j = 0; j < 16 && mask > 0; j++, mask -= 8) + { + unsigned char bytemask; + + bytemask = (mask < 8) ? ~((1L << (8 - mask)) - 1) : 0xff; + + if ((ip[j] & bytemask) != (list->nets[i].net6.network.s6_addr[j] & bytemask)) + break; + } + + if (mask <= 0) + { + debug(D_NET, "Hit!"); + return 1; + } } } Index: spamass-milter/spamass-milter.h =================================================================== --- spamass-milter.orig/spamass-milter.h 2012-06-21 13:03:25.000000000 -0700 +++ spamass-milter/spamass-milter.h 2012-06-21 13:03:42.000000000 -0700 @@ -56,16 +56,30 @@ extern struct smfiDesc smfilter; /* struct describing a single network */ -struct net +union net { - struct in_addr network; - struct in_addr netmask; + struct + { + uint8_t af; + } net; + struct + { + uint8_t af; + struct in_addr network; + struct in_addr netmask; + } net4; + struct + { + uint8_t af; + struct in6_addr network; + int netmask; /* Just the number of bits for IPv6 */ + } net6; }; /* an array of networks */ struct networklist { - struct net *nets; + union net *nets; int num_nets; }; @@ -165,7 +179,7 @@ /* Private data structure to carry per-client data between calls */ struct context { - struct in_addr connect_ip; // remote IP address + char connect_ip[64]; // remote IP address char *helo; SpamAssassin *assassin; // pointer to the SA object if we're processing a message }; @@ -182,7 +196,7 @@ int cmp_nocase_partial(const string&, const string&); void closeall(int fd); void parse_networklist(char *string, struct networklist *list); -int ip_in_networklist(struct in_addr ip, struct networklist *list); +int ip_in_networklist(struct sockaddr *addr, struct networklist *list); void parse_debuglevel(char* string); char *strlwr(char *str); void warnmacro(char *macro, char *scope);