/*
*
*              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.
*/

#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 "spfptr.h"
#include "utils.h"

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

/* find ptr for spf1 */
/* return -1 dns error */
/* return 0 OK */
/* return 1 FALSE */

int find_ptr(char *domain, char *client_ip, 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;
    char ptrr[1024];
    char rot[1024];
    char cmpdomain[512];

    rev_addr(client_ip, rot, sizeof(rot));
    snprintf(cmpdomain,sizeof(cmpdomain),".%s|",domain);
    
    snprintf(ptrr,sizeof(ptrr),"%s.in-addr.arpa",rot);
//    printf("find: %s\n",ptrr);

    querycount[0]++;
    if (querycount[0]>querymax)
            return -1;
    if (res_search(ptrr, C_IN, T_PTR, query, 1000) < 0) {
        if(h_errno == HOST_NOT_FOUND ||
                h_errno == TRY_AGAIN ||
                h_errno == NO_DATA)
            return 1;
        return -1;
        }
									
    h = (HEADER *) query;

    p = query + sizeof(HEADER);
    qcount = ntohs(h->qdcount);
    acount = ntohs(h->ancount);

    while (qcount--) {
	p += dn_expand(query, query + 1000, p, buf, 1000);
	p += 4;
    }

    out[0] = 0;
    while (acount--) {

	p += dn_expand(query, query + 1000, p, buf, 1000);

	r = (struct rrecord *) p;
	if (htons(r->r_type) == T_PTR) {
	    unsigned char *p1 = p + 10;

	    dn_expand(query, query + 1000, p1, out, 1000);
	    
//	    printf("%s\n",out);

	    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 (check_ip4(odb,client_ip,"32")==0) {
			char cmpout[512];
			
			snprintf(cmpout,sizeof(cmpout),".%s|",out);
			if (strstr(cmpout,cmpdomain)) {
			    if(!tmode)
				return 0; //pass
			    else
				printf("[ptr%s] %s %s pass\n",prefix,domain,out);
			    }
			else {
			    if(tmode)
				printf("[ptr%s] %s %s fail\n",prefix,domain,out);
			    }
			}
		    }
		else {
		    i=256;
		    }
		}
		}

	    }
	p = p + 10 + ntohs(r->r_length);
    }

return result;
}

/* get ptr */
/* return -1 dns error */
/* return 0 OK */

int get_ptr(char *client_ip, char *domain, int sizeofdomain) {
    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;
    char ptrr[1024];
    char rot[1024];

    rev_addr(client_ip, rot, sizeof(rot));
    
    snprintf(ptrr,sizeof(ptrr),"%s.in-addr.arpa",rot);

    if (res_search(ptrr, C_IN, T_PTR, query, 1000) < 0) {
        if(h_errno == HOST_NOT_FOUND ||
                h_errno == TRY_AGAIN ||
                h_errno == NO_DATA)
            return 1;
        return -1;
        }
									
    h = (HEADER *) query;

    p = query + sizeof(HEADER);
    qcount = ntohs(h->qdcount);
    acount = ntohs(h->ancount);

    while (qcount--) {
	p += dn_expand(query, query + 1000, p, buf, 1000);
	p += 4;
    }

    out[0] = 0;
    while (acount--) {

	p += dn_expand(query, query + 1000, p, buf, 1000);

	r = (struct rrecord *) p;
	if (htons(r->r_type) == T_PTR) {
	    unsigned char *p1 = p + 10;

	    dn_expand(query, query + 1000, p1, out, 1000);
	    
	    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 (check_ip4(odb,client_ip,"32")==0) {
			snprintf(domain,sizeofdomain,"%s",out);
			return 0;
			}
		    }
		else {
		    i=256;
		    }
		}
		}

	    }
	p = p + 10 + ntohs(r->r_length);
    }

return result;
}
