/*
*
*              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 <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <resolv.h>
#include <arpa/nameser.h>
#include <stdio.h>
#include <netdb.h>
#include <syslog.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/utsname.h>

#include "utils.h"
#include "mxget.h"
#include "fecyk.h"
#include "acl.h"
#include "spfptr.h"
#include "spfmacro.h"
#include "spf1.h"
#include "sign.h"

char matchs[7][11]={"default","spf1","dmp","user.relay","relay","a","mx"};

int test_spf=1;
int test_dmp=0;
int test_a=0;
int test_mx=0;
int default_result=2; //unknown
int dunno_on_error=0;
int dunno_on_fail=0;
int fail_on_softfail=0;
int skip_helo_check=0;
int ok_on_pass=0;
int prepend_spf_after_relay=0;
int querycount=0;
int querymax=1000;
int procmail_skip_line=0;

char message_fail[1024];
char message_acl_error[1024];
char message_dns_error[1024];
char message_fail_on_unknown[1024];
char myhostname[257];

int load_config (char *plik) {
    FILE *kk;
    char linia[1000],*k,*sp;
    
    kk=fopen(plik,"r");
    if(!kk)return 0;
    while(fgets(linia,sizeof(linia)-1,kk)) {
	k=(char *)index(linia,10);
	if(k)k[0]=0;
	k=(char *)index(linia,13);
	if(k)k[0]=0;
    
	if(strlen(linia)>0) {
	    if(linia[0]!='#') {
		k=(char *)index(linia,9);
		sp=(char *)index(linia,32);
		if(k && sp)
		    if(sp<k)k=sp;
		if(!k && sp)k=sp;
		if(k) {
		    k++[0]=0;
		    if(strcmp(linia,"prepend_spf_after_relay")==0) {
			prepend_spf_after_relay=atoi(k);
			}
		    else
		    if(strcmp(linia,"myhostname")==0) {
			snprintf(myhostname,sizeof(myhostname),"%s",k);
			}
		    else
		    if(strcmp(linia,"skip_helo_check")==0) {
			skip_helo_check=atoi(k);
			}
		    else
		    if(strcmp(linia,"dunno_on_error")==0) {
			dunno_on_error=atoi(k);
			}
		    else
		    if(strcmp(linia,"message_fail")==0) {
			snprintf(message_fail,sizeof(message_fail),"%s",k);
			}
		    else
		    if(strcmp(linia,"message_acl_error")==0) {
			snprintf(message_acl_error,sizeof(message_acl_error),"%s",k);
			}
		    else
		    if(strcmp(linia,"message_dns_error")==0) {
			snprintf(message_dns_error,sizeof(message_dns_error),"%s",k);
			}
		    else
		    if(strcmp(linia,"message_fail_on_unknown")==0) {
			snprintf(message_fail_on_unknown,sizeof(message_fail_on_unknown),"%s",k);
			}
		    else
		    if(strcmp(linia,"fail_on_softfail")==0) {
			fail_on_softfail=atoi(k);
			}
		    else
		    if(strcmp(linia,"procmail_skip_line")==0) {
			procmail_skip_line=atoi(k);
			}
		    else
		    if(strcmp(linia,"ok_on_pass")==0) {
			ok_on_pass=atoi(k);
			}
		    else
		    if(strcmp(linia,"dunno_on_fail")==0) {
			dunno_on_fail=atoi(k);
			}
		    else
		    if(strcmp(linia,"test_spf")==0) {
			test_spf=atoi(k);
			}
		    else
		    if(strcmp(linia,"test_dmp")==0) {
			test_dmp=atoi(k);
			}
		    else
		    if(strcmp(linia,"test_a")==0) {
			test_a=atoi(k);
			}
		    else
		    if(strcmp(linia,"test_mx")==0) {
			test_mx=atoi(k);
			}
		    else
		    if(strcmp(linia,"default_result")==0) {
			default_result=atoi(k);
			}
		    else
		    if(strcmp(linia,"dns_query_limit")==0) {
			querymax=atoi(k);
			}
		    }
		}
	    }
	}
    fclose(kk);
    return 0;
    }

/*
-1 dns error
0  pass
1  fail
2  unknown
3  softfail
*/

