/*
 * selfaddresses() routines handle recognition of our own IP addresses,
 * so that we won't talk with ourself from smtp sender, and recognize,
 * when the SMTP traget is ourself, even when using alternate IP
 * addresses that are not matched with our own name.
 *
 * There are damn varying methods on how to do this, thus we rely on
 * system configurer to do the right thing, and list them at the
 * ZENV file  SELFADDRESSES= -entry  as a string of style:
 *	"1.2.3.4,6.7.8.9,::1.2.3.4,my.domain.name,"
 *              ^-------^---------^----------  commas to separate them!
 *
 * In theory we COULD use  SIOCGIFCONF, but it is somewhat open-ended
 * beast with its own troubles..
 */

#include "hostenv.h"
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

extern char *getzenv __((const char *));

/* hostent.c */
extern void hp_init __((struct hostent *hp));
extern void hp_setalist __((struct hostent *hp, void **));
extern char **hp_getaddr __((void));
extern char **hp_nextaddr __((void));
extern void hp_addr_randomize __((struct hostent *hp));


int nmyaddrs = 0;
char *myaddrs = NULL;


static void
stachmyaddress(host)
	char *host;
{
	int naddrs;
	struct hostent *hp, hent;
	struct in_addr ia;
	void *addrs[2];
	const int addrlen = 4; /* IPv4 length.. */
	int nmyaddr0;

	if (host == NULL) return;

	hp = gethostbyname(host);

	if (hp == NULL) { /* No such host ?? */
	  ia.s_addr = inet_addr(host);
	  if (ia.s_addr == 0xFFFFFFFF)
	    return; /* Bad address */
	  hp = &hent;
	  /* don't really care about gethostbyaddr() here */
	  hp->h_name = host;
	  hp->h_addrtype = AF_INET;
	  hp->h_aliases = NULL;
	  hp->h_length = sizeof(ia);
	  addrs[0] = (void *)&ia;
	  addrs[1] = NULL;
	  hp_setalist(hp, addrs);
	  naddrs = 1;
	} else {
	  naddrs = 0;
	  for (hp_init(hp); *hp_getaddr() != NULL; hp_nextaddr())
	    ++naddrs;
	}
	nmyaddr0 = nmyaddrs;
	nmyaddrs += naddrs;
	if (myaddrs == NULL)
	  myaddrs = malloc(nmyaddrs * addrlen);
	else
	  myaddrs = realloc(myaddrs, nmyaddrs * addrlen);

	for (hp_init(hp); *hp_getaddr() != NULL; hp_nextaddr())
	  memcpy(&myaddrs[(nmyaddr0 + --naddrs) * hp->h_length],
		 *hp_getaddr(),
		 hp->h_length);
}

void
stachmyaddresses(host)
char *host;
{
	char *s1, *s2, *zenv;

	zenv = getzenv("SELFADDRESSES");

	stachmyaddress(host);

	s1 = zenv;
	while (s1) {
	  s2 = strchr(s1,',');
	  if (s2) *s2 = 0;
	  stachmyaddress(s1);
	  if (s2) *s2 = ',';
	  if (s2)
	    s1 = s2+1;
	  else
	    s1 = NULL;
	}
}


int
matchmyaddress(addr,len)
	char *addr;
	int len;
{
	int i;
	unsigned int ia; /* 32 bit address */

	if (!myaddrs) return 0; /* Don't know my addresses ! */
	
	for (i = 0; i < nmyaddrs; ++i) {
	  /* if this is myself, skip to next MX host */
	  if (memcmp(addr, &myaddrs[i*len],len) == 0)
	    return 1;
	}
	/* Also match loopback net.. */
	if (len == 4) {
	  memcpy(&ia,addr,4);
#ifndef INADDR_LOOPBACK /* FreeBSD does not have this in  <netinet/in.h> ! */
# define INADDR_LOOPBACK 0x7f000001
#endif
#define INADDR_LOOPBACKNET (IN_CLASSA_NET & INADDR_LOOPBACK)

	  if ((ntohl(ia) & IN_CLASSA_NET) == INADDR_LOOPBACKNET) {
	    return 2;
	  }
	}
	return 0;
}

int
matchmyaddresses(hp)
	struct hostent *hp;
{
	char **alist;
	int i;
#ifndef	h_addr	/* no h_addr macro -- presumably 4.2BSD or earlier.. */
	char *fakealist[2];

	alist = fakealist;
	fakealist[0] = hp->h_addr;
	fakealist[1] = NULL;
#else
	alist = hp->h_addr_list;
#endif
	while (alist && *alist) {
	  if ((i = matchmyaddress(*alist,hp->h_length)))
	    return i;
	  ++alist;
	}
	return 0;
}
