diff options
author | Roger Dingledine <arma@torproject.org> | 2002-06-26 22:45:49 +0000 |
---|---|---|
committer | Roger Dingledine <arma@torproject.org> | 2002-06-26 22:45:49 +0000 |
commit | 9a928eeb1215f0d7c9b6d0bb9e4571d0a16ed79a (patch) | |
tree | fac560bf2dce8a8d2b82e296b71ff24f59ab1a7a /src/smtpap | |
parent | 766a465a6043ac4e643c398feb14f708fd0d863f (diff) | |
download | tor-9a928eeb1215f0d7c9b6d0bb9e4571d0a16ed79a.tar tor-9a928eeb1215f0d7c9b6d0bb9e4571d0a16ed79a.tar.gz |
Initial revision
svn:r2
Diffstat (limited to 'src/smtpap')
-rw-r--r-- | src/smtpap/Makefile | 80 | ||||
-rw-r--r-- | src/smtpap/io.c | 133 | ||||
-rw-r--r-- | src/smtpap/io.h | 2 | ||||
-rw-r--r-- | src/smtpap/smtpap.c | 1393 | ||||
-rw-r--r-- | src/smtpap/smtpap.h | 90 |
5 files changed, 1698 insertions, 0 deletions
diff --git a/src/smtpap/Makefile b/src/smtpap/Makefile new file mode 100644 index 000000000..e10605e0a --- /dev/null +++ b/src/smtpap/Makefile @@ -0,0 +1,80 @@ +SRC=smtpap.c io.c +OBJ=${SRC:.c=.o} +PROGS=smtpap +LIB= +LIBS= +INCLUDE = + +CFLAGS= $(INCLUDE) -Wall -Wpointer-arith -O2 -ggdb +LDFLAGS = $(LIB) $(LIBS) + +all: ${PROGS} + +clean: + rm -f *.o ${PROGS} + +depend: + makedepend -- ${CFLAGS} -- ${SRC} + +${PROGS}: ${OBJ} + $(LINK.c) -o $@ $(OBJ) ../common/log.o ../common/config.o ../common/utils.o + +# DO NOT DELETE + + +io.o: ../common/log.h ../common/utils.h io.h smtpap.h +io.o: /usr/include/bits/endian.h /usr/include/bits/pthreadtypes.h +io.o: /usr/include/bits/sched.h /usr/include/bits/select.h +io.o: /usr/include/bits/sigset.h /usr/include/bits/sockaddr.h +io.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +io.o: /usr/include/bits/types.h /usr/include/bits/wchar.h +io.o: /usr/include/endian.h /usr/include/features.h /usr/include/_G_config.h +io.o: /usr/include/gconv.h /usr/include/gnu/stubs.h /usr/include/libio.h +io.o: /usr/include/malloc.h /usr/include/stdio.h /usr/include/string.h +io.o: /usr/include/sys/cdefs.h /usr/include/syslog.h /usr/include/sys/select.h +io.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h +io.o: /usr/include/sys/time.h /usr/include/sys/types.h /usr/include/sys/un.h +io.o: /usr/include/time.h /usr/include/wchar.h /usr/include/xlocale.h +io.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +io.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +smtpap.o: ../common/config.h ../common/log.h ../common/ss.h ../common/utils.h +smtpap.o: ../common/version.h io.h smtpap.h /usr/include/alloca.h +smtpap.o: /usr/include/arpa/inet.h /usr/include/asm/errno.h +smtpap.o: /usr/include/asm/sigcontext.h /usr/include/asm/socket.h +smtpap.o: /usr/include/asm/sockios.h /usr/include/bits/byteswap.h +smtpap.o: /usr/include/bits/confname.h /usr/include/bits/endian.h +smtpap.o: /usr/include/bits/environments.h /usr/include/bits/errno.h +smtpap.o: /usr/include/bits/in.h /usr/include/bits/local_lim.h +smtpap.o: /usr/include/bits/netdb.h /usr/include/bits/posix1_lim.h +smtpap.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h +smtpap.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/resource.h +smtpap.o: /usr/include/bits/sched.h /usr/include/bits/select.h +smtpap.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h +smtpap.o: /usr/include/bits/siginfo.h /usr/include/bits/signum.h +smtpap.o: /usr/include/bits/sigset.h /usr/include/bits/sigstack.h +smtpap.o: /usr/include/bits/sigthread.h /usr/include/bits/sockaddr.h +smtpap.o: /usr/include/bits/socket.h /usr/include/bits/stdio_lim.h +smtpap.o: /usr/include/bits/time.h /usr/include/bits/types.h +smtpap.o: /usr/include/bits/uio.h /usr/include/bits/waitflags.h +smtpap.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h +smtpap.o: /usr/include/bits/wordsize.h /usr/include/bits/xopen_lim.h +smtpap.o: /usr/include/ctype.h /usr/include/endian.h /usr/include/errno.h +smtpap.o: /usr/include/features.h /usr/include/_G_config.h +smtpap.o: /usr/include/gconv.h /usr/include/getopt.h /usr/include/gnu/stubs.h +smtpap.o: /usr/include/libio.h /usr/include/limits.h +smtpap.o: /usr/include/linux/errno.h /usr/include/linux/limits.h +smtpap.o: /usr/include/netdb.h /usr/include/netinet/in.h +smtpap.o: /usr/include/rpc/netdb.h /usr/include/signal.h /usr/include/stdint.h +smtpap.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h +smtpap.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +smtpap.o: /usr/include/sys/resource.h /usr/include/sys/select.h +smtpap.o: /usr/include/sys/socket.h /usr/include/sys/syslog.h +smtpap.o: /usr/include/sys/sysmacros.h /usr/include/sys/time.h +smtpap.o: /usr/include/sys/types.h /usr/include/sys/ucontext.h +smtpap.o: /usr/include/sys/uio.h /usr/include/sys/un.h /usr/include/sys/wait.h +smtpap.o: /usr/include/time.h /usr/include/ucontext.h /usr/include/unistd.h +smtpap.o: /usr/include/wait.h /usr/include/wchar.h /usr/include/xlocale.h +smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h +smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h diff --git a/src/smtpap/io.c b/src/smtpap/io.c new file mode 100644 index 000000000..57ea72854 --- /dev/null +++ b/src/smtpap/io.c @@ -0,0 +1,133 @@ +#include <sys/time.h> +#include <stdarg.h> +#include <stdio.h> +#include <malloc.h> + +#include "../common/log.h" +#include "../common/utils.h" + +#include "smtpap.h" +#include "io.h" + +/* connection timeout */ +extern struct timeval *conn_toutp; + +/* printf-like function used to send messages to a socket */ +int sendmessage(int s, char *buf, size_t buflen, const char *format, ...) +{ + int retval = 0; + va_list ap; + + if (!buf) + return -1; + + va_start(ap,format); + retval = vsnprintf(buf,buflen, format, ap); + va_end(ap); + + if (retval < 0) + { + log(LOG_DEBUG,"sendmessage() : could not print to buffer"); + return -1; + } + + log(LOG_DEBUG,"sendmessage() : printed this to buffer : %s",buf); + + retval = write_tout(s,buf,(size_t)retval, conn_toutp); + if (retval < 0) + { + log(LOG_DEBUG,"sendmessage() : could not send"); + return -1; + } + + return retval; +} + +/* receive a response from the recipient SMTP server into *op_in + * Can handle multi-line responses. */ +int receive(int s, char **inbuf,size_t *inbuflen, int flags) +{ + int inputlen = 0; /* running total length of the input */ + int inputerror = 0; /* has an error occured? */ + int retval = 0; /* used for saving function return values */ + + /* for processing multi-line responses */ + int i=0; + + /* storing old values of *inbuf and *inbuflen */ + char *inbuf_old = NULL; + size_t inbuflen_old=0; + + if ((!inbuf) || (!*inbuf) || (!inbuflen)) + return -1; + + /* saving old values in case we need to restore them */ + inbuf_old = *inbuf; + inbuflen_old = *inbuflen; + + do + { + if (inputlen == *inbuflen-1) /* we need to increase the buffer size */ + { + /* increase the size of the buffer */ + *inbuflen += 512; + + *inbuf = (char *)realloc(*inbuf,(size_t)*inbuflen); + if (!*inbuf) + { + log(LOG_ERR,"Could not allocate memory."); + *inbuf = inbuf_old; + *inbuflen = inbuflen_old; + inputerror = 1; + break; + } + } + + retval=read_tout(s,*inbuf+inputlen,(size_t)(*inbuflen-inputlen-1),flags, conn_toutp); /* subtract 1 from inbuflen to leave space for \0 */ + if (retval <= 0) + { + log(LOG_ERR,"Error occured while receiving data."); + inputerror = 1; + break; + } + else + { + inputerror = 0; + inputlen += retval; + + /* exit clause if we have received CRLF, otherwise we need to keep reading*/ + /* also keep on reading if it's a multi-line response */ + if (inputlen >= SMTPAP_CRLF_LEN) + { + if (!strncmp(*inbuf+inputlen-SMTPAP_CRLF_LEN,SMTPAP_CRLF,SMTPAP_CRLF_LEN)) /* entire line received */ + { + /* now check wether we should expect more lines */ + /* find the <CRLF> sequence which occurs one before last */ + for(i=inputlen-SMTPAP_CRLF_LEN-1; i > 0; i--) /* move backwards, start just before the final CRLF */ + { + if ((*inbuf)[i] == SMTPAP_LF) /* got a LF */ + { + /* check for a CR preceding it */ + if ((*inbuf)[i-1] == SMTPAP_CR) /* got a CR */ + break; + } + } + if (i==0) /* correct the offset if no CRLF found */ + i=-1; + + /* check the 4th character after the <CRLF> to see if it is - or <SP> */ + if ((*inbuf)[i+4] != '-') /* no more lines */ + break; + } + } + } + } while(1); + + if (!inputerror) + { + (*inbuf)[inputlen]=0; /* add the terminating NULL character */ + return inputlen; + } + + return -1; +} diff --git a/src/smtpap/io.h b/src/smtpap/io.h new file mode 100644 index 000000000..d5515adb4 --- /dev/null +++ b/src/smtpap/io.h @@ -0,0 +1,2 @@ +int sendmessage(int s, char *buf, size_t buflen, const char *format, ...); +int receive(int s, char **inbuf,size_t *inbuflen, int flags); diff --git a/src/smtpap/smtpap.c b/src/smtpap/smtpap.c new file mode 100644 index 000000000..701dde809 --- /dev/null +++ b/src/smtpap/smtpap.c @@ -0,0 +1,1393 @@ +/** + * smtpap.c + * SMTP Application Proxy for Onion Routing + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.32 2002/04/02 14:29:49 badbytes + * Final finishes. + * + * Revision 1.31 2002/03/25 08:03:17 badbytes + * Added header sanitization. + * + * Revision 1.30 2002/03/02 23:54:06 mp292 + * Fixed missing CRLFs at the end of error messages. + * + * Revision 1.29 2002/01/29 01:00:10 mp292 + * All network operations are now timeoutable. + * + * Revision 1.28 2002/01/28 21:38:18 mp292 + * Fixed bugs in RSET handling. Added Anonimize option which signifies whether + * the router should falsify the identity of the sender or not. + * + * Revision 1.27 2002/01/26 22:45:02 mp292 + * Now handles SS_ERROR_INVALID_PORT. + * + * Revision 1.26 2002/01/26 22:33:21 mp292 + * Removed hard-coded values for onion proxy return codes. Also fixed a bug in + * parameter checking. + * + * Revision 1.25 2002/01/26 21:58:27 mp292 + * Added some missing parameter checking. + * + * Revision 1.24 2002/01/26 21:50:17 mp292 + * Reviewed according to Secure-Programs-HOWTO. Still need to deal with network + * timeouts. + * + * Revision 1.23 2002/01/18 21:07:02 mp292 + * (a) THe form of HELO is now HELO Anonymous.Smtp.Daemon rather than the real + * address. (b) The user *can* now specify a default SMTP daemon to route through + * although this is insecure and not recommended. + * + * Revision 1.22 2002/01/16 23:01:58 mp292 + * First phase of system testing completed (main functionality). + * + * Revision 1.21 2002/01/16 17:04:01 mp292 + * Bug in checking whether incoming connection is local or not. + * + * Revision 1.20 2002/01/09 09:18:22 badbytes + * Now handles EINTR error from accept(). + * + * Revision 1.19 2001/12/19 11:15:27 badbytes + * Corrected AF_INET to PF_INET in socket() calls. + * + * Revision 1.18 2001/12/19 08:36:04 badbytes + * Incorrect error checking in recv() calls caused zombies ... fixed + * + * Revision 1.17 2001/12/18 14:42:46 badbytes + * Variable name op_port_str was incorrectly named, changed to dest_port_str + * + * Revision 1.16 2001/12/18 13:20:16 badbytes + * Some error messages did not include a terminating <CRLF> + * + * Revision 1.15 2001/12/18 12:37:23 badbytes + * Found an overflow bug ... + * + * Revision 1.14 2001/12/18 09:17:31 badbytes + * Corrected a spelling mistake in print_usage() + * + * Revision 1.13 2001/12/14 13:13:24 badbytes + * Changed types.h references to ss.h + * + * Revision 1.12 2001/12/14 09:17:25 badbytes + * Moved function stolower(char *str) from smtpap.c to common/utils.c + * + * Revision 1.11 2001/12/13 13:51:05 badbytes + * Fixed a bug in processing command-line parameters. + * + * Revision 1.10 2001/12/13 13:36:44 badbytes + * Now accepts the -l command-line option which specifies the logging threshold. + * + * Revision 1.9 2001/12/12 16:02:29 badbytes + * Testing completed. + * + * Revision 1.8 2001/12/11 16:30:20 badbytes + * Some bugs removed, still testing though. + * + * Revision 1.7 2001/12/11 14:12:20 badbytes + * Onion Proxy connection setup completed. Proceeding to test. + * + * Revision 1.6 2001/12/11 10:43:21 badbytes + * MAIL and RCPT handling completed. Still coding connection to Onion Proxy. + * + * Revision 1.5 2001/12/10 16:10:35 badbytes + * Wrote a tokenize() function to help with parsing input from SMTP clients. + * + * Revision 1.4 2001/12/07 15:02:43 badbytes + * Server setup code completed. + * + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <errno.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <wait.h> +#include <stdarg.h> +#include <ctype.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> + +#include "../common/log.h" +#include "../common/config.h" +#include "../common/ss.h" +#include "../common/utils.h" +#include "../common/version.h" + +#include "smtpap.h" +#include "io.h" + +int loglevel = LOG_ERR; +struct timeval conn_tout; +struct timeval *conn_toutp = &conn_tout; + +/* valid command-line options */ +static const char *args = "hf:p:l:"; + +/* valid config file options */ +static config_opt_t options[] = +{ + {"OnionProxy", CONFIG_TYPE_INT, {0}, 0}, + {"MaxConn", CONFIG_TYPE_INT, {0}, 0}, + {"Anonimize", CONFIG_TYPE_INT, {0}, 0}, + {"ConnTimeout", CONFIG_TYPE_INT, {0}, 0}, + {0} +}; +enum opts { + OnionProxy=0,MaxConn, Anonimize, ConnTimeout +}; + +/* number of open connections */ +int connections=0; + +/* prints help on using smtpap */ +void print_usage() +{ + char *program = "smtpap"; + + printf("\n%s - SMTP application proxy for Onion Routing.\nUsage : %s -f config [-p port -l loglevel -h]\n-h : display this help\n-f config : config file\n-p port : port number which %s should bind to\n-l loglevel : logging threshold; one of alert|crit|err|warning|notice|info|debug\n\n", program,program,program); +} + +/* used for reaping zombie processes */ +void sigchld_handler(int s) +{ + while (wait(NULL) > 0); + connections--; +} + +/* takes the contents of a RCPT command in a null-terminated string and retrieves the address + * of the corresponding recipient domain*/ +char *extract_smtp_dest(char *rcptbuf) +{ + char *dest_smtp=NULL; + char *pos1, *pos2; + + if (!rcptbuf) + return NULL; + + pos1 = (char *)strchr(rcptbuf,'@'); + if (pos1 == NULL) + return NULL; + + pos2 = (char *)strpbrk(pos1,SMTPAP_PATH_SEPCHARS); + if (pos2 == NULL) + return NULL; + else + { + dest_smtp = (char *)malloc((size_t)(pos2-pos1)); + if (!dest_smtp) + { + log(LOG_ERR,"Could not allocate memory."); + return NULL; + } + else + { + strncpy(dest_smtp,pos1+1,(size_t)(pos2-pos1-1)); + dest_smtp[pos2-pos1-1] = 0; + } + } + + return dest_smtp; +} + +/* process a DATA stream and remove any e-mail headers */ +int sanitize_data(unsigned char **buf, int *buflen) +{ + unsigned char *offset; /* offset to data after the last header */ + unsigned char *crlf = NULL; + unsigned char *colon = NULL; + unsigned char *line; + unsigned char *newbuf; + int newbuflen; + + if ((!buf) || (!buflen)) /* invalid parameters */ + return -1; + + offset = *buf; + line = *buf; + /* process the data line by line and discard anything that looks like a header */ + while(1) + { + /* find the end of line */ + crlf = strstr(line, SMTPAP_CRLF); + if (crlf) + { + colon = (unsigned char *)memchr((void *)line,(int)':',crlf-line); + if (!colon) + break; /* this doesn't seem to be a header, can stop */ + else + offset = crlf + 2; /* discard this line */ + + line = crlf + 2; /* move on to the next line */ + } + else /* no more CRLFs found, end of data */ + /* NB : there is no need to check the current line at this stage as this will be of the form <CRLF>.<CRLF> */ + /* we should never reach this point in the code anyway, the '.' will be trapped as a non-header line in the above code */ + break; + } + + if (offset != *buf) /* data changed */ + { + newbuflen = *buflen - (offset - *buf); + newbuf = (unsigned char *)malloc(newbuflen+1); /* leave space for a terminating NULL character */ + if (!newbuf) /* malloc() error */ + return -1; + else + { + /* copy into the new buffer */ + memcpy((void *)newbuf, (void *)offset, newbuflen); + newbuf[newbuflen] = 0; + + /* replace the old buffer with the new one */ + free((void *)*buf); + *buf = newbuf; + *buflen = newbuflen; + } + } + + return 0; +} + +/* main logic of smtpap */ +int handle_connection(int s, struct hostent *local, struct sockaddr_in remote, u_short op_port) +{ + int retval = 0; + int state = 0; /* 0 - start / RSET received + * 1 - connection not local, waiting for QUIT + * 2 - connection local, waiting for HELO/EHLO + * 3 - HELO/EHLO received, waiting for MAIL + * 4 - MAIL received, waiting for RCPT + * 5 - waiting for DATA + * 6 - DATA received, accepting data + * - data accepted, back to state 3 + */ + int islocal = 0; + char *cp; + int i=0; + char message[512]; /* for storing outgoing messages */ + char *inbuf = NULL; /* for storing incoming messages */ + char *token = NULL; /* next token in the incoming message */ + char *tmpbuf = NULL; /* temporary buffer for copying data */ + char *mailbuf = NULL; /* storing the MAIL command */ + char **rcptarray = NULL; /* storing a NULL-terminated array of RCPT commands */ + char *rcptbuf = NULL; /* storing a single RCPT command */ + int tmpbuflen = 0; /* length of tmpbuflen in bytes */ + int inbuflen = 0; /* length of inbuf in bytes */ + int inputlen = 0; /* length of actual input in bytes */ + int mailbuflen=0; /* etc ... */ + int rcptbuflen=0; + int inputerror=0; /* error occured when receiving data from the client */ + + /* the following is used for conecting to the SMTP host through the OR network */ + char *dest_addr_str = NULL; /* for storing the ASCII address of the destination SMTP */ + int sop=-1; /* socket for connecting to the onion proxy */ + struct sockaddr_in op_addr; /* stores the address of the onion proxy */ + ss_t ss; /* standard structure */ + char dest_port_str[6]; /* ascii representation of the destination port */ + /* input and output buffers for talking to the onion proxy */ + char *op_out = NULL; + char *op_in = NULL; + int op_outlen = 0; + int op_inlen = 0; + + int partial_dataend = 0; /* used for recognising the <CRLF>.<CRLF> sequence that ends the DATA segment */ + + if (!local) + return -1; + + log(LOG_DEBUG, "handle_connection() : Local address = %s.", inet_ntoa(*(struct in_addr *)local->h_addr)); + log(LOG_DEBUG, "handle_connection() : Remote address = %s.", inet_ntoa(remote.sin_addr)); + + /* first check that the connection is from the local host, otherwise reject */ + if (*(uint32_t *)&remote.sin_addr == inet_addr("127.0.0.1")) + islocal = 1; + for (i=0; (local->h_addr_list[i] != NULL) && (!islocal); i++) + { + cp = local->h_addr_list[i]; + log(LOG_DEBUG,"handle_connection() : Checking if connection is from address %s.",inet_ntoa(*(struct in_addr *)cp)); + if (!memcmp(&remote.sin_addr, cp, sizeof(struct in_addr))) + islocal = 1; + } + + if (islocal) + { + log(LOG_DEBUG,"handle_connection() : Connection seems to be local. Will accept."); + state = 2; + sendmessage(s, (char *)message, (size_t)512, "220 This is smtpap v1.0 running on %s.%s",local->h_name,SMTPAP_CRLF); + } + else + { + log(LOG_DEBUG,"handle_connection() : Connection doesn't seem to be local. Will reject."); + state = 1; + sendmessage(s,(char *)message, (size_t)512,"554 smtpap v1.0 Connection refused. Only local connections allowed.%s",SMTPAP_CRLF); + } + + /* initially allocate 512 bytes for incoming message buffer */ + inbuf = (char *)malloc((size_t)512); + if (!inbuf) + { + log(LOG_ERR,"Could not allocate memory."); + return -1; + } + inbuflen = 512; + + /* initially allocate 512 bytes for the temporary buffer */ + tmpbuf = (char *)malloc((size_t)512); + if (!tmpbuf) + { + log(LOG_ERR,"Could not allocate memory."); + free(inbuf); + return -1; + } + tmpbuflen = 512; + + while(1) + { + inputlen = 0; + do + { + if (inputlen == inbuflen-1) /* we need to increase the buffer size */ + { + /* increase the size of the buffers */ + inbuflen += 512; + tmpbuflen += 512; + + inbuf = (char *)realloc(inbuf,(size_t)inbuflen); + if (!inbuf) + { + log(LOG_ERR,"Could not allocate memory."); + inputerror = 1; + break; + } + + tmpbuf = (char *)realloc(tmpbuf,(size_t)tmpbuflen); + if (!tmpbuf) + { + log(LOG_ERR,"Could not allocate memory."); + free(inbuf); + inputerror = 1; + break; + } + } + + retval=read_tout(s,inbuf+inputlen,(size_t)(inbuflen-inputlen-1),0, conn_toutp); /* subtract 1 from inbuflen to leave space for \0 */ + if (retval <= 0) + { + log(LOG_ERR,"Error occured while receiving data."); + inputerror = 1; + break; + } + else + { + inputerror = 0; + inputlen += retval; + + /* exit clause if we have received CRLF or SMTPAP_ENDDATA, otherwise we need to keep reading*/ + if ( (state == 6) && (inputlen >= SMTPAP_ENDDATA_LEN) ) + { + if (!strncmp(inbuf+inputlen-SMTPAP_ENDDATA_LEN,SMTPAP_ENDDATA,SMTPAP_ENDDATA_LEN)) + break; + } + else if ( (state != 6) && (inputlen >= SMTPAP_CRLF_LEN) ) + { + if (!strncmp(inbuf+inputlen-SMTPAP_CRLF_LEN,SMTPAP_CRLF,SMTPAP_CRLF_LEN)) + break; + } + } + } while(1); + + if (inputerror != 0) + break; + + if (*inbuf == EOF) + { + log(LOG_DEBUG,"handle_connection() : Received EOF. Exiting."); + break; + } + + inbuf[inputlen]=0; /* add the terminating NULL character */ + log(LOG_DEBUG, "Received this from client : %s",inbuf); + + /* save a copy of inbuf into tmpbuf, because calls to strtok() will change it */ + strcpy(tmpbuf,inbuf); + + /* now handle input depending on the state */ + + /* first check for a quit */ + token = stolower((char *)strtok(inbuf,SMTPAP_SEPCHARS)); + log(LOG_DEBUG,"handle_connection() : First token is %s.",token); + if ((!strcmp(token,SMTPAP_QUIT)) && (state != 6)) /* QUIT command - but doesn't count in state 6 + * That's when we are receiving DATA input + */ + { + sendmessage(s,(char *)message, (size_t)512,"221 %s closing connection. Goodbye.%s",local->h_name,SMTPAP_CRLF); + break; + } + /* check for a RSET */ + if ((!strcmp(token,SMTPAP_RSET)) && (state !=6)) /* RSET command - again, doesn't count in state 6 */ + { + sendmessage(s,(char *)message,(size_t)512,"250 RSET received.%s",SMTPAP_CRLF); + /* clean up message state */ + if (mailbuf != NULL) + { + free(mailbuf); + mailbuf = NULL; + } + if (rcptarray != NULL) + { + free(rcptarray); + rcptarray = NULL; + } + if (rcptbuf != NULL) + { + free(rcptbuf); + rcptbuf=NULL; + } + + close(sop); + + /* set state to 2/3 (depending on wether we have recieved HELO yet) and loop back and start again */ + if (state != 2) + state=3; + + continue; + } + + if (state == 1) + { + sendmessage(s,(char *)message, (size_t)512,"503 Connection refused. Please QUIT.%s",SMTPAP_CRLF); + } + else if (state == 2) + { + if ((!strcmp(token,SMTPAP_HELO)) || (!strcmp(token,SMTPAP_EHLO))) + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (!token) /* no more tokens in inbuf */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Received HELO/EHLO without arguments."); + sendmessage(s,(char *)message,(size_t)512,"500 HELO/EHLO requires domain address.%s",SMTPAP_CRLF); + } + else + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Received HELO/EHLO with the following domain address : %s.",token); + state =3; + sendmessage(s,(char *)message,(size_t)512,"250 Hello user at %s. Pleased to meet you.%s",inet_ntoa(remote.sin_addr),SMTPAP_CRLF); + } + } + else + sendmessage(s,(char *)message,(size_t)512,"503 Expecting either HELO/EHLO or QUIT.%s",SMTPAP_CRLF); + } + else if (state == 3) + { + int further_check=0; + if ((!strncmp(token,SMTPAP_MAIL,SMTPAP_MAIL_LEN))) + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (!token) + { + sendmessage(s,(char *)message,(size_t)512,"500 MAIL requires From:<sender@address> .%s",SMTPAP_CRLF); + } + else + { + stolower(token); + if (!strcmp(token,"from:")) /* from: separate from the address */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* expected another parameter but it's not there */ + { + log(LOG_DEBUG,"handle_connection() : Received MAIL From: without an address."); + sendmessage(s,(char *)message,(size_t)512,"500 MAIL From: requires sender address.%s",SMTPAP_CRLF); + further_check = 0; + } + else /* continue further checking */ + further_check = 1; + } + else if (!strcmp(token,"from")) /* probably from : address */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* not enough parameters */ + { + log(LOG_DEBUG,"handle_connection() : Received Mail From with no other parameters."); + sendmessage(s,(char *)message,(size_t)512, "500 MAIL From: requires sender address.%s",SMTPAP_CRLF); + further_check=0; + } + else if ( (token[0] == ':') && (token[1]!='\0') ) /* contains :address */ + { + token++; + further_check=1; + } + else if ( (token[0] == ':') && (token[1]=='\0') )/* the address is in the next token */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* not enough parameters */ + { + log(LOG_DEBUG,"handle_connection() : Received Mail From : with no other parameters."); + sendmessage(s,(char *)message,(size_t)512,"500 MAIL From: requires sender address.%s",SMTPAP_CRLF); + further_check = 0; + } + else /* continue further checking */ + further_check =1; + } + else /* couldn't find a colon (:) */ + { + log(LOG_DEBUG,"handle_connection() : Couldn't find a colon in the received MAIL command."); + sendmessage(s,(char *)message,(size_t)512,"500 There is a colon (:) missing in that command.%s",SMTPAP_CRLF); + further_check = 1; + } + } + else /* probably from:address */ + { + if (!strncmp(token,"from:",5)) /* string starts with from: */ + { + token += 5; /* skip the from: bit */ + further_check = 1; /* continue further checking */ + } + else /* error */ + { + log(LOG_DEBUG,"handle_connection() : MAIL parameters don't start with from: ."); + sendmessage(s,(char *)message,(size_t)512,"500 MAIL requires From:<sender@address>.%s",SMTPAP_CRLF); + further_check=0; + } + } + if (further_check == 1) /* check that this is in the correct, format - we can't handle anything else + * but straightforward <user@host> representation, <> optional + */ + { + if (((cp = (char *)strchr(token,',')) != NULL) || ((cp = (char *)strchr(token,':')) != NULL)) /* path contains , or : - can't cope with that */ + { + log(LOG_DEBUG,"handle_connection() : The client is probably trying to specify a reverse path, which I can't handle."); + sendmessage(s,(char *)message,(size_t)512,"500 I can only handle a simple return address.%s",SMTPAP_CRLF); + } + else if ((cp = (char *)strchr(token,'@')) == NULL) /* no @, that is most likely a problem :-) */ + { + log(LOG_DEBUG,"handle_connection() : The client specified a sender address with no @."); + sendmessage(s,(char *)message,(size_t)512,"500 Domain name required.%s",SMTPAP_CRLF); + } + else /* the mail command seems to be OK, save it */ + { + if (mailbuf != NULL) + free(mailbuf); + mailbuflen = strlen(tmpbuf) + 1; + mailbuf = (char *)malloc(mailbuflen); + if (!mailbuf) + { + log(LOG_ERR,"Could not allocate memory."); + sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF); + } + else + { + strncpy(mailbuf,tmpbuf,mailbuflen); + mailbuf[mailbuflen-1] = '\0'; /* add the terminating NULL character */ + log(LOG_DEBUG,"handle_connection() : MAIL command saved as %s.",mailbuf); + + /* send an OK response to the client */ + sendmessage(s,(char *)message,(size_t)512,"250 Sender address OK.%s",SMTPAP_CRLF); + state=4; + } + } + } + } + } + else + sendmessage(s,(char *)message, (size_t)512,"503 Need MAIL first.%s",SMTPAP_CRLF); + } + else if(state == 4) + { + int further_check=0; + if ((!strcmp(token,SMTPAP_RCPT))) + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (!token) + { + sendmessage(s,(char *)message,(size_t)512,"500 RCPT requires To:<recipient@address> .%s",SMTPAP_CRLF); + } + else + { + stolower(token); + if (!strcmp(token,"to:")) /* to: separate from the address */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* expected another parameter but it's not there */ + { + log(LOG_DEBUG,"handle_connection() : Received RCPT To: without an address."); + sendmessage(s,(char *)message,(size_t)512,"500 RCPT To: requires recipient address.%s",SMTPAP_CRLF); + further_check = 0; + } + else /* continue further checking */ + further_check = 1; + } + else if (!strcmp(token,"to")) /* probably to : address or to :address */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* not enough parameters */ + { + log(LOG_DEBUG,"handle_connection() : Received RCPT To with no other parameters."); + sendmessage(s,(char *)message,(size_t)512, "500 RCPT To: requires recipient address.%s",SMTPAP_CRLF); + further_check=0; + } + else if ( (token[0] == ':') && (token[1]!='\0') ) /* contains :address */ + { + token++; + further_check=1; + } + else if ( (token[0] == ':') && (token[1]=='\0') )/* the address is in the next token */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* not enough parameters */ + { + log(LOG_DEBUG,"handle_connection() : Received RCPT To : with no other parameters."); + sendmessage(s,(char *)message,(size_t)512,"500 RCPT To: requires recipient address.%s",SMTPAP_CRLF); + further_check = 0; + } + else /* continue further checking */ + further_check =1; + } + else /* couldn't find a colon (:) */ + { + log(LOG_DEBUG,"handle_connection() : Couldn't find a colon in the received RCPT command."); + sendmessage(s,(char *)message,(size_t)512,"500 There is a colon (:) missing in that command.%s",SMTPAP_CRLF); + further_check = 1; + } + } + else /* probably to:address */ + { + if (!strncmp(token,"to:",3)) /* string starts with from: */ + { + token += 3; /* skip the to: bit */ + further_check = 1; /* continue further checking */ + } + else /* error */ + { + log(LOG_DEBUG,"handle_connection() : RCPT parameters don't start with to: ."); + sendmessage(s,(char *)message,(size_t)512,"500 RCPT requires To:<recipient@address>.%s",SMTPAP_CRLF); + further_check=0; + } + } + if (further_check == 1) /* check that this is in the correct, format - we can't handle anything else + * but straightforward <user@host> representation, <> optional + */ + { + if (((cp = (char *)strchr(token,',')) != NULL) || ((cp = (char *)strchr(token,':')) != NULL)) /* path contains , or : - can't cope with that */ + { + log(LOG_DEBUG,"handle_connection() : The client is probably trying to specify a forward path, which I can't handle."); + sendmessage(s,(char *)message,(size_t)512,"500 I can only handle a simple recipient address.%s",SMTPAP_CRLF); + } + else if ((cp = (char *)strchr(token,'@')) == NULL) /* no @, that is most likely a problem :-) */ + { + log(LOG_DEBUG,"handle_connection() : The client specified a recipient address with no @."); + sendmessage(s,(char *)message,(size_t)512,"500 Domain name required.%s",SMTPAP_CRLF); + } + else /* the rcpt command seems to be OK, save it */ + { + if (rcptbuf != NULL) + { + free(rcptbuf); + rcptbuf = NULL; + } + rcptbuflen = strlen(tmpbuf) + 1; + rcptbuf = (char *)malloc(rcptbuflen); + if (!rcptbuf) + { + log(LOG_ERR,"Could not allocate memory."); + sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF); + } + else + { + strncpy(rcptbuf,tmpbuf,rcptbuflen); + rcptbuf[rcptbuflen-1] = '\0'; /* add the terminating NULL character */ + log(LOG_DEBUG,"handle_connection() : handle_connection : RCPT command saved."); + + /* attempt to connect to the destination SMTP server through the OR network */ + /* first extract the destination address */ + dest_addr_str = extract_smtp_dest(rcptbuf); + log(LOG_DEBUG,"handle_connection() : handle_connection : called extract_smtp_dest()"); + if (!dest_addr_str) + { + log(LOG_DEBUG,"handle_connection() : Could not extract a destination SMTP address from the specified recipient address."); + sendmessage(s,(char *)message,(size_t)512,"550 Could not extract destination domain.%s",SMTPAP_CRLF); + } + else + { + /* fill in the standard structure */ + ss.version = VERSION; + ss.protocol= SS_PROTOCOL_SMTP; + ss.retry_count = 0; + ss.addr_fmt = SS_ADDR_FMT_ASCII_HOST_PORT; + + /* open a socket for connecting to the proxy */ + sop = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (sop < 0) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Error opening socket."); + sendmessage(s,(char *)message,(size_t)512,"451 Could not connect to the onion proxy.%s",SMTPAP_CRLF); + if (dest_addr_str != NULL) { + free(dest_addr_str); + dest_addr_str = NULL; + } + } + else + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Socket opened."); + memset((void *)&op_addr,0,sizeof(op_addr)); /* clear the structure first */ + /* set up the sockaddr_in structure */ + op_addr.sin_family=AF_INET; + op_addr.sin_port=htons(op_port); + memcpy((void *)&op_addr.sin_addr,local->h_addr,local->h_length); + log(LOG_DEBUG,"handle_connection() : Trying to connect to %s at port %u.",inet_ntoa(*((struct in_addr *)local->h_addr)),op_port); + + /* try to connect */ + retval = connect(sop,(struct sockaddr *)&op_addr,sizeof(op_addr)); + if (retval == -1) + { + sendmessage(s,(char *)message,(size_t)512,"451 Could not connect to the onion proxy.%s",SMTPAP_CRLF); + close(sop); + if (dest_addr_str != NULL) + { + free(dest_addr_str); + dest_addr_str = NULL; + } + } + else /* connection established, now send the standard structure + address and wait for a response */ + { + /* write the message to the op_out buffer */ + snprintf(dest_port_str,6,"%u",htons(SMTPAP_DEFAULT_SMTP_PORT)); + + if (op_out != NULL) + { + free(op_out); + op_out = NULL; + } + + op_outlen = sizeof(ss) /* standard structure */ + + strlen(dest_addr_str) /* destination address */ + + 1 /* terminating NULL character */ + + strlen(dest_port_str) + + 1; /* terminating NULL character */ + op_out = (char *)malloc(op_outlen); + + if (!op_out) /* error */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Could not allocate memory."); + sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF); + close(sop); + if (dest_addr_str != NULL) + { + free(dest_addr_str); + dest_addr_str = NULL; + } + } + else + { + memcpy(op_out,(void *)&ss,sizeof(ss)); + strcpy(op_out+sizeof(ss), dest_addr_str); + strcpy(op_out+sizeof(ss)+strlen(dest_addr_str)+1,dest_port_str); + /* now send the message */ + retval = write_tout(sop,op_out,op_outlen,conn_toutp); + /* now clean up the buffers */ + op_outlen = 0; + free(op_out); + free(dest_addr_str); + if (retval == -1) /* send failed */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : send() failed."); + sendmessage(s,(char *)message,(size_t)512,"451 Could not send to onion proxy.%s",SMTPAP_CRLF); + close(sop); + } + else /* send seemed to have succeeded */ + { + /* wait for the return code */ + op_inlen = 1; + op_in = (char *)malloc(op_inlen); + if (!op_in) /* memory allocation failed */ + { + log(LOG_DEBUG,"handle_connection() : handle_conection : Could not allocate memory."); + sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF); + close(sop); + } + else + { + retval = read_tout(sop,op_in,1,0, conn_toutp); + if (retval <= 0) /* recv() failed */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : recv() failed."); + sendmessage(s,(char *)message,(size_t)512,"451 Could not receive data from the onion proxy.%s",SMTPAP_CRLF); + close(sop); + } + else + { + if (!(*op_in)) /* onion proxy says OK */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : received E_SUCCESS from onion proxy"); + /* clean up */ + free(op_in); + op_inlen=0; + + /* allocate both op_in and op_out 512 bytes, the maximum size of an SMTP line */ + op_outlen=512; + op_inlen=512; + op_out = (char *)malloc(512); + op_in = (char *)malloc(512); + if ((!op_out) || (!op_in)) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Could not allocate memory."); + sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF); + close(sop); + } + else + { + /* receive the greeting message from the recipient */ + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) /* could not receive greeting */ + { + + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving greeting from recipient."); + sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF); + } + else /* received greeting */ + { + /* send HELO command */ + retval = sendmessage(sop,(char *)op_out,(size_t)op_outlen,"HELO ANONYMOUS.smtp.daemon%s",SMTPAP_CRLF); + if (retval == -1) + { + sendmessage(s,(char *)message,(size_t)512,"451 Error sending HELO to the recipient."); + close(sop); + } + else + { + + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving HELO response from recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF); + close(sop); + } + else + { + op_in[retval]=0; + log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in); + if (op_in[0] == '2') /* success */ + { + /* send MAIL */ + if (options[Anonimize].r.i) + retval = sendmessage(sop,(char *)op_out,(size_t)op_outlen,"MAIL From:anonymous@anon.net%s",SMTPAP_CRLF); + else + retval = write_tout(sop,mailbuf,mailbuflen-1,conn_toutp); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error sending MAIL to recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error sending MAIL to the recipient.%s",SMTPAP_CRLF); + sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF); + close(sop); + } + else + { + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving MAIL response from recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF); + close(sop); + } + else + { + op_in[retval]=0; + log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in); + if (op_in[0] == '2') /* success */ + { + /* send RCPT */ + retval = write_tout(sop,rcptbuf,rcptbuflen-1,conn_toutp); /* rcptbuflen includes the terminating NULL character but we don't want to send that */ + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error sending RCPT to recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error sending RCPT to the recipient.%s",SMTPAP_CRLF); + sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF); + close(sop); + } + else + { + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving RCPT response from recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF); + close(sop); + } + else + { + op_in[retval]=0; + log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in); + if (op_in[0] == '2') /* success */ + { + sendmessage(s,(char *)message,(size_t)512,"250 Recipient OK.%s",SMTPAP_CRLF); + state = 5; + } + else /* didn't like my RCPT */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : RCPT unsuccessful"); + sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF); + close(sop); + sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my RCPT.%s",SMTPAP_CRLF); + } + } + } + } + else /* didn't like my MAIL */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : MAIL unsuccessful"); + sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF); + close(sop); + sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my MAIL.%s",SMTPAP_CRLF); + } + } + } + } + else + { + log(LOG_DEBUG,"handle_connection() : handle_connection : HELO unsuccessful"); + sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF); + close(sop); + sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my HELO.%s",SMTPAP_CRLF); + } + } + } + } + } + } + else + { + log(LOG_DEBUG,"handle_connection() : handle_connection : onion proxy returned non-zero error code %d.",*op_in); + close(sop); + switch(*op_in) + { + case SS_ERROR_VERSION_UNSUPPORTED : + sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Protocol version not supported).%s",SMTPAP_CRLF); + break; + case SS_ERROR_ADDR_FMT_UNSUPPORTED: + sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Address format not recognised).%s",SMTPAP_CRLF); + break; + case SS_ERROR_INVALID_ADDRESS: + sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Invalid destination address).%s",SMTPAP_CRLF); + break; + case SS_ERROR_INVALID_PORT: + sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Invalid destination port).%s",SMTPAP_CRLF); + break; + default : + sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned unexpected error code %d.%s",*op_in,SMTPAP_CRLF); + break; + } + /* clean up */ + free(op_in); + op_inlen=0; + } + } + } + } + } + } + } + } + } + } + } + } + } + else + sendmessage(s,(char *)message, (size_t)512,"503 Need RCPT first.%s",SMTPAP_CRLF); + } + else if (state == 5) + { + if ((!strcmp(token,SMTPAP_DATA))) /* received data */ + { + partial_dataend = 0; + retval = write_tout(sop, tmpbuf, strlen(tmpbuf), conn_toutp); /* send DATA */ + if (retval == -1) /* send(0) failed */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Failed to send DATA to recipient."); + sendmessage(s,(char *)message,(size_t)512,"451 Error sending DATA to the recipient.%s",SMTPAP_CRLF); + } + else /* get response from the recipient */ + { + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving DATA response from recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF); + } + else + { + op_in[retval]=0; + log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in); + if (op_in[0] == '3') /* success */ + { + sendmessage(s,(char *)message,(size_t)512,"354 Enter mail, end with \".\" on a line by itself%s",SMTPAP_CRLF); + state = 6; + } + else /* didn't like my DATA */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : DATA unsuccessful"); + sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my DATA.%s",SMTPAP_CRLF); + } + } + } + } + else + sendmessage(s,(char *)message, (size_t)512,"503 Expecting DATA.%s",SMTPAP_CRLF); + } + else if (state == 6) + { + /* sanitize the data stream if necessary */ + if (options[Anonimize].r.i == 1) + { + log(LOG_DEBUG,"handle_connection() : Sanitizing headers ..."); + retval = sanitize_data((unsigned char **)&tmpbuf, &inputlen); + } + + if ((!retval) || (!options[Anonimize].r.i)) /* sanitization successsful (or wasn't necessary)? */ + { + log(LOG_DEBUG,"handle_connection() : Attempting to send ..."); + /* forward to recipient */ + retval = write_tout(sop,tmpbuf, inputlen, conn_toutp); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Failed to forward data to recipient."); + sendmessage(sop, (char *)op_out, (size_t)op_outlen,"451 Failed to forward data to the recipient.%s",SMTPAP_CRLF); + } + else + { + /* get the response */ + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving response from recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Data sent but did not receive a response from the recipient%s.",SMTPAP_CRLF); + } + else + { + op_in[retval]=0; + log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in); + if (op_in[0] == '2') /* success */ + { + sendmessage(s,(char *)message,(size_t)512,"250 Message accepted for delivery.%s",SMTPAP_CRLF); + sendmessage(sop, (char *)op_out, (size_t)op_outlen,"QUIT%s",SMTPAP_CRLF); + } + else /* didn't like my DATA */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : DATA unsuccessful"); + sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my DATA.%s",SMTPAP_CRLF); + } + } + } + } + else /* sanitization error */ + { + log(LOG_ERR,"Unable to sanitize an incoming message. Will reject."); + sendmessage(sop,(char *)op_out, (size_t)op_outlen,"400 Failed to sanitize the data stream.%s", SMTPAP_CRLF); + } + + /* after state 6 we go back to state 3, regardless of wether the transfer was succesful or not */ + state = 3; + close(sop); + free(op_in);op_in=NULL; + free(op_out);op_out=NULL; + } + else /* unexpected state */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Unexpected state!"); + log(LOG_ERR,"An unexpected error has occured. Closing connection."); + sendmessage(s,(char *)message,(size_t)512,"500 An unexpected error has ocurred. Closing connection.%s",SMTPAP_CRLF); + break; + } + } + + /* clean up */ + if (inbuf != NULL) + free(inbuf); + if (tmpbuf != NULL) + free(tmpbuf); + if (mailbuf != NULL) + free(mailbuf); + if (rcptbuf != NULL) + free(rcptbuf); + if (rcptarray != NULL) + free(rcptarray); + if (dest_addr_str != NULL) + free(dest_addr_str); + if (op_in != NULL) + free(op_in); + if (op_out != NULL) + free(op_out); + close(sop); + close(s); + + return retval; +} + +int main(int argc, char *argv[]) +{ + int retval = 0; + + char c; /* command-line option */ + + /* configuration file */ + char *conf_filename = NULL; + FILE *cf = NULL; + + struct hostent *local_host; + char local_hostname[512]; + + struct sockaddr_in local, remote; /* local and remote address info */ + + int request_sock; /* where we listen for connections */ + int new_sock; /* for accepted connections */ + + size_t sin_size; /* for accept() calls */ + + u_short p; /* smtp proxy port */ + u_short op_port; /* onion proxy port */ + + /* used for reaping zombie processes */ + struct sigaction sa; + + char *errtest = NULL; /* for detecting strtoul() errors */ + + /* set default listening port */ + p = htons(SMTPAP_LISTEN_PORT); + + /* deal with program arguments */ + if ((argc < 2) && (argc > 5)) /* to few or too many arguments*/ + { + print_usage(); + return -1; + } + + opterr = 0; + while ((c = getopt(argc,argv,args)) != -1) + { + switch(c) + { + case 'f': /* config file */ + conf_filename = optarg; + break; + case 'p': + p = htons((u_short)strtoul(optarg,&errtest,0)); + if (errtest == optarg) /* error */ + { + log(LOG_ERR,"Error : -p must be followed by an unsigned positive integer value."); + print_usage(); + return -1; + } + break; + case 'h': + print_usage(); + return 0; + break; + case 'l': + if (!strcmp(optarg,"emerg")) + loglevel = LOG_EMERG; + else if (!strcmp(optarg,"alert")) + loglevel = LOG_ALERT; + else if (!strcmp(optarg,"crit")) + loglevel = LOG_CRIT; + else if (!strcmp(optarg,"err")) + loglevel = LOG_ERR; + else if (!strcmp(optarg,"warning")) + loglevel = LOG_WARNING; + else if (!strcmp(optarg,"notice")) + loglevel = LOG_NOTICE; + else if (!strcmp(optarg,"info")) + loglevel = LOG_INFO; + else if (!strcmp(optarg,"debug")) + loglevel = LOG_DEBUG; + else + { + log(LOG_ERR,"Error : argument to -l must be one of alert|crit|err|warning|notice|info|debug."); + print_usage(); + return -1; + } + break; + case '?': + if (isprint(c)) + log(LOG_ERR,"Missing argument or unknown option '-%c'.",optopt); + else + log(LOG_ERR,"Unknown option character 'x%x'.",optopt); + print_usage(); + return -1; + break; + default: + abort(); + } + } + + /* the -f option is mandatory */ + if (conf_filename == NULL) + { + log(LOG_ERR,"You must specify a config file with the -f option. See help (-h)."); + return -1; + } + + /* load config file */ + cf = open_config(conf_filename); + if (!cf) + { + log(LOG_ERR,"Could not open configuration file %s.",conf_filename); + return -1; + } + retval = parse_config(cf,options); + if (retval) + return -1; + + if (options[OnionProxy].err != 1) + { + log(LOG_ERR,"The OnionProxy option is mandatory."); + return -1; + } + + if (options[MaxConn].err != 1) + { + log(LOG_ERR,"The MaxConn option is mandatory."); + return -1; + } + + if (options[Anonimize].err != 1) + { + log(LOG_ERR,"The Anonimize option is mandatory."); + return -1; + } + else if ((options[Anonimize].r.i != 0) && (options[Anonimize].r.i != 1)) + { + log(LOG_ERR,"The Anonimize option takes the values 1 or 0."); + return -1; + } + + if (options[ConnTimeout].err != 1) + { + conn_tout.tv_sec = SMTPAP_DEFAULT_CONN_TIMEOUT; + } + else + { + if (!options[ConnTimeout].r.i) + conn_toutp = NULL; + else + conn_tout.tv_sec = options[ConnTimeout].r.i; + } + conn_tout.tv_usec = 0; + + op_port = (u_short)options[OnionProxy].r.i; + + /* get local address so that we know where to get the onion proxy when we need it */ + retval = gethostname(local_hostname, (size_t)512); + if (retval < 0) + { + log(LOG_ERR,"Error getting local hostname"); + return -1; + } + local_host = gethostbyname(local_hostname); + if (!local_host) + { + log(LOG_ERR,"Error getting local address."); + return -1; + } + log(LOG_DEBUG,"main() : Got local address : %s.",local_hostname); + + /* get the server up and running */ + request_sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (request_sock < 0) + { + log(LOG_ERR,"Error opening socket."); + return -1; + } + log(LOG_DEBUG,"Socket opened."); + memset((void *)&local,0,sizeof(local)); /* clear the structure first */ + /* set up the sockaddr_in structure */ + local.sin_family=AF_INET; + local.sin_addr.s_addr = INADDR_ANY; + local.sin_port=p; + /* bind it to the socket */ + retval = bind(request_sock,(struct sockaddr *)&local, sizeof(local)); + if (retval < 0) + { + log(LOG_ERR,"Error binding socket to local port %d.",ntohs(p)); + return retval; + } + log(LOG_DEBUG,"Socket bound to port %d.",ntohs(p)); + /* listen for connections */ + retval = listen(request_sock,SOMAXCONN); + if (retval < 0) + { + log(LOG_ERR,"Could not listen for connections."); + return retval; + } + log(LOG_DEBUG,"Listening for connections."); + /* server should now be up and running */ + + /* install the signal handler for making sure zombie processes are killed */ + sa.sa_handler = sigchld_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + retval = sigaction(SIGCHLD,&sa,NULL); + if (retval < 0) + { + log(LOG_ERR,"Could not install a signal handler."); + return -1; + } + + /* main server loop */ + /* I use a forking server technique - this isn't the most efficient way to do it, + * but it is simpler. */ + while(1) + { + sin_size = sizeof(struct sockaddr_in); + new_sock = accept(request_sock,(struct sockaddr *)&remote,&sin_size); + if (new_sock == -1) + { + if (errno != EINTR) + log(LOG_ERR,"Could not accept socket connection."); + else + log(LOG_DEBUG,"Interrupt received."); + continue; + } + if (connections >= options[MaxConn].r.i) + { + log(LOG_NOTICE,"Number of maximum connections reached. Rejecting incoming request."); + close(new_sock); + continue; + } + + log(LOG_DEBUG,"Accepted a connection from %s.",inet_ntoa(remote.sin_addr)); + connections++; + + if (!fork()) /* this is the child process */ + { + close(request_sock); /* the child doesn't need the request socket anymore */ + + /* Main logic of smtpap. */ + retval = handle_connection(new_sock, local_host, remote, op_port); + /* End main logic */ + + exit(retval); /* done, exit */ + } + + close(new_sock); /* don't need this anymore */ + } + + return retval; + +} + diff --git a/src/smtpap/smtpap.h b/src/smtpap/smtpap.h new file mode 100644 index 000000000..3e875f4d1 --- /dev/null +++ b/src/smtpap/smtpap.h @@ -0,0 +1,90 @@ +/** + * smtpap.h + * SMTP Application Proxy for Onion Routing + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.12 2002/01/29 01:00:10 mp292 + * All network operations are now timeoutable. + * + * Revision 1.11 2002/01/26 21:50:17 mp292 + * Reviewed according to Secure-Programs-HOWTO. Still need to deal with network + * timeouts. + * + * Revision 1.10 2001/12/18 14:56:29 badbytes + * *** empty log message *** + * + * Revision 1.9 2001/12/18 14:43:19 badbytes + * Added DEFAULT_SMTP_PORT. + * + * Revision 1.8 2001/12/12 16:02:29 badbytes + * Testing completed. + * + * Revision 1.7 2001/12/11 10:43:21 badbytes + * MAIL and RCPT handling completed. Still coding connection to Onion Proxy. + * + * Revision 1.6 2001/12/10 16:10:35 badbytes + * Wrote a tokenize() function to help with parsing input from SMTP clients. + * + * Revision 1.5 2001/12/07 15:02:43 badbytes + * Server setup code completed. + * + */ + +#ifndef __SMTPAP_H + +#define __SMTPAP_H + +#define SMTPAP_CRLF "\015\012" +#define SMTPAP_CRLF_LEN 2 + +#define SMTPAP_CR '\015' +#define SMTPAP_LF '\012' + +/* terminator for DATA input */ +#define SMTPAP_ENDDATA "\015\012.\015\012" +#define SMTPAP_ENDDATA_LEN 5 + +/* characters that separate tokens in SMTPAP commands */ +#define SMTPAP_SEPCHARS " \t\015\012" /* for general commands */ +#define SMTPAP_PATH_SEPCHARS " \t\015\012<>" /* for forward and reverse path */ + +/* default listening port */ +#define SMTPAP_LISTEN_PORT 25 + +/* default SMTP port */ +#define SMTPAP_DEFAULT_SMTP_PORT 25 + +/* default connection timeout */ +#define SMTPAP_DEFAULT_CONN_TIMEOUT 120; /* 120s */ + +/* SMTP commands and their lengths */ +#define SMTPAP_QUIT "quit" +#define SMTPAP_QUIT_LEN 4 + +#define SMTPAP_HELO "helo" +#define SMTPAP_HELO_LEN 4 +#define SMTPAP_EHLO "ehlo" +#define SMTPAP_EHLO_LEN 4 + +#define SMTPAP_MAIL "mail" +#define SMTPAP_MAIL_LEN 4 + +#define SMTPAP_RSET "rset" +#define SMTPAP_RSET_LEN 4 + +#define SMTPAP_RCPT "rcpt" +#define SMTPAP_RCPT_LEN 4 + +#define SMTPAP_DATA "data" +#define SMTPAP_DATA_LEN 4 + +#endif + |