xref: /openbsd/libexec/spamd/sync.c (revision 00ddf0ca)
1*00ddf0caSbeck /*	$OpenBSD: sync.c,v 1.8 2009/04/20 17:42:21 beck Exp $	*/
298babcadSbeck 
398babcadSbeck /*
498babcadSbeck  * Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org>
598babcadSbeck  *
698babcadSbeck  * Permission to use, copy, modify, and distribute this software for any
798babcadSbeck  * purpose with or without fee is hereby granted, provided that the above
898babcadSbeck  * copyright notice and this permission notice appear in all copies.
998babcadSbeck  *
1098babcadSbeck  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1198babcadSbeck  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1298babcadSbeck  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1398babcadSbeck  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1498babcadSbeck  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1598babcadSbeck  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1698babcadSbeck  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1798babcadSbeck  */
1898babcadSbeck 
1998babcadSbeck #include <sys/param.h>
2098babcadSbeck #include <sys/stdint.h>
2198babcadSbeck #include <sys/file.h>
2298babcadSbeck #include <sys/wait.h>
2398babcadSbeck #include <sys/socket.h>
2498babcadSbeck #include <sys/resource.h>
2598babcadSbeck #include <sys/uio.h>
2698babcadSbeck #include <sys/ioctl.h>
2798babcadSbeck #include <sys/queue.h>
2898babcadSbeck 
2998babcadSbeck #include <net/if.h>
3098babcadSbeck #include <netinet/in.h>
3198babcadSbeck #include <arpa/inet.h>
3298babcadSbeck 
3398babcadSbeck #include <err.h>
3498babcadSbeck #include <errno.h>
3598babcadSbeck #include <getopt.h>
3698babcadSbeck #include <pwd.h>
3798babcadSbeck #include <stdio.h>
3898babcadSbeck #include <stdlib.h>
3998babcadSbeck #include <string.h>
4098babcadSbeck #include <unistd.h>
4198babcadSbeck #include <sha1.h>
4298babcadSbeck #include <syslog.h>
4398babcadSbeck 
4498babcadSbeck #include <netdb.h>
4598babcadSbeck 
4698babcadSbeck #include <openssl/hmac.h>
4798babcadSbeck 
4898babcadSbeck #include "sdl.h"
4998babcadSbeck #include "grey.h"
5098babcadSbeck #include "sync.h"
5198babcadSbeck 
5298babcadSbeck extern struct syslog_data sdata;
5398babcadSbeck extern int debug;
5498babcadSbeck extern FILE *grey;
5598babcadSbeck extern int greylist;
5698babcadSbeck 
5798babcadSbeck u_int32_t sync_counter;
5898babcadSbeck int syncfd;
5998babcadSbeck int sendmcast;
6098babcadSbeck struct sockaddr_in sync_in;
6198babcadSbeck struct sockaddr_in sync_out;
6298babcadSbeck static char *sync_key;
6398babcadSbeck 
6498babcadSbeck struct sync_host {
6598babcadSbeck 	LIST_ENTRY(sync_host)	h_entry;
6698babcadSbeck 
6798babcadSbeck 	char			*h_name;
68f6c3b21dSotto 	struct sockaddr_in	sh_addr;
6998babcadSbeck };
7098babcadSbeck LIST_HEAD(synchosts, sync_host) sync_hosts = LIST_HEAD_INITIALIZER(sync_hosts);
7198babcadSbeck 
7298babcadSbeck void	 sync_send(struct iovec *, int);
7398babcadSbeck void	 sync_addr(time_t, time_t, char *, u_int16_t);
7498babcadSbeck 
7598babcadSbeck int
7698babcadSbeck sync_addhost(const char *name, u_short port)
7798babcadSbeck {
7898babcadSbeck 	struct addrinfo hints, *res, *res0;
7998babcadSbeck 	struct sync_host *shost;
8098babcadSbeck 	struct sockaddr_in *addr = NULL;
8198babcadSbeck 
8298babcadSbeck 	bzero(&hints, sizeof(hints));
8398babcadSbeck 	hints.ai_family = PF_UNSPEC;
8498babcadSbeck 	hints.ai_socktype = SOCK_STREAM;
8598babcadSbeck 	if (getaddrinfo(name, NULL, &hints, &res0) != 0)
8698babcadSbeck 		return (EINVAL);
8798babcadSbeck 	for (res = res0; res != NULL; res = res->ai_next) {
8898babcadSbeck 		if (addr == NULL && res->ai_family == AF_INET) {
8998babcadSbeck 			addr = (struct sockaddr_in *)res->ai_addr;
9098babcadSbeck 			break;
9198babcadSbeck 		}
9298babcadSbeck 	}
9398babcadSbeck 	if (addr == NULL) {
9498babcadSbeck 		freeaddrinfo(res0);
9598babcadSbeck 		return (EINVAL);
9698babcadSbeck 	}
9798babcadSbeck 	if ((shost = (struct sync_host *)
9898babcadSbeck 	    calloc(1, sizeof(struct sync_host))) == NULL) {
9998babcadSbeck 		freeaddrinfo(res0);
10098babcadSbeck 		return (ENOMEM);
10198babcadSbeck 	}
10298babcadSbeck 	if ((shost->h_name = strdup(name)) == NULL) {
10398babcadSbeck 		free(shost);
10498babcadSbeck 		freeaddrinfo(res0);
10598babcadSbeck 		return (ENOMEM);
10698babcadSbeck 	}
10798babcadSbeck 
108f6c3b21dSotto 	shost->sh_addr.sin_family = AF_INET;
109f6c3b21dSotto 	shost->sh_addr.sin_port = htons(port);
110f6c3b21dSotto 	shost->sh_addr.sin_addr.s_addr = addr->sin_addr.s_addr;
11198babcadSbeck 	freeaddrinfo(res0);
11298babcadSbeck 
11398babcadSbeck 	LIST_INSERT_HEAD(&sync_hosts, shost, h_entry);
11498babcadSbeck 
11598babcadSbeck 	if (debug)
11698babcadSbeck 		fprintf(stderr, "added spam sync host %s "
11798babcadSbeck 		    "(address %s, port %d)\n", shost->h_name,
118f6c3b21dSotto 		    inet_ntoa(shost->sh_addr.sin_addr), port);
11998babcadSbeck 
12098babcadSbeck 	return (0);
12198babcadSbeck }
12298babcadSbeck 
12398babcadSbeck int
12498babcadSbeck sync_init(const char *iface, const char *baddr, u_short port)
12598babcadSbeck {
12698babcadSbeck 	int one = 1;
12798babcadSbeck 	u_int8_t ttl;
12898babcadSbeck 	struct ifreq ifr;
12998babcadSbeck 	struct ip_mreq mreq;
13098babcadSbeck 	struct sockaddr_in *addr;
13198babcadSbeck 	char ifnam[IFNAMSIZ], *ttlstr;
13298babcadSbeck 	const char *errstr;
13398babcadSbeck 	struct in_addr ina;
13498babcadSbeck 
13598babcadSbeck 	if (iface != NULL)
13698babcadSbeck 		sendmcast++;
13798babcadSbeck 
13898babcadSbeck 	bzero(&ina, sizeof(ina));
13998babcadSbeck 	if (baddr != NULL) {
14098babcadSbeck 		if (inet_pton(AF_INET, baddr, &ina) != 1) {
14198babcadSbeck 			ina.s_addr = htonl(INADDR_ANY);
14298babcadSbeck 			if (iface == NULL)
14398babcadSbeck 				iface = baddr;
14498babcadSbeck 			else if (iface != NULL && strcmp(baddr, iface) != 0) {
14598babcadSbeck 				fprintf(stderr, "multicast interface does "
14698babcadSbeck 				    "not match");
14798babcadSbeck 				return (-1);
14898babcadSbeck 			}
14998babcadSbeck 		}
15098babcadSbeck 	}
15198babcadSbeck 
15298babcadSbeck 	sync_key = SHA1File(SPAM_SYNC_KEY, NULL);
15398babcadSbeck 	if (sync_key == NULL) {
15498babcadSbeck 		if (errno != ENOENT) {
15598babcadSbeck 			fprintf(stderr, "failed to open sync key: %s\n",
15698babcadSbeck 			    strerror(errno));
15798babcadSbeck 			return (-1);
15898babcadSbeck 		}
15998babcadSbeck 		/* Use empty key by default */
16098babcadSbeck 		sync_key = "";
16198babcadSbeck 	}
16298babcadSbeck 
16398babcadSbeck 	syncfd = socket(AF_INET, SOCK_DGRAM, 0);
16498babcadSbeck 	if (syncfd == -1)
16598babcadSbeck 		return (-1);
16698babcadSbeck 
16798babcadSbeck 	if (setsockopt(syncfd, SOL_SOCKET, SO_REUSEADDR, &one,
16898babcadSbeck 	    sizeof(one)) == -1)
16998babcadSbeck 		goto fail;
17098babcadSbeck 
17198babcadSbeck 	bzero(&sync_out, sizeof(sync_out));
17298babcadSbeck 	sync_out.sin_family = AF_INET;
17398babcadSbeck 	sync_out.sin_len = sizeof(sync_out);
17498babcadSbeck 	sync_out.sin_addr.s_addr = ina.s_addr;
17598babcadSbeck 	if (baddr == NULL && iface == NULL)
17698babcadSbeck 		sync_out.sin_port = 0;
17798babcadSbeck 	else
17898babcadSbeck 		sync_out.sin_port = htons(port);
17998babcadSbeck 
18098babcadSbeck 	if (bind(syncfd, (struct sockaddr *)&sync_out, sizeof(sync_out)) == -1)
18198babcadSbeck 		goto fail;
18298babcadSbeck 
18398babcadSbeck 	/* Don't use multicast messages */
18498babcadSbeck 	if (iface == NULL)
18598babcadSbeck 		return (syncfd);
18698babcadSbeck 
18798babcadSbeck 	strlcpy(ifnam, iface, sizeof(ifnam));
18898babcadSbeck 	ttl = SPAM_SYNC_MCASTTTL;
18998babcadSbeck 	if ((ttlstr = strchr(ifnam, ':')) != NULL) {
19098babcadSbeck 		*ttlstr++ = '\0';
19198babcadSbeck 		ttl = (u_int8_t)strtonum(ttlstr, 1, UINT8_MAX, &errstr);
19298babcadSbeck 		if (errstr) {
19398babcadSbeck 			fprintf(stderr, "invalid multicast ttl %s: %s",
19498babcadSbeck 			    ttlstr, errstr);
19598babcadSbeck 			goto fail;
19698babcadSbeck 		}
19798babcadSbeck 	}
19898babcadSbeck 
19998babcadSbeck 	bzero(&ifr, sizeof(ifr));
20098babcadSbeck 	strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
20198babcadSbeck 	if (ioctl(syncfd, SIOCGIFADDR, &ifr) == -1)
20298babcadSbeck 		goto fail;
20398babcadSbeck 
20498babcadSbeck 	bzero(&sync_in, sizeof(sync_in));
20598babcadSbeck 	addr = (struct sockaddr_in *)&ifr.ifr_addr;
20698babcadSbeck 	sync_in.sin_family = AF_INET;
20798babcadSbeck 	sync_in.sin_len = sizeof(sync_in);
20898babcadSbeck 	sync_in.sin_addr.s_addr = addr->sin_addr.s_addr;
20998babcadSbeck 	sync_in.sin_port = htons(port);
21098babcadSbeck 
21198babcadSbeck 	bzero(&mreq, sizeof(mreq));
21298babcadSbeck 	sync_out.sin_addr.s_addr = inet_addr(SPAM_SYNC_MCASTADDR);
21398babcadSbeck 	mreq.imr_multiaddr.s_addr = inet_addr(SPAM_SYNC_MCASTADDR);
21498babcadSbeck 	mreq.imr_interface.s_addr = sync_in.sin_addr.s_addr;
21598babcadSbeck 
21698babcadSbeck 	if (setsockopt(syncfd, IPPROTO_IP,
21798babcadSbeck 	    IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
21898babcadSbeck 		fprintf(stderr, "failed to add multicast membership to %s: %s",
21998babcadSbeck 		    SPAM_SYNC_MCASTADDR, strerror(errno));
22098babcadSbeck 		goto fail;
22198babcadSbeck 	}
22298babcadSbeck 	if (setsockopt(syncfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
22398babcadSbeck 	    sizeof(ttl)) < 0) {
22498babcadSbeck 		fprintf(stderr, "failed to set multicast ttl to "
22598babcadSbeck 		    "%u: %s\n", ttl, strerror(errno));
22698babcadSbeck 		setsockopt(syncfd, IPPROTO_IP,
22798babcadSbeck 		    IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
22898babcadSbeck 		goto fail;
22998babcadSbeck 	}
23098babcadSbeck 
23198babcadSbeck 	if (debug)
23298babcadSbeck 		printf("using multicast spam sync %smode "
23398babcadSbeck 		    "(ttl %u, group %s, port %d)\n",
23498babcadSbeck 		    sendmcast ? "" : "receive ",
23598babcadSbeck 		    ttl, inet_ntoa(sync_out.sin_addr), port);
23698babcadSbeck 
23798babcadSbeck 	return (syncfd);
23898babcadSbeck 
23998babcadSbeck  fail:
24098babcadSbeck 	close(syncfd);
24198babcadSbeck 	return (-1);
24298babcadSbeck }
24398babcadSbeck 
24498babcadSbeck void
24598babcadSbeck sync_recv(void)
24698babcadSbeck {
24798babcadSbeck 	struct spam_synchdr *hdr;
24898babcadSbeck 	struct sockaddr_in addr;
24998babcadSbeck 	struct spam_synctlv_hdr *tlv;
25098babcadSbeck 	struct spam_synctlv_grey *sg;
25198babcadSbeck 	struct spam_synctlv_addr *sd;
25298babcadSbeck 	u_int8_t buf[SPAM_SYNC_MAXSIZE];
25398babcadSbeck 	u_int8_t hmac[2][SPAM_SYNC_HMAC_LEN];
25498babcadSbeck 	struct in_addr ip;
25598babcadSbeck 	char *from, *to, *helo;
25698babcadSbeck 	u_int8_t *p;
25798babcadSbeck 	socklen_t addr_len;
25898babcadSbeck 	ssize_t len;
25998babcadSbeck 	u_int hmac_len;
26098babcadSbeck 	time_t expire;
26198babcadSbeck 
26298babcadSbeck 	bzero(&addr, sizeof(addr));
26398babcadSbeck 	bzero(buf, sizeof(buf));
26498babcadSbeck 
26598babcadSbeck 	addr_len = sizeof(addr);
26698babcadSbeck 	if ((len = recvfrom(syncfd, buf, sizeof(buf), 0,
26798babcadSbeck 	    (struct sockaddr *)&addr, &addr_len)) < 1)
26898babcadSbeck 		return;
26998babcadSbeck 	if (addr.sin_addr.s_addr != htonl(INADDR_ANY) &&
27098babcadSbeck 	    bcmp(&sync_in.sin_addr, &addr.sin_addr,
27198babcadSbeck 	    sizeof(addr.sin_addr)) == 0)
27298babcadSbeck 		return;
27398babcadSbeck 
27498babcadSbeck 	/* Ignore invalid or truncated packets */
27598babcadSbeck 	hdr = (struct spam_synchdr *)buf;
276544df9d5Sreyk 	if (len < sizeof(struct spam_synchdr) ||
277544df9d5Sreyk 	    hdr->sh_version != SPAM_SYNC_VERSION ||
27898babcadSbeck 	    hdr->sh_af != AF_INET ||
27998babcadSbeck 	    len < ntohs(hdr->sh_length))
28098babcadSbeck 		goto trunc;
28198babcadSbeck 	len = ntohs(hdr->sh_length);
28298babcadSbeck 
28398babcadSbeck 	/* Compute and validate HMAC */
28498babcadSbeck 	bcopy(hdr->sh_hmac, hmac[0], SPAM_SYNC_HMAC_LEN);
28598babcadSbeck 	bzero(hdr->sh_hmac, SPAM_SYNC_HMAC_LEN);
28698babcadSbeck 	HMAC(EVP_sha1(), sync_key, strlen(sync_key), buf, len,
28798babcadSbeck 	    hmac[1], &hmac_len);
28898babcadSbeck 	if (bcmp(hmac[0], hmac[1], SPAM_SYNC_HMAC_LEN) != 0)
28998babcadSbeck 		goto trunc;
29098babcadSbeck 
29198babcadSbeck 	if (debug)
29298babcadSbeck 		fprintf(stderr,
29398babcadSbeck 		    "%s(sync): received packet of %d bytes\n",
29498babcadSbeck 		    inet_ntoa(addr.sin_addr), (int)len);
29598babcadSbeck 
29698babcadSbeck 	p = (u_int8_t *)(hdr + 1);
29798babcadSbeck 	while (len) {
29898babcadSbeck 		tlv = (struct spam_synctlv_hdr *)p;
29998babcadSbeck 
300fcca1eedSreyk 		if (len < sizeof(struct spam_synctlv_hdr) ||
301fcca1eedSreyk 		    len < ntohs(tlv->st_length))
30298babcadSbeck 			goto trunc;
30398babcadSbeck 
30498babcadSbeck 		switch (ntohs(tlv->st_type)) {
30598babcadSbeck 		case SPAM_SYNC_GREY:
30698babcadSbeck 			sg = (struct spam_synctlv_grey *)tlv;
30798babcadSbeck 			if ((sizeof(*sg) +
30898babcadSbeck 			    ntohs(sg->sg_from_length) +
30998babcadSbeck 			    ntohs(sg->sg_to_length) +
31098babcadSbeck 			    ntohs(sg->sg_helo_length)) >
31198babcadSbeck 			    ntohs(tlv->st_length))
31298babcadSbeck 				goto trunc;
31398babcadSbeck 
314d72b910dSderaadt 			ip.s_addr = sg->sg_ip;
31598babcadSbeck 			from = (char *)(sg + 1);
31698babcadSbeck 			to = from + ntohs(sg->sg_from_length);
31798babcadSbeck 			helo = to + ntohs(sg->sg_to_length);
31898babcadSbeck 			if (debug) {
31998babcadSbeck 				fprintf(stderr, "%s(sync): "
32098babcadSbeck 				    "received grey entry ",
32198babcadSbeck 				    inet_ntoa(addr.sin_addr));
32298babcadSbeck 				fprintf(stderr, "helo %s ip %s "
32398babcadSbeck 				    "from %s to %s\n",
32498babcadSbeck 				    helo, inet_ntoa(ip), from, to);
32598babcadSbeck 			}
32698babcadSbeck 			if (greylist) {
32798babcadSbeck 				/* send this info to the greylister */
32898babcadSbeck 				fprintf(grey,
32998babcadSbeck 				    "SYNC\nHE:%s\nIP:%s\nFR:%s\nTO:%s\n",
33098babcadSbeck 				    helo, inet_ntoa(ip), from, to);
33198babcadSbeck 				fflush(grey);
33298babcadSbeck 			}
33398babcadSbeck 			break;
33498babcadSbeck 		case SPAM_SYNC_WHITE:
33598babcadSbeck 			sd = (struct spam_synctlv_addr *)tlv;
33698babcadSbeck 			if (sizeof(*sd) != ntohs(tlv->st_length))
33798babcadSbeck 				goto trunc;
33898babcadSbeck 
339d72b910dSderaadt 			ip.s_addr = sd->sd_ip;
34098babcadSbeck 			expire = ntohl(sd->sd_expire);
34198babcadSbeck 			if (debug) {
34298babcadSbeck 				fprintf(stderr, "%s(sync): "
34398babcadSbeck 				    "received white entry ",
34498babcadSbeck 				    inet_ntoa(addr.sin_addr));
34598babcadSbeck 				fprintf(stderr, "ip %s ", inet_ntoa(ip));
34698babcadSbeck 			}
34798babcadSbeck 			if (greylist) {
34898babcadSbeck 				/* send this info to the greylister */
34998babcadSbeck 				fprintf(grey, "WHITE:%s:", inet_ntoa(ip));
35098babcadSbeck 				fprintf(grey, "%s:%u\n",
35198babcadSbeck 				    inet_ntoa(addr.sin_addr), expire);
35298babcadSbeck 				fflush(grey);
35398babcadSbeck 			}
35498babcadSbeck 			break;
35598babcadSbeck 		case SPAM_SYNC_TRAPPED:
35698babcadSbeck 			sd = (struct spam_synctlv_addr *)tlv;
35798babcadSbeck 			if (sizeof(*sd) != ntohs(tlv->st_length))
35898babcadSbeck 				goto trunc;
35998babcadSbeck 
360d72b910dSderaadt 			ip.s_addr = sd->sd_ip;
36198babcadSbeck 			expire = ntohl(sd->sd_expire);
36298babcadSbeck 			if (debug) {
36398babcadSbeck 				fprintf(stderr, "%s(sync): "
36498babcadSbeck 				    "received trapped entry ",
36598babcadSbeck 				    inet_ntoa(addr.sin_addr));
36698babcadSbeck 				fprintf(stderr, "ip %s ", inet_ntoa(ip));
36798babcadSbeck 			}
36898babcadSbeck 			if (greylist) {
36998babcadSbeck 				/* send this info to the greylister */
37098babcadSbeck 				fprintf(grey, "TRAP:%s:", inet_ntoa(ip));
37198babcadSbeck 				fprintf(grey, "%s:%u\n",
37298babcadSbeck 				    inet_ntoa(addr.sin_addr), expire);
37398babcadSbeck 				fflush(grey);
37498babcadSbeck 			}
37598babcadSbeck 			break;
37698babcadSbeck 		case SPAM_SYNC_END:
37798babcadSbeck 			goto done;
37898babcadSbeck 		default:
37998babcadSbeck 			printf("invalid type: %d\n", ntohs(tlv->st_type));
38098babcadSbeck 			goto trunc;
38198babcadSbeck 		}
38298babcadSbeck 		len -= ntohs(tlv->st_length);
38398babcadSbeck 		p = ((u_int8_t *)tlv) + ntohs(tlv->st_length);
38498babcadSbeck 	}
38598babcadSbeck 
38698babcadSbeck  done:
38798babcadSbeck 	return;
38898babcadSbeck 
38998babcadSbeck  trunc:
39098babcadSbeck 	if (debug)
39198babcadSbeck 		fprintf(stderr, "%s(sync): truncated or invalid packet\n",
39298babcadSbeck 		    inet_ntoa(addr.sin_addr));
39398babcadSbeck }
39498babcadSbeck 
39598babcadSbeck void
39698babcadSbeck sync_send(struct iovec *iov, int iovlen)
39798babcadSbeck {
39898babcadSbeck 	struct sync_host *shost;
39998babcadSbeck 	struct msghdr msg;
40098babcadSbeck 
40198babcadSbeck 	/* setup buffer */
40298babcadSbeck 	bzero(&msg, sizeof(msg));
40398babcadSbeck 	msg.msg_iov = iov;
40498babcadSbeck 	msg.msg_iovlen = iovlen;
40598babcadSbeck 
40698babcadSbeck 	if (sendmcast) {
40798babcadSbeck 		if (debug)
40898babcadSbeck 			fprintf(stderr, "sending multicast sync message\n");
40998babcadSbeck 		msg.msg_name = &sync_out;
41098babcadSbeck 		msg.msg_namelen = sizeof(sync_out);
41198babcadSbeck 		sendmsg(syncfd, &msg, 0);
41298babcadSbeck 	}
41398babcadSbeck 
41498babcadSbeck 	LIST_FOREACH(shost, &sync_hosts, h_entry) {
41598babcadSbeck 		if (debug)
41698babcadSbeck 			fprintf(stderr, "sending sync message to %s (%s)\n",
417f6c3b21dSotto 			    shost->h_name, inet_ntoa(shost->sh_addr.sin_addr));
418f6c3b21dSotto 		msg.msg_name = &shost->sh_addr;
419f6c3b21dSotto 		msg.msg_namelen = sizeof(shost->sh_addr);
42098babcadSbeck 		sendmsg(syncfd, &msg, 0);
42198babcadSbeck 	}
42298babcadSbeck }
42398babcadSbeck 
42498babcadSbeck void
42598babcadSbeck sync_update(time_t now, char *helo, char *ip, char *from, char *to)
42698babcadSbeck {
4274aba4ff9Sderaadt 	struct iovec iov[7];
42898babcadSbeck 	struct spam_synchdr hdr;
42998babcadSbeck 	struct spam_synctlv_grey sg;
43098babcadSbeck 	struct spam_synctlv_hdr end;
4314aba4ff9Sderaadt 	u_int16_t sglen, fromlen, tolen, helolen, padlen;
4324aba4ff9Sderaadt 	char pad[SPAM_ALIGNBYTES];
43398babcadSbeck 	int i = 0;
43498babcadSbeck 	HMAC_CTX ctx;
43598babcadSbeck 	u_int hmac_len;
43698babcadSbeck 
43798babcadSbeck 	if (debug)
43898babcadSbeck 		fprintf(stderr,
43998babcadSbeck 		    "sync grey update helo %s ip %s from %s to %s\n",
44098babcadSbeck 		    helo, ip, from, to);
44198babcadSbeck 
44298babcadSbeck 	bzero(&hdr, sizeof(hdr));
44398babcadSbeck 	bzero(&sg, sizeof(sg));
4444aba4ff9Sderaadt 	bzero(&pad, sizeof(pad));
44598babcadSbeck 
44698babcadSbeck 	fromlen = strlen(from) + 1;
44798babcadSbeck 	tolen = strlen(to) + 1;
44898babcadSbeck 	helolen = strlen(helo) + 1;
44998babcadSbeck 
45098babcadSbeck 	HMAC_CTX_init(&ctx);
45198babcadSbeck 	HMAC_Init(&ctx, sync_key, strlen(sync_key), EVP_sha1());
45298babcadSbeck 
4534aba4ff9Sderaadt 	sglen = sizeof(sg) + fromlen + tolen + helolen;
4544aba4ff9Sderaadt 	padlen = SPAM_ALIGN(sglen) - sglen;
4554aba4ff9Sderaadt 
45698babcadSbeck 	/* Add SPAM sync packet header */
45798babcadSbeck 	hdr.sh_version = SPAM_SYNC_VERSION;
45898babcadSbeck 	hdr.sh_af = AF_INET;
459d72b910dSderaadt 	hdr.sh_counter = htonl(sync_counter++);
4604aba4ff9Sderaadt 	hdr.sh_length = htons(sizeof(hdr) + sglen + padlen + sizeof(end));
46198babcadSbeck 	iov[i].iov_base = &hdr;
46298babcadSbeck 	iov[i].iov_len = sizeof(hdr);
46398babcadSbeck 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
46498babcadSbeck 	i++;
46598babcadSbeck 
46698babcadSbeck 	/* Add single SPAM sync greylisting entry */
46798babcadSbeck 	sg.sg_type = htons(SPAM_SYNC_GREY);
4684aba4ff9Sderaadt 	sg.sg_length = htons(sglen + padlen);
46998babcadSbeck 	sg.sg_timestamp = htonl(now);
470d72b910dSderaadt 	sg.sg_ip = inet_addr(ip);
47198babcadSbeck 	sg.sg_from_length = htons(fromlen);
47298babcadSbeck 	sg.sg_to_length = htons(tolen);
47398babcadSbeck 	sg.sg_helo_length = htons(helolen);
47498babcadSbeck 	iov[i].iov_base = &sg;
47598babcadSbeck 	iov[i].iov_len = sizeof(sg);
47698babcadSbeck 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
47798babcadSbeck 	i++;
47898babcadSbeck 
47998babcadSbeck 	iov[i].iov_base = from;
48098babcadSbeck 	iov[i].iov_len = fromlen;
48198babcadSbeck 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
48298babcadSbeck 	i++;
48398babcadSbeck 
48498babcadSbeck 	iov[i].iov_base = to;
48598babcadSbeck 	iov[i].iov_len = tolen;
48698babcadSbeck 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
48798babcadSbeck 	i++;
48898babcadSbeck 
48998babcadSbeck 	iov[i].iov_base = helo;
49098babcadSbeck 	iov[i].iov_len = helolen;
49198babcadSbeck 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
49298babcadSbeck 	i++;
49398babcadSbeck 
4944aba4ff9Sderaadt 	iov[i].iov_base = pad;
4954aba4ff9Sderaadt 	iov[i].iov_len = padlen;
4964aba4ff9Sderaadt 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
4974aba4ff9Sderaadt 	i++;
4984aba4ff9Sderaadt 
49998babcadSbeck 	/* Add end marker */
50098babcadSbeck 	end.st_type = htons(SPAM_SYNC_END);
50198babcadSbeck 	end.st_length = htons(sizeof(end));
50298babcadSbeck 	iov[i].iov_base = &end;
50398babcadSbeck 	iov[i].iov_len = sizeof(end);
50498babcadSbeck 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
50598babcadSbeck 	i++;
50698babcadSbeck 
50798babcadSbeck 	HMAC_Final(&ctx, hdr.sh_hmac, &hmac_len);
50898babcadSbeck 
50998babcadSbeck 	/* Send message to the target hosts */
51098babcadSbeck 	sync_send(iov, i);
5111d936ddfSbeck 	HMAC_CTX_cleanup(&ctx);
51298babcadSbeck }
51398babcadSbeck 
51498babcadSbeck void
51598babcadSbeck sync_addr(time_t now, time_t expire, char *ip, u_int16_t type)
51698babcadSbeck {
51798babcadSbeck 	struct iovec iov[3];
51898babcadSbeck 	struct spam_synchdr hdr;
51998babcadSbeck 	struct spam_synctlv_addr sd;
52098babcadSbeck 	struct spam_synctlv_hdr end;
52198babcadSbeck 	int i = 0;
52298babcadSbeck 	HMAC_CTX ctx;
52398babcadSbeck 	u_int hmac_len;
52498babcadSbeck 
52598babcadSbeck 	if (debug)
526*00ddf0caSbeck 		fprintf(stderr, "sync %s %s\n",
527*00ddf0caSbeck 			type == SPAM_SYNC_WHITE ? "white" : "trapped", ip);
52898babcadSbeck 
52998babcadSbeck 	bzero(&hdr, sizeof(hdr));
53098babcadSbeck 	bzero(&sd, sizeof(sd));
53198babcadSbeck 
53298babcadSbeck 	HMAC_CTX_init(&ctx);
53398babcadSbeck 	HMAC_Init(&ctx, sync_key, strlen(sync_key), EVP_sha1());
53498babcadSbeck 
53598babcadSbeck 	/* Add SPAM sync packet header */
53698babcadSbeck 	hdr.sh_version = SPAM_SYNC_VERSION;
53798babcadSbeck 	hdr.sh_af = AF_INET;
538d72b910dSderaadt 	hdr.sh_counter = htonl(sync_counter++);
53998babcadSbeck 	hdr.sh_length = htons(sizeof(hdr) + sizeof(sd) + sizeof(end));
54098babcadSbeck 	iov[i].iov_base = &hdr;
54198babcadSbeck 	iov[i].iov_len = sizeof(hdr);
54298babcadSbeck 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
54398babcadSbeck 	i++;
54498babcadSbeck 
54598babcadSbeck 	/* Add single SPAM sync address entry */
54698babcadSbeck 	sd.sd_type = htons(type);
54798babcadSbeck 	sd.sd_length = htons(sizeof(sd));
54898babcadSbeck 	sd.sd_timestamp = htonl(now);
54998babcadSbeck 	sd.sd_expire = htonl(expire);
550d72b910dSderaadt 	sd.sd_ip = inet_addr(ip);
55198babcadSbeck 	iov[i].iov_base = &sd;
55298babcadSbeck 	iov[i].iov_len = sizeof(sd);
55398babcadSbeck 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
55498babcadSbeck 	i++;
55598babcadSbeck 
55698babcadSbeck 	/* Add end marker */
55798babcadSbeck 	end.st_type = htons(SPAM_SYNC_END);
55898babcadSbeck 	end.st_length = htons(sizeof(end));
55998babcadSbeck 	iov[i].iov_base = &end;
56098babcadSbeck 	iov[i].iov_len = sizeof(end);
56198babcadSbeck 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
56298babcadSbeck 	i++;
56398babcadSbeck 
56498babcadSbeck 	HMAC_Final(&ctx, hdr.sh_hmac, &hmac_len);
56598babcadSbeck 
56698babcadSbeck 	/* Send message to the target hosts */
56798babcadSbeck 	sync_send(iov, i);
5681d936ddfSbeck 	HMAC_CTX_cleanup(&ctx);
56998babcadSbeck }
57098babcadSbeck 
57198babcadSbeck void
57298babcadSbeck sync_white(time_t now, time_t expire, char *ip)
57398babcadSbeck {
57498babcadSbeck 	sync_addr(now, expire, ip, SPAM_SYNC_WHITE);
57598babcadSbeck }
57698babcadSbeck 
57798babcadSbeck void
57898babcadSbeck sync_trapped(time_t now, time_t expire, char *ip)
57998babcadSbeck {
58098babcadSbeck 	sync_addr(now, expire, ip, SPAM_SYNC_TRAPPED);
58198babcadSbeck }
582