Mercurial > dnsbl
diff src/context.cpp @ 90:962a1f8f1d9f stable-5-4
add verify statement to verify addresses with better mx host
author | carl |
---|---|
date | Sun, 18 Sep 2005 10:19:58 -0700 |
parents | 7245c45cef7a |
children | ca46fafc6621 |
line wrap: on
line diff
--- a/src/context.cpp Sun Aug 07 11:26:37 2005 -0700 +++ b/src/context.cpp Sun Sep 18 10:19:58 2005 -0700 @@ -8,6 +8,16 @@ #include "includes.h" +// needed for socket io +#include <sys/ioctl.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <sys/socket.h> +#include <sys/un.h> + static char* context_version="$Id$"; char *token_black; @@ -40,10 +50,290 @@ char *token_substitute; char *token_tld; char *token_unknown; +char *token_verify; char *token_white; +char *token_myhostname; +char myhostname[HOST_NAME_MAX+1]; + +verify_map verifiers; string_set all_strings; // owns all the strings, only modified by the config loader thread -const int maxlen = 1000; +const int maxlen = 1000; // used for snprintf buffers +const int maxage = 120; // smtp verify sockets older than this are ancient +extern int NULL_SOCKET; +extern time_t ERROR_SOCKET_TIME; // number of seconds between attempts to open a socket an smtp host for address verification + + +int SMTP::writer() { + // log("writer() sees buffer with %s", buffer); + // log("writer() sees error %d", (int)error); + int rs = 0; + if (!error) { + int len = strlen(buffer); + while (rs < len) { + int ws = write(fd, buffer+rs, len-rs); + if (ws > 0) { + rs += ws; + } + else { + // peer closed the socket! + rs = 0; + error = true; + break; + } + } + } + return rs; +} + + +int SMTP::reader() { + // read some bytes terminated by lf or end of buffer. + // we may have a multi line response or part thereof in the buffer. + // log("reader() sees error %d", (int)error); + if (error) return 0; + int len = maxlen-1; // room for null terminator + while (pending < len) { + int ws = read(fd, buffer+pending, len-pending); + if (ws > 0) { + pending += ws; + if (buffer[pending-1] == '\n') break; + } + else { + // peer closed the socket! + pending = 0; + error = true; + break; + } + } + buffer[pending] = '\0'; + // log("reader() sees buffer with %s", buffer); + return pending; +} + + +int SMTP::read_line() { + char *lf = strchr(buffer, '\n'); + if (!lf) { + reader(); // get a lf + lf = strchr(buffer, '\n'); + if (!lf) lf = buffer + pending - 1; + } + return (lf-buffer)+1; // number of bytes in this line +} + + +int SMTP::flush_line(int r) { + if (pending > r) memmove(buffer, buffer+r, pending-r); + pending -= r; +} + + +int SMTP::read_response() { + pending = 0; + buffer[pending] = '\0'; + while (true) { + int r = read_line(); + // log("read_response() sees line with %s", buffer); + // log("read_response() sees line length %d", r); + if (r == 0) return 0; // failed to read any bytes + if ((r > 4) && (buffer[3] == '-')) { + flush_line(r); + continue; + } + return atoi(buffer); + } + return 0; +} + + +int SMTP::cmd(char *c) { + if (c) { + init(); + append(c); + } + append("\r\n"); + writer(); + return read_response(); +} + + +int SMTP::helo() { + if (read_response() != 220) return 0; + init(); + append("HELO "); + append(token_myhostname); + return cmd(NULL); +} + + +int SMTP::rset() { + return cmd("RSET"); +} + + +int SMTP::from(char *f) { + init(); + append("MAIL FROM:<"); + append(f); + append(">"); + return cmd(NULL); +} + + +int SMTP::rcpt(char *t) { + init(); + append("RCPT TO:<"); + append(t); + append(">"); + return cmd(NULL); +} + + +int SMTP::quit() { + int rc = cmd("QUIT"); + shutdown(fd, SHUT_RDWR); + close(fd); + return rc; +} + + +// void SMTP::log(char *m, int v) { +// char buf[maxlen]; +// snprintf(buf, maxlen, m, v); +// my_syslog(buf); +// } +// +// +// void SMTP::log(char *m, char *v) { +// char buf[maxlen]; +// snprintf(buf, maxlen, m, v); +// my_syslog(buf); +// } +// +// +VERIFY::VERIFY(char *h) { + host = h; + last_err = 0; + pthread_mutex_init(&mutex, 0); +} + + +void VERIFY::closer() { + bool ok = true; + while (ok) { + int fd = 0; + pthread_mutex_lock(&mutex); + if (sockets.empty()) { + ok = false; + } + else { + time_t t = times.front(); + time_t now = time(NULL); + if ((now - t) > maxage) { + // this socket is ancient, remove it + fd = sockets.front(); + times.pop_front(); + sockets.pop_front(); + } + else { + ok = false; + } + } + pthread_mutex_unlock(&mutex); + if (fd) { + SMTP s(fd); + s.quit(); // closes the fd + // s.log("closer() closes ancient %d", fd); + } + } +} + + +int VERIFY::get_socket() { + int sock = NULL_SOCKET; + pthread_mutex_lock(&mutex); + if (!sockets.empty()) { + sock = sockets.front(); + times.pop_front(); + sockets.pop_front(); + // SMTP::log("get_socket() %d from cache", sock); + } + pthread_mutex_unlock(&mutex); + + if (sock == NULL_SOCKET) { + time_t now = time(NULL); + if ((now - last_err) > ERROR_SOCKET_TIME) { + // nothing recent, maybe this time it will work + hostent *h = gethostbyname(host); + if (h) { + sockaddr_in server; + server.sin_family = h->h_addrtype; + server.sin_port = htons(25); + memcpy(&server.sin_addr, h->h_addr_list[0], h->h_length); + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock != NULL_SOCKET) { + bool rc = (connect(sock, (sockaddr *)&server, sizeof(server)) == 0); + if (!rc) { + shutdown(sock, SHUT_RDWR); + close(sock); + sock = NULL_SOCKET; + last_err = now; + } + } + else last_err = now; + } + else last_err = now; + } + if (sock != NULL_SOCKET) { + SMTP s(sock); + if (s.helo() != 250) { + put_socket(sock, true); + sock = NULL_SOCKET; + } + } + } + return sock; +} + + +void VERIFY::put_socket(int fd, bool err) { + if (err) { + // SMTP::log("put_socket() %d with error, close it", fd); + shutdown(fd, SHUT_RDWR); + close(fd); + last_err = time(NULL); + } + else { + // SMTP::log("put_socket() %d", fd); + pthread_mutex_lock(&mutex); + time_t now = time(NULL); + times.push_back(now); + sockets.push_back(fd); + pthread_mutex_unlock(&mutex); + } +} + + +bool VERIFY::ok(char *from, char *to) { + if (host == token_myhostname) return true; + int fd = get_socket(); + if (fd == NULL_SOCKET) return true; // cannot verify right now, we have socket errors + SMTP s(fd); + s.rset(); + int rc; + rc = s.from(from); + // s.log("verify::ok from sees %d", rc); + if (rc != 250) { + put_socket(fd, s.err()); + return (rc >= 500) ? false : true; + } + rc = s.rcpt(to); + // s.log("verify::ok rcpt sees %d", rc); + put_socket(fd, s.err()); + return (rc >= 500) ? false : true; +} + DNSBL::DNSBL(char *n, char *s, char *m) { name = n; @@ -155,6 +445,7 @@ CONTEXT::CONTEXT(CONTEXTP parent_, char *name_) { parent = parent_; name = name_; + verify_host = NULL; env_from_default = (parent) ? token_inherit : token_unknown; content_filtering = (parent) ? parent->content_filtering : false; content_suffix = NULL; @@ -203,6 +494,26 @@ } +VERIFYP CONTEXT::find_verify(char *to) { + if (verify_host && (verify_host != token_myhostname) && cover_env_to(to)) { + verify_map::iterator i = verifiers.find(verify_host); + if (i == verifiers.end()) { + if (debug_syslog) { + char buf[maxlen]; + snprintf(buf, maxlen, "cannot find struc for %s", verify_host); + my_syslog(buf); + } + return NULL; + } + VERIFYP v = (*i).second; + + return v; + } + else if (parent) return parent->find_verify(to); + else return NULL; +} + + char *CONTEXT::find_from(char *from) { char *rc = token_inherit; string_map::iterator i = env_from.find(from); @@ -389,6 +700,10 @@ } printf("%s }; \n", indent); + if (verify_host) { + printf("%s verify %s; \n", indent, verify_host); + } + for (context_map::iterator i=children.begin(); i!=children.end(); i++) { CONTEXTP c = (*i).second; c->dump(level+1); @@ -682,6 +997,17 @@ //////////////////////////////////////////////// // +bool parse_verify(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_verify(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + char *host = tok.next(); + if (!tsa(tok, token_semi)) return false; + me.set_verify(host); + add_verify_host(host); +} + + +//////////////////////////////////////////////// +// bool parse_envfrom(TOKEN &tok, CONFIG &dc, CONTEXT &me); bool parse_envfrom(TOKEN &tok, CONFIG &dc, CONTEXT &me) { char *st = tok.next(); @@ -782,6 +1108,9 @@ else if (have == token_envto) { if (!parse_envto(tok, dc, *con)) return false; } + else if (have == token_verify) { + if (!parse_verify(tok, dc, *con)) return false; + } else if (have == token_envfrom) { if (!parse_envfrom(tok, dc, *con)) return false; } @@ -827,6 +1156,33 @@ //////////////////////////////////////////////// +// setup a new smtp verify host +// +void add_verify_host(char *host) { + verify_map::iterator i = verifiers.find(host); + if (i == verifiers.end()) { + VERIFYP v = new VERIFY(host); + verifiers[host] = v; + } +} + + +//////////////////////////////////////////////// +// thread to check for verify hosts with old sockets that we can close +// +void* verify_closer(void *arg) { + while (true) { + sleep(maxage); + for (verify_map::iterator i=verifiers.begin(); i!=verifiers.end(); i++) { + VERIFYP v = (*i).second; + v->closer(); + } + } + return NULL; +} + + +//////////////////////////////////////////////// // init the tokens // void token_init() { @@ -860,5 +1216,12 @@ token_substitute = register_string("substitute"); token_tld = register_string("tld"); token_unknown = register_string("unknown"); + token_verify = register_string("verify"); token_white = register_string("white"); + + if (gethostname(myhostname, HOST_NAME_MAX+1) != 0) { + strncpy(myhostname, "localhost", HOST_NAME_MAX+1); } + myhostname[HOST_NAME_MAX] = '\0'; // ensure null termination + token_myhostname = register_string(myhostname); +}