diff -ur stunnel-3.22/protocol.c stunnel-3.22-sg1/protocol.c --- stunnel-3.22/protocol.c Thu Dec 20 02:52:37 2001 +++ stunnel-3.22-sg1/protocol.c Sat May 11 15:07:14 2002 @@ -131,31 +131,176 @@ return 0; } +#define PP_BUFSIZE 8192 +static int plaintext_proxy(CLI *c) +{ + fd_set rd_set, wr_set; + char rbuf[PP_BUFSIZE]; + int rbufend,rdone; + char lbuf[PP_BUFSIZE]; + int lbufend,ldone; + struct timeval tv; + int ready; + int fdmax; + int howmuch, sofar; + + fdmax = c->remote_fd; + if (c->local_wfd > fdmax) + fdmax = c->local_wfd; + if (c->local_rfd > fdmax) + fdmax = c->local_rfd; + fdmax++; + + lbufend = rbufend = 0; + rdone = ldone = 0; + + tv.tv_sec = 86400; + tv.tv_usec = 0; + while( !( (rdone || ldone) && (!rbufend && !lbufend) ) ) { + log(LOG_DEBUG,"rdone=%d, ldone=%d, rbufend=%d, lbufend=%d",rdone,ldone,rbufend,lbufend); + FD_ZERO(&rd_set); + FD_ZERO(&wr_set); + if (rbufend) + FD_SET((c->local_wfd),&wr_set); + else if (!rdone) + FD_SET(c->remote_fd,&rd_set); + + if (lbufend) + FD_SET(c->remote_fd,&wr_set); + else if (!ldone) + FD_SET(c->local_rfd,&rd_set); + + do { /* Skip "Interrupted system call" errors */ + ready=select(fdmax, &rd_set, &wr_set, NULL, &tv); + } while(ready<0 && get_last_socket_error()==EINTR); + + if(ready<0) { /* Break the connection for others */ + sockerror("select"); + c->error=1; + return 1; + } + if(!ready) { /* Timeout */ + log(LOG_DEBUG, "select timeout - connection reset"); + c->error=1; + return 1; + } + + if (lbufend > 0) { + if (FD_ISSET(c->remote_fd,&wr_set)) + { + sofar = 0; + while (sofar < lbufend) { + if ((howmuch=write(c->remote_fd,lbuf+sofar,lbufend-sofar)) < 0) { + sockerror("write"); + c->error=1; + return 1; + } + sofar += howmuch; + } + lbufend = 0; + } + } else { + if (FD_ISSET(c->local_rfd,&rd_set)) { + if ( (lbufend = read(c->local_rfd,lbuf,PP_BUFSIZE)) < 0) { + sockerror("read"); + c->error = 1; + return 1; + } + if (lbufend == 0) /* EOF */ + { + log(LOG_DEBUG,"EOF reading from local"); + ldone=1; + } + } + } + + if (rbufend > 0) { + if (FD_ISSET(c->local_wfd, &wr_set)) { + sofar = 0; + while (sofar < rbufend) { + if ( (howmuch = write(c->local_wfd, rbuf + sofar, rbufend - sofar)) < 0) { + sockerror("write"); + c->error=1; + return 1; + } + sofar += howmuch; + } + rbufend = 0; + } + } else { + if (FD_ISSET(c->remote_fd,&rd_set)) { + if ( (rbufend = read(c->remote_fd,rbuf,PP_BUFSIZE)) < 0) { + sockerror("read"); + c->error = 1; + return 1; + } + if (rbufend == 0) /* EOF */ { + log(LOG_DEBUG,"EOF reading from remote"); + rdone=1; + } + } + } + } + + log(LOG_DEBUG, "plaintext_proxy finished normally."); + return 0; +} + static int smtp_server(CLI *c) { char line[STRLEN]; + int fdscanf_ret; if(RFC2487(c->local_rfd)==0) return 0; /* Return if RFC 2487 is not used */ - if(fdscanf(c->remote_fd, "220%[^\n]", line)!=1) { + if(fdscanf(c->remote_fd, "220%[^\r\n]", line)!=1) { log(LOG_ERR, "Unknown server welcome"); return -1; } if(fdprintf(c->local_wfd, "220%s + stunnel", line)<0) return -1; - if(fdscanf(c->local_rfd, "EHLO %[^\n]", line)!=1) { - log(LOG_ERR, "Unknown client EHLO"); - return -1; + + /* See if we get an EHLO command */ + if (((fdscanf_ret=fdscanf(c->local_rfd, "%[^\r\n]", line)) != 1) || + (strlen(line) < 4) || + (line[4] && !isspace(line[4])) || + (strncasecmp(line,"ehlo",4) != 0) ) + { + if ((fdscanf_ret == 1) && line[0]) + fdprintf(c->remote_fd,"%s",line); + exit(plaintext_proxy(c)); + } + if (fdprintf(c->remote_fd,"%s",line) < 0) + return -1; + while ( (fdscanf(c->remote_fd,"%[^\r\n]", line) == 1) + && (line[3] == '-') ) { + if (fdprintf(c->local_wfd, "%s",line) < 0) + return -1; } - if(fdprintf(c->local_wfd, "250-%s Welcome", line)<0) - return -1; - if(fdprintf(c->local_wfd, "250 STARTTLS")<0) - return -1; - if(fdscanf(c->local_rfd, "STARTTLS", line)<0) { - log(LOG_ERR, "STARTTLS expected"); - return -1; + line[3] = '-'; + fdprintf(c->local_wfd,"%s",line); + fdprintf(c->local_wfd,"250 STARTTLS"); + + /* See if we get a STARTTLS command */ + if (((fdscanf_ret=fdscanf(c->local_rfd, "%[^\r\n]", line)) != 1) || + (strlen(line) < 8) || + (line[8] && !isspace(line[8])) || + (strncasecmp(line,"starttls",8) != 0) ) + { + if ((fdscanf_ret==1) && line[0]) + fdprintf(c->remote_fd,"%s",line); + exit(plaintext_proxy(c)); + } + + if (line[8]) { + fdprintf(c->local_wfd,"501 Syntax error (no parameters allowed; STARTTLS disabled) (#5.5.4)"); + exit(plaintext_proxy(c)); } - if(fdprintf(c->local_wfd, "220 Go ahead", line)<0) + + if(fdprintf(c->local_wfd, "220 Go ahead")<0) return -1; + + /* Technically, we should shut down the SMTP connection and get + * a new one, but screw it for now. */ return 0; }