int check_mx(char *sender,char *client_address,int tolerance, int *match, int tmode, struct tspfm *spfm, 
	char *spf_exp_res, int spf_sizeof_exp_res) {
struct hostent *hh;
int i,f;
char *domain;
char user[1024];
char fquery[1024];

if (!index(sender,'@')) return 2; //invalid email address
domain=index(sender,'@')+1;
if (strlen(domain)<3) return 2; //domain too short

if (domain[0]=='[' && domain[strlen(domain)-1]==']') return 2; //ip in domain (todo: helo)

snprintf(user,sizeof(user),"%s",sender);
index(user,'@')[0]=0;

if (strlen(user)==0)return 2; //username too short

*match=0;

/* spf1 */
if(test_spf) {
    if(tmode)printf("Test spf1:\n");
    *match=1;
    f=check_spf1(domain, client_address,tmode, spfm, spf_exp_res, spf_sizeof_exp_res, &querycount, querymax);

    if(!tmode) {
	if(f==-1) return -1; //dns error
	if(f==0) return 0; //ok dunno
	if(f==1) return 1; //fail
    //if(f==2) return 2; //unknown (continue)
	if(f==3) return 3; //softfail
	};
    };
    
/* simple fecyk query (ehlo ignored)*/
if(test_dmp) {
    if(tmode)printf("Test dmp:\n");
    *match=2;
    snprintf(fquery,sizeof(fquery),"_smtp-client.%s",domain);
    f=find_dmp(fquery);
    querycount++;
    if (querycount>querymax)
        return -1;

    if(!tmode)
	if(f==-1) return (-1); //dns error
    if(f==0) { //domain has dmp records
	char rev_ca[100];
    
	if(tmode)printf("[+] %s\n",domain);

	rev_addr(client_address,rev_ca,sizeof(rev_ca));
	snprintf(fquery,sizeof(fquery),"%s.in-addr._smtp-client.%s",rev_ca,domain);
	f=find_dmp(fquery);
	querycount++;
	if (querycount>querymax)
	    return -1;
	if(!tmode) {
	    if(f==1) return 0; // pass
	    if(f==2) return 2; // fail
	    if(f==(-1)) return (-1); //dns error 450
	    }
	};
    // if(i<>0) continue
    };
//else try RMXF other tests

/* a */
if(test_a) {
    if(tmode)printf("Test a:\n");
    *match=5;
    hh=gethostbyname(domain);
    querycount++;
    if (querycount>querymax)
        return -1;
    if(!hh) {
	if(h_errno==TRY_AGAIN || h_errno==NO_RECOVERY)
	    if(!tmode)return (-1); //dns error
	}
    else {
	for(i=0;i<256;i++) {
    	    if(hh->h_addr_list[i]) {
		char odp[256];
		snprintf(odp,sizeof(odp),"%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],
		    (tolerance==1)?0:(unsigned char)hh->h_addr_list[i][3]);
		if(!tmode) {
		    if (strncmp(odp,client_address,strlen(odp)-tolerance)==0) {
			return 0;
			}
		//    if (strncmp(odp,"127.0.0",7)==0) {
		//	return 2; //admin reject
		//	}
		    }
		else {
		    if (strncmp(odp,"127.0.0",7)!=0)
			printf("[+] %s/%s\n",odp,(tolerance)?"24":"32");
		    else
			printf("[-] 0/0\n");
		    }
		}
	    else {
		i=256;
		}
	    }
	}
    }
    
/* mx */
if(test_mx) {
    if(tmode)printf("Test mx:\n");
    *match=6;
    i=find_mailhost(domain,client_address,(tolerance)?"24":"32", tmode,"+", &querycount, querymax);
    if(i==0)return 0; //pass
    // if(i==1) //fail
    if(i==-1) return -1; //dns error
    // if(i==-2) //not found
    }

*match=0;
return default_result;
}

