diff src/dccifd.cpp @ 196:ff6d14d75b1e

add missing files to cvs
author carl
date Sat, 02 Feb 2008 10:08:08 -0800
parents
children 92a5c866bdfa
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dccifd.cpp	Sat Feb 02 10:08:08 2008 -0800
@@ -0,0 +1,338 @@
+/*
+
+Copyright (c) 2007 Carl Byington - 510 Software Group, released under
+the GPL version 3 or any later version at your choice available at
+http://www.gnu.org/licenses/gpl-3.0.txt
+
+*/
+
+#include "includes.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <string>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.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 const char Id[] = "$Id$";
+
+const  int	maxlen = 1000;			// used for snprintf buffers
+extern int	NULL_SOCKET;
+const  char *options = "header\n";
+
+
+////////////////////////////////////////////////
+// helper to convert syslog control chars
+//
+string escaper(string v);
+string escaper(string v)
+{
+	size_t n = v.length();
+	char buf[n+1];
+	strncpy(buf, v.c_str(), n);
+	for (size_t i=0; i<n; i++) {
+		if (buf[i] == '\r') buf[i] = 'r';
+		if (buf[i] == '\n') buf[i] = 'n';
+		if ((unsigned char)(buf[i]) < ' '){
+			buf[i] = '.';
+		}
+	}
+	return string(buf, n);
+}
+
+
+DccInterface::DccInterface(char *port_, mlfiPriv *priv_, int ip, char *helo_, char *from)
+{
+	err 			= false;
+	first_recipient = true;
+	first_header	= true;
+	priv			= priv_;
+	ip4 			= ip;
+	helo			= helo_;
+	envfrom 		= from;
+	dccifd_port 	= port_;
+	dccifd_socket	= NULL_SOCKET;
+}
+
+
+DccInterface::~DccInterface()
+{
+	my_disconnect();
+}
+
+
+void DccInterface::mlfi_envrcpt(SMFICTX *ctx, char *envrcpt, bool grey)
+{
+	if (first_recipient) {
+		first_recipient = false;
+		char adr[sizeof "255.255.255.255   "];
+		adr[0] = '\0';
+		inet_ntop(AF_INET, (const u_char *)&ip4, adr, sizeof(adr));
+		// Validated sending site's address
+		char *rdns = getmacro(ctx, "_", "");
+		char buf[maxlen+1];
+		if (*rdns == '[') rdns = "";
+		else {
+			int n = 0;
+			while ((n < maxlen) && rdns[n] && (rdns[n] != ' ')) n++;
+			strncpy(buf, rdns, n);
+			buf[n] = '\0';
+			rdns = buf;
+		}
+		output(options);
+		output(adr);		output("\r");
+		output(rdns);		output("\n");
+		//output("4.3.2.1\r\n");  // !! not local whitelisting
+		output(helo);		output("\n");
+		output(envfrom);	output("\n");
+	}
+	output(envrcpt);
+	if (grey) output("\r\n");
+	else	  output("\rdnsblnogrey\n");
+}
+
+
+void DccInterface::mlfi_header(SMFICTX *ctx, char* headerf, char* headerv)
+{
+	if (dccifd_socket == NULL_SOCKET) Connect();
+	if ((dccifd_socket != NULL_SOCKET) && (!dccifd_input.empty())) {
+		output(dccifd_input);
+		dccifd_input.clear();
+	}
+
+	if (first_header) {
+		output("\n");
+		first_header = false;
+	}
+
+	output(headerf);
+	output(": ");
+	output(headerv);
+	output("\r\n");
+}
+
+
+void DccInterface::mlfi_eoh()
+{
+	output("\r\n");
+}
+
+
+void DccInterface::mlfi_body(u_char *bodyp, size_t bodylen)
+{
+	output((char *)bodyp, bodylen);
+}
+
+
+void DccInterface::mlfi_eom(bool &grey, int &bulk)
+{
+	// AnAnX-DCC-Rhyolite-Metrics: ns.five-ten-sg.com 104; Body=2 Fuz1=2nn
+
+	close_output(); // signal EOF to DccInterface
+	input();		// read what the dcc has to say about this message
+	my_syslog(priv, "dcc returned " + escaper(dccifd_output));
+	grey = false;
+	bulk = 0;
+	const int n = dccifd_output.length();
+	char buf[n+1];
+	strncpy(buf, dccifd_output.c_str(), n);
+	buf[n] = '\0';
+
+	int newlines = 0;
+	int j, i = 0;
+	while ((i<n) && (newlines < 2)) {
+		switch (buf[i++]) {
+			case 'G' :
+				grey = true;
+				break;
+			case '\n' :
+				newlines++;
+			default : ;
+		}
+	}
+
+	// skip up to and including ;
+	while ((i<n) && (buf[i++] != ';'));
+
+	// convert to lower, = to space, ctrl-chars to space
+	for (j=i; j<n; j++) {
+		buf[j] = tolower(buf[j]);
+		if (buf[j] == '=') buf[j] = ' ';
+		if (buf[j] <  ' ') buf[j] = ' ';
+	}
+
+	while (i<n) {
+		// skip leading blanks
+		while ((i<n) && (buf[i] == ' ')) i++;
+
+		// find blank terminator
+		for (j=i; (j<n) && (buf[j] != ' '); j++);
+
+		// find anything?
+		if (j > i) {
+			// process this token
+			buf[j] = '\0';
+			//my_syslog(priv, string("dccifd token") + (buf+i));
+			if (strcmp(buf+i, "bulk") == 0)           bulk = dccbulk;
+			else if (strcmp(buf+i, "many") == 0)      bulk = dccbulk;
+			else if (strcmp(buf+i, "whitelist") == 0) bulk = 0;
+			else if (isdigit(buf[i])) {
+				int b = atoi(buf+i);
+				if (b > bulk) bulk = b;
+			}
+			// skip this token
+			i = j+1;
+		}
+	}
+	char buff[maxlen];
+	snprintf(buff, sizeof(buff), "dccifd found grey %s bulk %d", ((grey) ? "yes" : "no"), bulk);
+	//my_syslog(priv, buff);
+}
+
+
+void DccInterface::my_disconnect()
+{
+	if (dccifd_socket != NULL_SOCKET) {
+		shutdown(dccifd_socket, SHUT_RDWR);
+		close(dccifd_socket);
+		dccifd_socket = NULL_SOCKET;
+	}
+}
+
+
+void DccInterface::Connect()
+{
+	if (err) return;
+
+	sockaddr_un server;
+	memset(&server, '\0', sizeof(server));
+	server.sun_family = AF_UNIX;
+	strncpy(server.sun_path, dccifd_port, sizeof(server.sun_path)-1);
+	dccifd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (dccifd_socket != NULL_SOCKET) {
+		bool rc = (connect(dccifd_socket, (sockaddr *)&server, sizeof(server)) == 0);
+		if (!rc) {
+			my_disconnect();
+			err = true;
+		}
+	}
+}
+
+
+size_t DccInterface::my_write(const char *buf, size_t len) {
+	if (err) return 0;
+	size_t rs = 0;
+	while (len) {
+		ssize_t ws = write(dccifd_socket, buf, len);
+		if (ws > 0) {
+			rs	+= ws;
+			len -= ws;
+			buf += ws;
+		}
+		else {
+			// error or peer closed the socket!
+			rs = 0;
+			err = true;
+			break;
+		}
+	}
+	return rs;
+}
+
+size_t DccInterface::my_read(char *buf, size_t len) {
+	if (err) return 0;
+	size_t rs = 0;
+	while (len) {
+		ssize_t ws = read(dccifd_socket, buf, len);
+		if (ws > 0) {
+			rs	+= ws;
+			len -= ws;
+			buf += ws;
+		}
+		else if (ws < 0) {
+			// read error
+			rs = 0;
+			err = true;
+			break;
+		}
+		else {
+			// peer closed the socket, end of file
+			break;
+		}
+	}
+	return rs;
+}
+
+void DccInterface::output(const char* buffer, size_t size)
+{
+	// if there are problems, fail.
+	if (err) return;
+
+	// buffer it if not connected yet
+	if (dccifd_socket == NULL_SOCKET) {
+		//my_syslog(priv, string("dcc buffered ") + escaper(string(buffer, size)));
+		dccifd_input.append(buffer, size);
+		return;
+	}
+
+	// write it if we are connected
+	//my_syslog(priv, string("dcc write ") + escaper(string(buffer, size)));
+	my_write(buffer, size);
+}
+
+
+void DccInterface::output(const char* buffer)
+{
+	output(buffer, strlen(buffer));
+}
+
+
+void DccInterface::output(string buffer)
+{
+	output(buffer.c_str(), buffer.size());
+}
+
+
+void DccInterface::close_output()
+{
+	if (dccifd_socket != NULL_SOCKET) {
+		shutdown(dccifd_socket, SHUT_WR);
+	}
+}
+
+
+void DccInterface::input()
+{
+	if ((dccifd_socket == NULL_SOCKET) || err) return;
+	char buf[maxlen];
+	int rs;
+	while (rs = my_read(buf, maxlen)) {
+		//my_syslog(priv, string("dcc read ") + escaper(string(buf, rs)));
+		dccifd_output.append(buf, rs);
+	}
+	my_disconnect();
+}
+
+
+char *DccInterface::getmacro(SMFICTX *ctx, char *macro, char *def)
+{
+	char *rc = smfi_getsymval(ctx, macro);
+	if (!rc) rc = def;
+	return rc;
+}
+
+