/*
*
*              Reverse MX Filter for Postfix 2
*                    reject_bad_rmx
*           (c) 2004 Elita rozanski@sergiusz.com
*
*/

/*
MXfilter is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.
*/

/*
This file is a modify part of public message:

To: sslug-cprog@sslug.dk 
Subject: Re: [CPROG] DNS query p. MX record. 
From: "Erwin S. Andreasen" <erw@dde.dk.sslug.dk> 
Date: Mon, 25 Oct 1999 11:51:52 +0200 (CEST) 
Delivered-To: mailing list sslug-cprog@sslug.dk 
In-Reply-To: <381364DB.48919E5@risoe.dk> 
Mailing-List: contact sslug-cprog-help@sslug.dk; run by ezmlm 

http://www.sslug.dk/emailarkiv/cprog/1999_10/msg00056.html
*/


#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <ctype.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <arpa/inet.h>

#include "mxget.h"
#include "utils.h"

/* Mailhost lookup stuff

 * See RFCs 1034 and 1035 for details of these structures.
 */

/* blah.. we have to build our own structures here since nameser.h doesn't
 * have a decent RR structure
 */

struct rrecord {
    int16_t r_type;
    int16_t r_class;
    u_int32_t r_ttl;
    int16_t r_length;
};

/* find mxdomain for domain */
/* return -2 dns noerror host not found */
/* return -1 dns error */
/* return 0 OK */
/* return 1 FALSE */

int find_mailhost(char *domain, char *client_ip, char *net, int tmode, char *prefix, int *querycount, int querymax) {
    static char out[1000];
    unsigned char query[1000];
    HEADER *h;
    unsigned char *p;
    int qcount, acount;
    char buf[1000];
    struct rrecord *r;
    int i;

    struct hostent *hh;
    int result=1;

    /* ask libc to do the query:
     * look up 'domain'
     * class C_IN (internet)
     * type  T_MX (mail exchanger record)
     * record results in query, max len 1000
     */
    
    querycount[0]++;
    if (querycount[0]>querymax)
	return -1;
    if (res_search(domain, C_IN, T_MX, query, 1000) <= 0) {
	if(h_errno == HOST_NOT_FOUND || 
		h_errno == TRY_AGAIN || 
		h_errno == NO_DATA)
	    return (-2);
	return -1; 
	}
	
    h = (HEADER *) query;

    /* skip over queries in packet */
    p = query + sizeof(HEADER);
    qcount = ntohs(h->qdcount);	/* # of queries present */
    acount = ntohs(h->ancount);	/* # of answers present */
    /* get past queries */
    while (qcount--) {
	p += dn_expand(query, query + 1000, p, buf, 1000);
	p += 4;			/* qclass, qtype */
    }

    /* now we are in the answers section. Just get the best exchanger,
     * we don't want to spend too much time messing around
     */
    out[0] = 0;
    while (acount--) {
	/* name that this answer refers to */
	p += dn_expand(query, query + 1000, p, buf, 1000);

	r = (struct rrecord *) p;
	if (htons(r->r_type) == T_MX) {		/* MX record */
	    int pref;
	    unsigned char *p1 = p + 10;

	    pref = ntohs(*(int16_t *) p1);	/* preference */
	    p1 += 2;
	    dn_expand(query, query + 1000, p1, out, 1000);

	    querycount[0]++;
	    if (querycount[0]>querymax)
		return -1;
	    hh=gethostbyname(out);
	    if(!hh) {
		if(h_errno==TRY_AGAIN || h_errno==NO_RECOVERY)
        	return (-1); //dns error
	        }
	    else {
	    for(i=0;i<256;i++) {
		char odb[256];
		if(hh->h_addr_list[i]) {
        	    snprintf(odb,sizeof(odb),"%d.%d.%d.%d",(unsigned char)hh->h_addr_list[i][0],
	        	    (unsigned char)hh->h_addr_list[i][1],
			    (unsigned char)hh->h_addr_list[i][2],
			    (unsigned char)hh->h_addr_list[i][3]);
		    if (!tmode) {
			if(check_ip4(odb,client_ip,net)==0) result=0;
			}
		    else {
			printf("[mx%s] %s/%s\n",prefix,odb,net);
			}
		    }
		else {
		    i=256;
		    }
		}
		}
	    }
	p = p + 10 + ntohs(r->r_length);	/* skip RR header and data */
    }

return result;
}