int main (int argc, char *argv[]) {

int log=1;
int log_level=LOG_INFO;
int tmode=0;
int prep=1;
int sh=0;
int reject_unsigned_bounce=0;
int sign_auth_relay=0;
int daemon=0;

char sh_ip[256];

char buf[1001];
char *k;

char request[257];
char sender[257];
char client_address[257];
char recipient[257];
char helo_name[257];
char client_name[257];
char queue_id[30];
char sasl_method[257];
char sasl_username[257];
char sasl_sender[257];
char argv1[257];
char shprep[]=" x-spf-after-relay=yes;";

int fi;
int servlen;
int clilen;
struct sockaddr_un serv_addr;
struct sockaddr_un cli_addr;
int sockfd;
int newsockfd;
		    
int match=0;

struct tspfm spfm;

FILE *fout=stdout;
FILE *fin=stdin;

memset(&myhostname,0,sizeof(myhostname));
gethostname(myhostname,250);

sprintf(message_fail,"Undelivered, check spf records. http://spf.pobox.com/");
sprintf(message_acl_error,"Email system error: mxfilter.acl problem. http://rmxf.comm.pl/");
sprintf(message_dns_error,"DNS error, try again later.");
sprintf(message_fail_on_unknown,"Setup spf records. http://spf.pobox.com/");

load_config ("/etc/postfix/mxfilter.cf");

memset(&sender,0,sizeof(sender));
memset(&request,0,sizeof(request));
memset(&client_address,0,sizeof(client_address));
memset(&recipient,0,sizeof(recipient));
memset(&client_name,0,sizeof(client_name));
memset(&helo_name,0,sizeof(helo_name));
memset(&queue_id,0,sizeof(queue_id));
memset(&sasl_method,0,sizeof(sasl_method));
memset(&sasl_username,0,sizeof(sasl_username));
memset(&sasl_sender,0,sizeof(sasl_sender));
memset(&argv1,0,sizeof(argv1));

if(argc>1) {
    snprintf(argv1,sizeof(argv1),"%s",argv[1]);
    }

if(index(argv1,'@')) {
    char spf_reason[1024]="";

    tmode=1;
    snprintf(sender,sizeof(sender),"%s",argv1);
    snprintf(client_address,sizeof(client_address),"127.0.0.1");
    
    k=(char *)index(sender,'@');
    if(k) {
	k[0]=0;
	snprintf(spfm.l,sizeof(spfm.l),"%s",sender);
	snprintf(spfm.o,sizeof(spfm.o),"%s",k+1);
	snprintf(spfm.d,sizeof(spfm.d),"%s",k+1);
	k[0]='@';
	snprintf(spfm.s,sizeof(spfm.s),"%s",sender);
	}
    else {
	snprintf(spfm.l,sizeof(spfm.l),"postmaster");
	snprintf(spfm.o,sizeof(spfm.o),"%s",sender);
	snprintf(spfm.d,sizeof(spfm.d),"%s",sender);
	snprintf(spfm.s,sizeof(spfm.s),"%s",sender);    
	}
    snprintf(spfm.i,sizeof(spfm.i),"127.0.0.1");
    snprintf(spfm.t,sizeof(spfm.t),"%d",(int)time(NULL));
    snprintf(spfm.p,sizeof(spfm.p),"localhost");
    snprintf(spfm.h,sizeof(spfm.h),"localhost");
    
    printf("RMXF test: %s\n",sender);
    check_mx(sender,client_address,1,&match,1,&spfm,spf_reason,sizeof(spf_reason));
    printf("[spf] exp=%s\n",spf_reason);
    printf("DNS query counter: %d/%d\n",querycount,querymax);
    return 0;
    }

// procmail

if(strcmp(argv1,"--procmail")==0) {
    int head=1;
    int prp=0;
    int pip=0;
    int pptr;
    char spf_reason[1024]="";
    char pomb[512];
    int result;
    int done=0;
    
    load_config ("~/.mxfilter.cf");
    
    while((fgets(buf,1000,fin))) {
	k=(char *)index(buf,10);
	if(k)k[0]=0;
	k=(char *)index(buf,13);
	if(k)k[0]=0;
	
	if(strlen(buf)==0)
	    head=0;

	/* search line for sender */
	/* Return-Path: <user@domain.tld> */
	if(head && !prp && !pip && done==0) {
	    if(strncmp(buf,"Return-Path: ",13)==0) {
		k=(char *)index(buf,'<');
		if(k) {
		    snprintf(sender,sizeof(sender),"%s",k+1);
		    k=(char *)index(sender,'>');
		    if(k) {
			k[0]=0;
			prp=1;
			}
		    }
		}
	    }

	/* search line for ip */
	/* Received: from helo.name.domain.tld (ptr.name.domain.tld [192.168.12.1]) */
	/* or */
	/* Received: from helo.name.domain.tld ([192.186.12.1]:port "EHLO hame") by myhostname */
	/* Received: from ptr.name.domain.tld (HELO helo.name.domain.tld) (192.168.12.1) */
	/* Received: from ptr.name.domain.tld (HELO helo.name.domain.tld) (user@192.168.12.1) */
	/* Received: from ptr.name.domain.tld (192.168.12.1) */
	/* Received: from localhost (user@127.0.0.1) */

	if(head && prp && !pip && done==0) {
	    if(strncmp(buf,"Received: from ",15)==0) {
		pip=1;
		k=(char *)index(buf,'[');
		if(k) {
		    snprintf(client_address,sizeof(client_address),"%s",k+1);
		    k=(char *)index(client_address,']');
		    if(k) {
			k[0]=0;
			done=1;
			}
		    }
		else
		if(buf[strlen(buf)-1]==')') {
		    k=(char *)rindex(buf,'(');
		    if(k) {
			snprintf(pomb,sizeof(pomb),"%s",k+1);
			k=(char *)index(pomb,')');
			if(k) {
			    k[0]=0;
			    k=(char *)index(pomb,'@');
			    if(k)
				snprintf(client_address,sizeof(client_address),"%s",k+1);
			    else
				snprintf(client_address,sizeof(client_address),"%s",pomb);
			    done=1;
			    }
			}
		    }
		}
	    }

// skip lan and localhost
// received from antivirus, antispam etc.
// by RFC 1918: 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16

	if(head && done==1) {
	    if(strcmp(client_address,"127.0.0.1")==0 ||
		    strncmp(client_address,"192.168.",8)==0 ||
		    strncmp(client_address,"10.",3)==0 ||
		    check_ip4(client_address,"172.16.0.0","12")==0) {
		done=0;
		pip=0;
		}
	    }

// skip procmail_skip_line header lines 'Received: from'

	if(head && done==1) {
	    if(procmail_skip_line) {
		procmail_skip_line--;
		done=0;
		pip=0;
		}
	    }
	
	if(head && done==1) {
	    k=(char *)index(sender,'@');
	    if(k) {
		k[0]=0;
		snprintf(spfm.l,sizeof(spfm.l),"%s",sender);
		snprintf(spfm.o,sizeof(spfm.o),"%s",k+1);
		snprintf(spfm.d,sizeof(spfm.d),"%s",k+1);
		k[0]='@';
		snprintf(spfm.s,sizeof(spfm.s),"%s",sender);
		}
	    else {
		snprintf(spfm.l,sizeof(spfm.l),"postmaster");
		snprintf(spfm.o,sizeof(spfm.o),"%s",sender);
		snprintf(spfm.d,sizeof(spfm.d),"%s",sender);
		snprintf(spfm.s,sizeof(spfm.s),"%s",sender);    
		}
	    snprintf(spfm.i,sizeof(spfm.i),"%s",client_address);
	    snprintf(spfm.t,sizeof(spfm.t),"%d",(int)time(NULL));
	    pptr=get_ptr(client_address,spfm.p,sizeof(spfm.p));
	    skip_helo_check=1;
	    
	    result=check_mx(sender,client_address,1,&match,0,&spfm,spf_reason,sizeof(spf_reason));

	    if(result==-1) {
		fprintf(fout,"Received-SPF: error (%s: error in processing during lookup of %s: DNS timeout)\n",
		    myhostname,
		    sender); //error
		    }
	    else
	    if(result==0) {
			    fprintf(fout,"Received-SPF: pass (%s: domain of %s: designates %s as permitted sender) x-test=%s\n",
				myhostname,
				sender,
				client_address,
				matchs[match]); //ok
	        }
	    else
	    if(result==1) {
			    fprintf(fout,"Received-SPF: fail (%s: domain of %s does not designate %s as permitted sender)\n",
				myhostname,
				sender,
				client_address);
	        }
	    else
	    if(result==2) {
			    fprintf(fout,"Received-SPF: none (%s: %s does not designate permitted sender hosts)\n",
				myhostname,
				sender);
	        }
	    else
	    if(result==3) {
			    fprintf(fout,"Received-SPF: softfail (%s: domain of transitioning %s does not designate %s as permitted sender)\n",
				myhostname,
				sender,
				client_address);
	        }

	    done=2;
	    }	
	fprintf(fout,"%s\n",buf);
	}
    return 0;
    } //end of procmail


// int i=0;

if(argc==2) {
    if (strcmp(argv1,"-sign_auth_relay")==0) {
	sign_auth_relay=1;
	}
    else
    if (strcmp(argv1,"-reject_unsigned_bounce")==0) {
	reject_unsigned_bounce=1;
	}
    }
else
if(argc==3) {
    if (strcmp(argv1,"-sh")==0 || strcmp(argv1,"-smarthost")==0) {
	sh=1;
	snprintf(sh_ip,sizeof(sh_ip),"%s",argv[2]);
	}
    }

/* daemon */

if(argv[0][strlen(argv[0])-1]=='d')
    daemon=1;

if(daemon==1) {
    char path[256];
    signal(SIGCHLD, SIG_IGN);
    umask(0);

    if (sh)
	sprintf(path,"/var/mxfilter/mxfiltersh");
    else
    if (sign_auth_relay)
	sprintf(path,"/var/mxfilter/sign_auth_relay");
    else
    if (reject_unsigned_bounce)
	sprintf(path,"/var/mxfilter/reject_unsigned_bounce");
    else
	sprintf(path,"/var/mxfilter/mxfilter");
	
    unlink(path);
    sockfd = socket (AF_UNIX, SOCK_STREAM, 0);
    serv_addr.sun_family = AF_UNIX;
    strcpy(serv_addr.sun_path, path);
    servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);
    clilen = sizeof (cli_addr);
    bind(sockfd, (struct sockaddr *) &serv_addr, servlen);
    listen (sockfd, 5);

    for (fi=0;fi<1;) {
	int p;
	newsockfd = accept (sockfd, (struct sockaddr * ) &cli_addr, &clilen);
	p=fork();
	if(p>0) {
	    close(newsockfd);
	    }
	else
	if(p<0) {
	    //error
	    }
	else {
	    close (sockfd);
	    close (0);
	    close (1);
	    close (2);
	    dup2 (newsockfd, 0);
	    dup2 (newsockfd, 1);
	    dup2 (newsockfd, 2);
	    close (newsockfd);
	    fi=2;
	    }
	}
    }

