#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define isblank(a) ( a == '\t' || a == ' ' ) #define BUFSIZE 1024 #define SBUFSIZE 200 #define IPSIZE 20 #define MATCH "^Received:.*\\[(([0-9]{1,3}\\.){3}[0-9]{1,3})\\]" #define MATCH2 "^Received:.*from *([^ \t\n\\)]+)" namespace regex { /* Acts like sscanf except with regex! Note: there must be at least 3 arguments to this function, even though it only requires two, since the 3rd argument will always be the total match. Synopsis: sscanf("This is a test match","t(est) *((ma)(tch))", &one, &two, &three, &four, &five); one - five are all previously allocated strings Each will contain, respectively: one -- "test match" two -- "est" three -- "match" four -- "ma" five -- "tch" If you don't care about one of the matches (the full expression for example), the argument must be NULL. There MUST be an argument for every submatch. */ int compile(regex_t* reg, const char* match) { if(int error = regcomp(reg, match, REG_EXTENDED)) if(error != REG_NOMATCH) { char errbuf[200]; regerror(error, reg, errbuf, 200); printf("Error! %s -- %s\n", errbuf, strerror(errno)); exit(1); } } int sscanf(const char* buf, regex_t* match, int numargs, ...) { int error; regmatch_t* psub = NULL; int nsub; va_list args; int count = 0; va_start(args, numargs); nsub = match->re_nsub + 1; psub = new regmatch_t[nsub]; error = regexec(match, buf, nsub, psub, 0); if (error==0) { //Match found int size = 0; char* arg = NULL; for(int i = 0; i < nsub; i++) { arg = va_arg(args, char*); if(arg!=0) { size = psub[i].rm_eo - psub[i].rm_so; memcpy(arg, buf + psub[i].rm_so, size); arg[size] = '\0'; } count++; if(count >= numargs) goto DONE; } } delete[] psub; ERROR: if(error && error != REG_NOMATCH) { char errbuf[200]; regerror(error, match, errbuf, 200); printf("Error! %s -- %s\n", errbuf, strerror(errno)); } DONE: va_end(args); return count; } } template struct AfterBlock { T data; typedef int (*TFUNC)(T); TFUNC func; AfterBlock(T arg_d, TFUNC arg_f) { data = arg_d; func = arg_f; } ~AfterBlock() { func(data); } }; string* getheader(FILE* fp) { static char buf[BUFSIZE] = ""; static string* ret = NULL; int retlen = 0; if(ret) { delete ret; ret = NULL; } FILE* strdump = tmpfile(); if(!*buf) { //First header fgets(buf, BUFSIZE, fp); if(!*buf) return NULL; } fputs(buf, strdump); retlen += strlen(buf); *buf = '\0'; while(!feof(fp)) { fgets(buf, BUFSIZE, fp); if(!isblank(buf[0])) { //Then it's a new header //Thank you RFC822! break; } fputs(buf, strdump); retlen += strlen(buf); } rewind(strdump); char* tline = new char[retlen+1]; fread(tline, sizeof(char), retlen, strdump); tline[retlen] = '\0'; if(tline[retlen-1]=='\n') tline[retlen-1] = '\0'; ret = new string(tline); fclose(strdump); delete[] tline; return ret; } template class auto_array { T* data; public: auto_array(T* watch) : data(watch) {} ~auto_array() { delete[] data; } }; #define RBL_MATCH "RBL filtered by (.*)" string rbl_result(void) { int count; string ret; ifstream infile(".rblfile"); static regex_t* rbl_match = NULL; if(!rbl_match) { rbl_match = new regex_t; regex::compile(rbl_match, RBL_MATCH); } while(getline(infile, ret, '\n')) { char* addr = new char[ret.length()]; auto_array delete_me(addr); // We don't want 'not RBL filtered' to match of course. if(strstr(ret.c_str(), "not RBL filtered by")) continue; if(count = regex::sscanf(ret.c_str(), rbl_match, 2, NULL, addr)) { ret = addr; return ret; } } return string(); } /* Here we put the spam X-IP headers on top to minimize spam filtage time. Then print the rest of the headers stored in tmp. */ void rblprint(const char* ip) { int oldmask = umask(0000); // printf("Old mask: %o\n", oldmask); //Now get result from rblcheck, that great lil' dnsbl utility pid_t pid; if(!(pid = fork())) { //Child... //DON'T close FILE* structures! int newout = creat(".rblfile", S_IRWXU); if(!newout) { perror("Can't open temporary file!"); exit(0); } dup2(newout,fileno(stdout)); dup2(newout,fileno(stderr)); execlp("rblcheck", "rblcheck", "-m", ip, NULL); perror(ip); exit(0); } int status = 0; waitpid(pid, &status, 0); if(WIFEXITED(status)) { status = WEXITSTATUS(status); if(status) { string fromwho = rbl_result(); fprintf(stdout, "%s (%s)\n", " blackhole", fromwho.c_str()); } else fprintf(stdout, "\n"); } else { perror("rblcheck didn't exit?"); exit(1); } } int main(void) { char buf[SBUFSIZE]; string* header = NULL; char ip[IPSIZE]; FILE* tmp = tmpfile(); //Yeah, yeah, first line has to stay first line. if(fgets(buf,SBUFSIZE,stdin)) { buf[strlen(buf) - 1] = '\0'; fputs(buf,stdout); fputc('\n', stdout); } regex_t match1; regex_t match2; regex::compile(&match1, MATCH); regex::compile(&match2, MATCH2); while(header = getheader(stdin)) { int count = 0; //Check if Recieved line. if(count = regex::sscanf(header->c_str(), &match1, 2, NULL, &ip)) { int addr; hostent* host = NULL; if ((int) (addr = inet_addr(ip)) != -1) { host=gethostbyaddr((char *) &addr, sizeof (addr), AF_INET); if (host == NULL) { fprintf(stdout, "X-IP: %s", ip); } else { for (char** p = host->h_addr_list; *p!=0;p++) { struct in_addr in; char **q; memcpy(&in.s_addr, *p, sizeof(in.s_addr)); fprintf(stdout, "X-IP: %s [%s]", host->h_name, inet_ntoa(in)); } } } rblprint(ip); } // if regex::sscanf MATCH else if(count = regex::sscanf(header->c_str(), &match2, 2, NULL, &ip)) { // No [xx.xx.xx.xx] but this is the next best. hostent* host = NULL; host = gethostbyname(ip); if (host == NULL) { fprintf(stdout, "X-IP: %s", ip); } else { for (char** p = host->h_addr_list; *p!=0;p++) { struct in_addr in; char **q; memcpy(&in.s_addr, *p, sizeof(in.s_addr)); fprintf(stdout, "X-IP: %s [%s]", host->h_name, inet_ntoa(in)); } } rblprint(ip); } //Even after checked, still put Recieved: line or other header fputs(header->c_str(),tmp); fputc('\n', tmp); } rewind(tmp); FILE* debuglog = fopen("debug.log", "w"); while(int size = fread(buf,sizeof(char), SBUFSIZE, tmp)) { fwrite(buf,sizeof(char), size, stdout); } //End of headers fputc('\n',stdout); fclose(tmp); return 0; }