#include #include #include #include #include #include "local_scan.h" #define BUFSIZE 16384 typedef struct { char *url; void *next; int res; } url_t; static url_t *cl_url = NULL; static char err_msg[CURL_ERROR_SIZE]; static CURL *curl; static char buffer[BUFSIZE]; uschar *reason; //static void cleanup(void) __attribute__((destructor)); //------------------------------------------------------------------- static int test_domain( char *dom ) { uschar *r; if ((debug_selector & D_local_scan) != 0) debug_printf("SURBL: Testing url %s",dom); r = expand_string(string_sprintf("${lookup dnsdb{a=%s.multi.surbl.org}{$value}{}}",dom)); if (!r) { log_write(0,LOG_MAIN,"SURBL: Expansion error: %s",expand_string_message); return -1; } if (!*r) return 1; reason = string_sprintf("%s listed at multi.surbl.org (%s)",dom,r); return 0; } //------------------------------------------------------------------- static char *follow_url ( char *url ) { CURLcode ret; static char *nurl; curl_easy_setopt(curl,CURLOPT_URL,url); ret = curl_easy_perform(curl); if (ret && (debug_selector & D_local_scan) != 0) { debug_printf("SURBL: Error navigating %s: %s",url,err_msg); } curl_easy_getinfo(curl,CURLINFO_EFFECTIVE_URL,&nurl); return nurl; } //------------------------------------------------------------------- static int process_url ( char *url ) { char *nurl,*durl,*dom,*s; char *d1,*d2,*d3; int r; url_t *t; if ((debug_selector & D_local_scan) != 0) debug_printf("SURBL: Processing url %s",url); for (t=cl_url; t; t=t->next) { if (!strcmp(url,t->url)) { if (t->res!=-1) { if ((debug_selector & D_local_scan) != 0) debug_printf("SURBL: URL already tested"); if (!t->res) reason = string_sprintf("%s listed at multi.surbl.org",url); return t->res; } break; } } if (!t) { t= store_get_perm(sizeof(url_t)); if (!t) return -1; t->url = store_get_perm(strlen(url)+1); if (!t->url) return -1; strcpy(t->url,url); t->next = cl_url; t->res = 1; cl_url = t; } nurl = follow_url(url); durl = curl_unescape(nurl,0); if ((debug_selector & D_local_scan) != 0) debug_printf("SURBL: Found to be %s",durl); if (strlen(durl)<7) return 1; // Get hostname dom = durl+7; if (*dom=='/') dom++; for (s=dom; *s; s++) { if (*s=='@') dom=s+1; else if (*s=='/' || *s=='?') { *s=0; break; } } // Strip www. if (!strncmp(dom,"www.",4)) dom += 4; if ((debug_selector & D_local_scan) != 0) debug_printf("SURBL: Cleaned to %s",dom); // Get host parts d1=d2=d3=NULL; for (s = dom; *s; s++) { if (*s=='.') { d3=d2; d2=d1; d1=s+1; } else if (*s==':') { *s=0; break; } } // Check it r = -1; if (!d1) return 1; if (isdigit(*d1)) { if (d1 && d2 && d3) { *d1=*d2=*d3=0; r = test_domain(CS string_sprintf("%s.%s.%s.%s",d1+1,d2+1,d3+1,dom)); } } else { if (strlen(d1)==2 && d3) { r = test_domain(d3); } if (r!=0) { if (d2) r = test_domain(d2); else r = test_domain(dom); } } t->res = r; curl_free(durl); if (r==0) return 0; else return 1; } //------------------------------------------------------------------- static int process_data ( void ) { char *s,*u,c; s = buffer; while ((s = strstr(s,"http://"))!=NULL) { c=0; if (s>buffer && (*(s-1)=='"' || *(s-1)=='\'')) { c=*(s-1); } u=s; for (; *s; s++) { if (*s=='\\') { s++; continue; } if (*s==c) break; if (c) continue; if (isspace(*s) || *s=='<' || *s=='>' || *s=='"') break; } *s=0; s++; if (!process_url(u)) return 0; } return 1; } //------------------------------------------------------------------- static int scan_file ( uschar **yield, char *file ) { FILE *fp; if ((fp=fopen(file,"rt"))==NULL) { *yield = string_sprintf("Unable to open %s",file); return ERROR; } if ((debug_selector & D_local_scan) != 0) debug_printf("SURBL: Testing file %s",file); while (!feof(fp)) { fgets(buffer,BUFSIZE,fp); if (!process_data()) { if ((debug_selector & D_local_scan) != 0) debug_printf("SURBL: Listed"); *yield = reason; fclose(fp); return OK; } } fclose(fp); *yield = string_copy(US "0"); return OK; } //------------------------------------------------------------------- void cleanup ( void ) { if (curl) { curl_easy_cleanup(curl); curl = NULL; } log_write(0,LOG_MAIN,"SURBL: cleanup"); } //------------------------------------------------------------------- int surbl(uschar **yield, int argc, uschar *argv[]) { if (!curl) { curl=curl_easy_init(); if (!curl) { *yield = string_copy(US "Unable to init curl"); return ERROR; } curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); curl_easy_setopt(curl,CURLOPT_FAILONERROR,1); curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,err_msg); curl_easy_setopt(curl,CURLOPT_NOBODY,1); curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1); curl_easy_setopt(curl,CURLOPT_AUTOREFERER,1); curl_easy_setopt(curl,CURLOPT_MAXREDIRS,5); curl_easy_setopt(curl,CURLOPT_USERAGENT,"Mozilla/4.0 (compatible; MSIE 6.0; Win32);"); curl_easy_setopt(curl,CURLOPT_TIMEOUT,10); //atexit(cleanup); } return scan_file(yield, CS argv[0]); }