while((fgets(buf,1000,fin))) {
    k=(char *)index(buf,10);
    if(k)k[0]=0;
    k=(char *)index(buf,13);
    if(k)k[0]=0;

//    printf("%d %s\n",i++,buf);

    if(strncmp(buf,"request=",8)==0) {
	snprintf(request,sizeof(request),"%s",index(buf,'=')+1);
	}
    else
    if(strncmp(buf,"sasl_method=",12)==0) {
	snprintf(sasl_method,sizeof(sasl_method),"%s",index(buf,'=')+1);
	}
    else
    if(strncmp(buf,"sasl_username=",14)==0) {
	snprintf(sasl_username,sizeof(sasl_username),"%s",index(buf,'=')+1);
	}
    else
    if(strncmp(buf,"sasl_sender=",12)==0) {
	snprintf(sasl_sender,sizeof(sasl_sender),"%s",index(buf,'=')+1);
	}
    else
    if(strncmp(buf,"sender=",7)==0) {
	snprintf(sender,sizeof(sender),"%s",index(buf,'=')+1);
	}
    else
    if(strncmp(buf,"queue_id=",9)==0) {
	snprintf(queue_id,sizeof(queue_id),"%s",index(buf,'=')+1);
	}
    else
    if(strncmp(buf,"client_address=",15)==0) {
	snprintf(client_address,sizeof(client_address),"%s",index(buf,'=')+1);
	}
    else
    if(strncmp(buf,"recipient=",10)==0) {
	snprintf(recipient,sizeof(recipient),"%s",index(buf,'=')+1);
	}
    else
    if(strncmp(buf,"helo_name=",10)==0) {
	snprintf(helo_name,sizeof(helo_name),"%s",index(buf,'=')+1);
	}
    else
    if(strncmp(buf,"client_name=",12)==0) {
	snprintf(client_name,sizeof(client_name),"%s",index(buf,'=')+1);
	}
    else
    if(strlen(buf)==0) {
	int result=0;
	char exitcode[30]="";
	char reason[1024]="";
	char spf_reason[1024]="";
	int acl;
	int s_ok=0;
	int use_helo=0;

// sign_auth_relay always return dunno, and rewrite envelope-sender
// for sasl sessions

	if(sign_auth_relay==1) {
	    int for_relay=0;
	    
	    if(strlen(sasl_username)>0) {
		for_relay=1;
		}
	    // todo: check mynetworks for sign-sender

	    if(for_relay && index(sender,'@')) {
		char signed_sender[1024];

		memset(&signed_sender,0,sizeof(signed_sender));
		if (set_sign_sender("LRS",sender,signed_sender,sizeof(signed_sender),"") < 0) {
		    fprintf(fout,"action=dunno\n\n"); //no rewrite
		    return 0;
		    }
		fprintf(fout,"action=sender_rewrite %s\n",signed_sender);
		fprintf(fout,"dunno\n\n");
		return 0;
		}
	    fprintf(fout,"action=dunno\n\n");
	    return 0;
	    }
	else

// reject_unsigned_bounce: reject only unsigned bounce message
// other dunno

	if(reject_unsigned_bounce==1) {
	    char org_sender[1024];
	    int k;

	    if(strlen(sender)!=0 || strncmp(recipient,"postmaster@",11)==0 ||
		    strncmp(recipient,"abuse@",6)==0) {
		fprintf(fout,"action=dunno\n\n");
		return 0;
		}
	    memset(&org_sender,0,sizeof(org_sender));
	    k=check_sign_sender("LRS",recipient,org_sender,sizeof(org_sender));
	    if(k==-1) {
		fprintf(fout,"action=550 Unsigned bounce message.\n\n");
		return 0;
		}
	    else
	    if(k==0) {
		fprintf(fout,"action=recipient_rewrite %s\n",org_sender);
		fprintf(fout,"dunno\n\n");
		return 0;
		}
	    else {
		fprintf(fout,"action=dunno\n\n");
		return 0;
		}
	    return 0;
	    }
	else

// spf smarthost

	if(sh==1) {
	    if (strlen(sasl_username)==0) {
		fprintf(fout,"action=dunno\n\n");
		return 0;
		}
	    snprintf(client_address,sizeof(client_address),"%s",sh_ip);
	    if (prepend_spf_after_relay==0)
		prep=0;
	    }

// spf1

	if(strlen(sender)==0 && skip_helo_check==1) {
	    fprintf(fout,"action=dunno\n\n");
	    return 0;
	    }
	    
	snprintf(spfm.i,sizeof(spfm.i),"%s",client_address);
	snprintf(spfm.t,sizeof(spfm.t),"%d",(int)time(NULL));
	snprintf(spfm.p,sizeof(spfm.p),"%s",client_name);
	snprintf(spfm.h,sizeof(spfm.h),"%s",helo_name);

	k=(char *)index(sender,'@');
	if(k) {
	    k[0]=0;
	    if(strlen(sender)==0)
		snprintf(spfm.l,sizeof(spfm.l),"postmaster");
	    else
		snprintf(spfm.l,sizeof(spfm.l),"%s",sender);

	    if(strlen(k+1)>0) {
		snprintf(spfm.o,sizeof(spfm.o),"%s",k+1);
		snprintf(spfm.d,sizeof(spfm.d),"%s",k+1);
		k[0]='@';
		snprintf(spfm.s,sizeof(spfm.s),"%s",sender);
		s_ok=1;
		}
	    else {
		if(strlen(helo_name)>0) {
		    s_ok=1;
		    use_helo=1;
		    // envelope sender has no domain use the helo
		    snprintf(sender,sizeof(sender),"%s@%s",spfm.l,helo_name);

		    snprintf(spfm.o,sizeof(spfm.o),"%s",helo_name);
		    snprintf(spfm.d,sizeof(spfm.d),"%s",helo_name);
		    snprintf(spfm.s,sizeof(spfm.s),"%s",sender);
		    }
		}
	    }
	else {
	    if(strlen(sender)==0)
		snprintf(spfm.l,sizeof(spfm.l),"postmaster");
	    else
		snprintf(spfm.l,sizeof(spfm.l),"%s",sender);

	    if(strlen(helo_name)>0) {
		s_ok=1;
		use_helo=1;
		// envelope sender has no domain use the helo
		snprintf(sender,sizeof(sender),"%s@%s",spfm.l,helo_name);

		snprintf(spfm.o,sizeof(spfm.o),"%s",helo_name);
		snprintf(spfm.d,sizeof(spfm.d),"%s",helo_name);
		snprintf(spfm.s,sizeof(spfm.s),"%s",sender);
		}
	    }

	if(strlen(queue_id)>0)
	    prep=0;

	fprintf(fout,"action=");
	
	if(s_ok==0) {
	    if(log) {
		openlog("postfix/mxfilter", LOG_PID, LOG_MAIL);
		syslog(log_level,"[%s] %s %s %s\n",client_address,sender,recipient,"ERROR: bad sender");
		closelog();
		}
	    if(prep)
		fprintf(fout,"prepend Received-SPF: unknown (%s: empty sender)%s\n",myhostname,(sh)?shprep:"");
	    fprintf(fout,"dunno\n\n"); //not @ in sender?
	    return 0;
	    }

	if(sh==0) {
	    char config[1024],sconfig[1024];
	    
	    memset(&config,0,sizeof(config));
	    memset(&sconfig,0,sizeof(sconfig));
	    acl=mxfilter_acl(sender,recipient,exitcode,sizeof(exitcode),reason,sizeof(reason),config,sconfig);
	    if (strlen(config)>0) {
		load_config(config);
		}
	    if (strlen(sconfig)>0) {
		load_config(sconfig);
		}
	    }
	else {
	    sprintf(exitcode,"550");
	    acl=1;
	    }
	if(acl<0) { //acl file error
	    int macro_res;
	    if(log) {
		openlog("postfix/mxfilter", LOG_PID, LOG_MAIL);
		syslog(log_level,"[%s] %s %s %s\n",client_address,sender,recipient,"ERROR: access list");
		closelog();
		}
	    macro_res=macro(message_acl_error,sizeof(message_acl_error),&spfm);
	    if (macro_res<0) {
		openlog("postfix/mxfilter", LOG_PID, LOG_MAIL);
		syslog(log_level,"message_acl_error: [%s] expand error\n", message_acl_error);
		closelog();
		}
	    fprintf(fout,"450 %s\n\n",message_acl_error);
	    return 0;
	    }
	if(acl==0) {
	    if(log) {
		openlog("postfix/mxfilter", LOG_PID, LOG_MAIL);
		syslog(log_level,"[%s] %s %s %s\n",client_address,sender,recipient,"ACL: skip");
		closelog();
		}
	    if(prep)
		fprintf(fout,"prepend Received-SPF: unknown (%s: test skip)%s\n",
		    myhostname,
		    (sh)?shprep:"");
	    fprintf(fout,"dunno\n\n");
	    return 0;
	    }
	querycount=0;
	result=check_mx(sender,client_address,1,&match,0, &spfm, spf_reason, sizeof(spf_reason));

	if(result == -1) { // dns error
	    if(dunno_on_error) {
		if(prep)
		    fprintf(fout,"prepend Received-SPF: error (%s: error in processing during lookup of %s: DNS timeout)%s\n",myhostname,sender,(sh)?shprep:""); //error
		fprintf(fout,"dunno\n\n");
		}
	    else {
		int macro_res=macro(message_dns_error,sizeof(message_dns_error),&spfm);
		if (macro_res<0) {
		    openlog("postfix/mxfilter", LOG_PID, LOG_MAIL);
		    syslog(log_level,"message_dns_error: [%s] expand error\n", message_dns_error);
		    closelog();
		    }
		fprintf(fout,"450 %s\n\n",message_dns_error);
		}
	    }
	else
	if(result==0) { //pass
	    if(ok_on_pass) {
		fprintf(fout,"OK\n\n");
		}
	    else {
		if(prep)
		    fprintf(fout,"prepend Received-SPF: pass (%s: domain of %s: designates %s as permitted sender) x-test=%s;%s\n",
			myhostname,
			sender,
			client_address,
			matchs[match],
			(sh)?shprep:""); //ok
		fprintf(fout,"dunno\n\n");
		}
	    }
	else
	if (result==1 || (fail_on_softfail && result==3)) { // fail
	    if(dunno_on_fail) {
		if(prep)
		    fprintf(fout,"prepend Received-SPF: fail (%s: domain of %s does not designate %s as permitted sender)%s\n",
			myhostname,
			sender,
			client_address,
			(sh)?shprep:"");
		fprintf(fout,"dunno\n\n");
		}
	    else {
		char lres[1024];
		if(strlen(spf_reason)>0 && match==1) {
		    snprintf(lres,sizeof(lres),"%s",spf_reason);
		    }
		else {
		    int macro_res=macro(message_fail,sizeof(message_fail),&spfm);
		    if (macro_res<0) {
			openlog("postfix/mxfilter", LOG_PID, LOG_MAIL);
			syslog(log_level,"message_fail: [%s] expand error\n", message_fail);
			closelog();
			}
		    snprintf(lres,sizeof(lres),"%s",message_fail);
		    }
		fprintf(fout,"550 %s\n\n",lres);
		}
	    }
	else
	if(result==2) { // unknown
	    char lres[1024];
	    if(strcmp(exitcode,"HMARK")==0) {
		if(prep)
		    fprintf(fout,"prepend Received-SPF: none (%s: %s does not designate permitted sender hosts)%s\n",
			myhostname,
			sender,
			(sh)?shprep:"");
		fprintf(fout,"dunno\n\n");
		}
	    else {
		if(strlen(spf_reason)>0 && match==1) {
		    snprintf(lres,sizeof(lres),"%s",spf_reason);
		    }
		else {
		    int macro_res=macro(message_fail_on_unknown,sizeof(message_fail_on_unknown),&spfm);
		    if (macro_res<0) {
			openlog("postfix/mxfilter", LOG_PID, LOG_MAIL);
			syslog(log_level,"message_fail_on_unknown: [%s] expand error\n", message_fail_on_unknown);
			closelog();
			}
		    snprintf(lres,sizeof(lres),"%s",message_fail_on_unknown);
		    }
		fprintf(fout,"%s %s\n\n",
		    (strlen(exitcode)==0)?"550":exitcode,
		    (strlen(reason)==0)?lres:reason);
		}
	    }
	else
	//if(result==3) // softfail
	    {
	    if(prep)
	        fprintf(fout,"prepend Received-SPF: softfail (%s: domain of transitioning %s does not designate %s as permitted sender)%s\n",
		    myhostname,
		    sender,
		    client_address,
		    (sh)?shprep:"");
	    fprintf(fout,"dunno\n\n");
	    }

	if(log) {
	    if(sh==0)
		openlog("postfix/mxfilter", LOG_PID, LOG_MAIL);
	    else
		openlog("postfix/mxfiltersh", LOG_PID, LOG_MAIL);
		
	    syslog(log_level,"[%s] from:%s%s to:%s rmx:%s->%s%s%s qc:%d\n",
		client_address,
		sender,
		(use_helo)?"/helo":"",
		recipient,
		(match==6 && result!=0)?"false":matchs[match],
		(result==0)?"PASS":(result==1)?"FALSE":(result==2)?"UNKNOWN":(result==3)?"SOFTFAIL":"DNSERROR",
		(!sh)?" acl:":"",
		(!sh)?exitcode:"",
		querycount);
	    closelog();
	    }

	return 0;
	}
    }

// strange, postfix false?
if(log) {
    openlog("postfix/mxfilter", 0, LOG_MAIL);
    syslog(log_level,"%s %s %s %s\n",client_address,sender,recipient,"ERROR: stdin empty");
    closelog();
    }
fprintf(fout,"action=dunno\n\n");
return 0;
}
