зеркало из https://github.com/mozilla/gecko-dev.git
1219 строки
36 KiB
C
1219 строки
36 KiB
C
/* -*- Mode: C; c-file-style: "stroustrup"; comment-column: 40 -*- */
|
|
/*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is the Netscape Mailstone utility,
|
|
* released March 17, 2000.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1997-2000 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s): Dan Christian <robodan@netscape.com>
|
|
* Marcel DePaolis <marcel@netcape.com>
|
|
* Mike Blakely
|
|
*
|
|
* Alternatively, the contents of this file may be used under the
|
|
* terms of the GNU Public License Version 2 or later (the "GPL"), in
|
|
* which case the provisions of the GPL are applicable instead of
|
|
* those above. If you wish to allow use of your version of this file
|
|
* only under the terms of the GPL and not to allow others to use your
|
|
* version of this file under the NPL, indicate your decision by
|
|
* deleting the provisions above and replace them with the notice and
|
|
* other provisions required by the GPL. If you do not delete the
|
|
* provisions above, a recipient may use your version of this file
|
|
* under either the NPL or the GPL.
|
|
*/
|
|
/* SMTP protocol tests */
|
|
|
|
#include "bench.h"
|
|
#include "pish.h"
|
|
#include <glob.h> /* for glob, globfree */
|
|
|
|
#define MAXCOMMANDLEN 256 /* parse buffer length */
|
|
|
|
/* SMTP protocol line to send after a message */
|
|
#define MSG_TRAILER CRLF "." CRLF
|
|
|
|
/* run time state */
|
|
typedef struct _doSMTP_state {
|
|
int nothing;
|
|
} doSMTP_state_t;
|
|
|
|
/* info on files/messages to send */
|
|
typedef struct {
|
|
char * filename; /* file name */
|
|
int offset; /* where to start sending from */
|
|
char * msgMailFrom; /* message mail from (envelope sender) */
|
|
} smtp_file_t;
|
|
|
|
/* flags definitions */
|
|
#define useEHLO 0x01 /* use EHLO instead of HELO */
|
|
#define useAUTHLOGIN 0x02 /* use AUTH LOGIN to authenticate */
|
|
#define useAUTHPLAIN 0x04 /* use AUTH PLAIN to authenticate */
|
|
|
|
static void doSMTPExit (ptcx_t ptcx, doSMTP_state_t *me);
|
|
|
|
/* ================ Support routines ================ */
|
|
/*
|
|
Handle the initial look at the specified file
|
|
*/
|
|
static int
|
|
SmtpFileInit (pmail_command_t cmd, /* command being checked */
|
|
param_list_t *defparm, /* default parameters */
|
|
smtp_file_t *fileEntry) /* file entry to fill in */
|
|
{
|
|
/*pish_command_t *pish = (pish_command_t *)cmd->data;*/
|
|
int fd;
|
|
int bytesRead;
|
|
struct stat statbuf;
|
|
char *msgdata; /* temp buffer */
|
|
int msgsize; /* message size without trailing CRLF.CRLF */
|
|
|
|
|
|
/* Handle multiple files. This means that the file contents
|
|
(starting from an offset) are read when the block is executed.
|
|
The start of the file is scanned here for special commands. */
|
|
|
|
D_PRINTF (stderr, "SmtpFileInit: %s\n", fileEntry->filename);
|
|
|
|
/* read the contents of file into struct */
|
|
memset(&statbuf, 0, sizeof(statbuf));
|
|
if (stat(fileEntry->filename, &statbuf) != 0) {
|
|
return returnerr(stderr,"Couldn't stat file %s: errno=%d: %s\n",
|
|
fileEntry->filename, errno, strerror(errno));
|
|
}
|
|
|
|
/* open file */
|
|
if ((fd = open(fileEntry->filename, O_RDONLY)) <= 0) {
|
|
return returnerr(stderr, "Cannot open file %s: errno=%d: %s\n",
|
|
fileEntry->filename, errno, strerror(errno));
|
|
}
|
|
|
|
/* We don't really need much of the file, but it warms up the
|
|
* cache. Limit at 1Mb */
|
|
msgsize = MIN (statbuf.st_size, 1024*1024);
|
|
msgdata = (char *) mycalloc(msgsize+strlen(MSG_TRAILER)+1);
|
|
|
|
if ((bytesRead = read(fd, msgdata, msgsize)) <= 0) {
|
|
close(fd);
|
|
return returnerr(stderr, "Cannot read file %s: errno=%d: %s\n",
|
|
fileEntry->filename, errno, strerror(errno));
|
|
}
|
|
|
|
if (bytesRead != msgsize) {
|
|
returnerr(stderr, "Error reading file %s, got %d expected %d\n",
|
|
fileEntry->filename, bytesRead, msgsize);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
msgdata[msgsize] = 0;
|
|
|
|
/* If the file starts with SMTP, then it has extra info in it */
|
|
/* clean up message to rfc822 style */
|
|
|
|
#define PROTO_HEADER "SMTP"
|
|
#define PROTO_MAIL_FROM "\nMAIL FROM:"
|
|
#define PROTO_DATA "\nDATA\n"
|
|
|
|
if (strncmp(msgdata, PROTO_HEADER, strlen(PROTO_HEADER)) == 0) {
|
|
char *cp, *cp2;
|
|
int off;
|
|
|
|
D_PRINTF(stderr, "found PROTO_HEADER [%s]\n", PROTO_HEADER);
|
|
cp = strstr(msgdata, PROTO_MAIL_FROM);
|
|
if (cp != NULL) { /* copy out FROM field */
|
|
cp += strlen(PROTO_MAIL_FROM);
|
|
cp2 = strchr(cp, '\n');
|
|
off = cp2 - cp;
|
|
fileEntry->msgMailFrom = (char *) mycalloc(off+1);
|
|
memcpy(fileEntry->msgMailFrom, cp, off);
|
|
fileEntry->msgMailFrom[off] = 0;
|
|
D_PRINTF(stderr, "got PROTO_MAIL_FROM:%s\n",
|
|
fileEntry->msgMailFrom);
|
|
}
|
|
|
|
cp = strstr(msgdata, PROTO_DATA);
|
|
if (cp != NULL) { /* DATA marks the real message, copy up */
|
|
off = cp - msgdata;
|
|
off += strlen(PROTO_DATA);
|
|
fileEntry->offset = off;
|
|
D_PRINTF(stderr, "found PROTO_DATA at off=%d\n", off);
|
|
} else {
|
|
D_PRINTF(stderr,
|
|
"WARNING: PROTO_HEADER [%s] given, but PROTO_DATA [%s] never found\n",
|
|
PROTO_HEADER, PROTO_DATA);
|
|
}
|
|
}
|
|
|
|
|
|
myfree (msgdata);
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Handle file expansion for the given file pattern
|
|
*/
|
|
static int
|
|
SmtpFilePrep (pmail_command_t cmd, /* command being checked */
|
|
param_list_t *defparm) /* default parameters */
|
|
{
|
|
pish_command_t *pish = (pish_command_t *)cmd->data;
|
|
int ii;
|
|
glob_t globs;
|
|
smtp_file_t *fileEntry;
|
|
|
|
/* We randomly pick files, so dont bother to sort them */
|
|
if (glob (pish->filePattern, GLOB_NOSORT | GLOB_MARK, NULL, &globs) < 0) {
|
|
globfree (&globs);
|
|
return returnerr(stderr,"Error globbing '%s': errno=%d: %s\n",
|
|
pish->filePattern, errno, strerror(errno));
|
|
}
|
|
|
|
pish->fileCount = globs.gl_pathc;
|
|
D_PRINTF (stderr, "SmtpFilePrep: filePattern='%s' entries=%d\n",
|
|
pish->filePattern, pish->fileCount);
|
|
pish->files = mycalloc (pish->fileCount * sizeof (smtp_file_t));
|
|
fileEntry = pish->files;
|
|
for (ii = 0; ii < pish->fileCount; ++ii, ++fileEntry) {
|
|
fileEntry->filename = mystrdup (globs.gl_pathv[ii]);
|
|
if (SmtpFileInit (cmd, defparm, fileEntry) < 0) {
|
|
globfree (&globs);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
globfree (&globs);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Send a file with leading and trailing text.
|
|
Currently only used by SMTP.
|
|
The leading and trailing text may be NULL.
|
|
For SMTP, put the <CRLF>.<CRLF> in the trailing text.
|
|
*/
|
|
static int
|
|
sendFile (
|
|
ptcx_t ptcx, /* timer context */
|
|
SOCKET sock, /* socket to send on */
|
|
char *leading, /* initial text/headers or NULL */
|
|
char *fileName, /* filename */
|
|
int offset, /* offset into file */
|
|
char *trailing) /* trailing text or NULL */
|
|
{
|
|
int leadLen = 0, trailLen = 0;
|
|
int sentbytes = 0;
|
|
int sent, todo, done, fd;
|
|
char buff[16*1024];
|
|
|
|
fd = open (fileName, O_RDONLY);
|
|
if (fd < 0) {
|
|
snprintf (buff, sizeof (buff),
|
|
"<sendfile: Error opening '%s' (%s)",
|
|
fileName, strerror (errno));
|
|
strcat (ptcx->errMsg, buff);
|
|
return -1;
|
|
}
|
|
|
|
|
|
if (NULL != leading) leadLen = strlen(leading);
|
|
if (NULL != trailing) trailLen = strlen(trailing);
|
|
|
|
D_PRINTF(debugfile, "Writing file '%s' to server: leadLen=%d trailLen=%d offset=%d\n",
|
|
fileName, leadLen, trailLen, offset);
|
|
|
|
/* Send leading text */
|
|
todo = leadLen;
|
|
done = 0;
|
|
while (todo > 0) {
|
|
if ((sent = retryWrite(ptcx, sock, leading + done,
|
|
todo - done)) == -1) {
|
|
strcat (ptcx->errMsg, "<sendFile");
|
|
return -1;
|
|
}
|
|
|
|
done += sent;
|
|
todo -= sent;
|
|
|
|
if (gf_timeexpired >= EXIT_FAST) {
|
|
D_PRINTF(debugfile,"sendFile() Time expired.\n");
|
|
sentbytes += done;
|
|
goto exitFast;
|
|
}
|
|
}
|
|
sentbytes += done;
|
|
|
|
/* Send the file */
|
|
if (offset > 0) { /* skip an initial offset */
|
|
if (lseek (fd, offset, SEEK_SET) < 0) {
|
|
strcat (ptcx->errMsg, "<sendFile() seek failed.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
while ((todo = read (fd, buff, sizeof (buff))) > 0) {
|
|
done = 0;
|
|
while (todo > 0) {
|
|
if ((sent = retryWrite(ptcx, sock, buff + done,
|
|
todo - done)) == -1) {
|
|
strcat (ptcx->errMsg, "<sendFile");
|
|
return -1;
|
|
}
|
|
|
|
done += sent;
|
|
todo -= sent;
|
|
|
|
if (gf_timeexpired >= EXIT_FAST) {
|
|
D_PRINTF(debugfile,"sendFile() Time expired.\n");
|
|
sentbytes += done;
|
|
goto exitFast;
|
|
}
|
|
}
|
|
sentbytes += done;
|
|
}
|
|
if (todo < 0) {
|
|
D_PRINTF(debugfile,"sendFile() File read.\n");
|
|
}
|
|
close (fd);
|
|
|
|
/* Send trailing text */
|
|
todo = trailLen;
|
|
done = 0;
|
|
while (todo > 0) {
|
|
if ((sent = retryWrite(ptcx, sock, trailing + done,
|
|
todo - done)) == -1) {
|
|
strcat (ptcx->errMsg, "<sendFile");
|
|
return -1;
|
|
}
|
|
|
|
done += sent;
|
|
todo -= sent;
|
|
|
|
if (gf_timeexpired >= EXIT_FAST) {
|
|
D_PRINTF(debugfile,"sendFile() Time expired.\n");
|
|
sentbytes += done;
|
|
goto exitFast;
|
|
}
|
|
}
|
|
sentbytes += done;
|
|
|
|
exitFast:
|
|
ptcx->byteswritten += sentbytes;
|
|
return sentbytes;
|
|
}
|
|
|
|
#if 0 /* not currently used */
|
|
/*
|
|
Send a message from memory. Currently only used by SMTP.
|
|
Assumes we already included the <CRLF>.<CRLF> at the end of message
|
|
*/
|
|
static int
|
|
sendMessage(ptcx_t ptcx, SOCKET sock, char *message)
|
|
{
|
|
int writelen;
|
|
int sentbytes = 0;
|
|
int sent;
|
|
|
|
writelen = strlen(message);
|
|
|
|
D_PRINTF(debugfile, "Writing message to server: len=%d\n", writelen );
|
|
|
|
while (sentbytes < writelen) {
|
|
if ((sent = retryWrite(ptcx, sock, message + sentbytes,
|
|
writelen - sentbytes)) == -1) {
|
|
strcat (ptcx->errMsg, "<sendMessage");
|
|
return -1;
|
|
}
|
|
|
|
sentbytes += sent;
|
|
|
|
if (gf_timeexpired >= EXIT_FAST) {
|
|
D_PRINTF(debugfile,"sendMessage() Time expired.\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
ptcx->byteswritten += sentbytes;
|
|
|
|
return sentbytes;
|
|
}
|
|
#endif
|
|
|
|
/* ================ SMTP protocol entry points ================ */
|
|
/*
|
|
Set defaults in command structure
|
|
*/
|
|
int
|
|
SmtpParseStart (pmail_command_t cmd,
|
|
char *line,
|
|
param_list_t *defparm)
|
|
{
|
|
param_list_t *pp;
|
|
pish_command_t *pish = (pish_command_t *)mycalloc
|
|
(sizeof (pish_command_t));
|
|
cmd->data = pish;
|
|
|
|
cmd->numLoops = 1; /* default 1 message */
|
|
pish->hostInfo.portNum = SMTP_PORT; /* get default port */
|
|
|
|
D_PRINTF(stderr, "Smtp Assign defaults\n");
|
|
/* Fill in defaults first, ignore defaults we don't use */
|
|
for (pp = defparm; pp; pp = pp->next) {
|
|
(void)pishParseNameValue (cmd, pp->name, pp->value);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
Fill in command structure from a list of lines
|
|
*/
|
|
int
|
|
SmtpParseEnd (pmail_command_t cmd,
|
|
string_list_t *section,
|
|
param_list_t *defparm)
|
|
{
|
|
string_list_t *sp;
|
|
pish_command_t *pish = (pish_command_t *)cmd->data;
|
|
|
|
/* Now parse section lines */
|
|
D_PRINTF(stderr, "Smtp Assign section lines\n");
|
|
/* skip first and last */
|
|
for (sp = section->next; sp->next; sp = sp->next) {
|
|
char *name = sp->value;
|
|
char *tok = name + strcspn(name, " \t=");
|
|
*tok++ = 0; /* split name off */
|
|
tok += strspn(tok, " \t=");
|
|
|
|
string_tolower(name);
|
|
tok = string_unquote(tok);
|
|
|
|
if (pishParseNameValue (cmd, name, tok) < 0) {
|
|
/* not a known attr */
|
|
D_PRINTF(stderr,"unknown attribute '%s' '%s'\n", name, tok);
|
|
returnerr(stderr,"unknown attribute '%s' '%s'\n", name, tok);
|
|
}
|
|
}
|
|
|
|
/* check for some of the required command attrs */
|
|
if (!pish->hostInfo.hostName) {
|
|
D_PRINTF(stderr,"missing server for command");
|
|
return returnerr(stderr,"missing server for command\n");
|
|
}
|
|
|
|
if (!pish->loginFormat) {
|
|
D_PRINTF(stderr,"missing loginFormat for command");
|
|
return returnerr(stderr,"missing loginFormat for command\n");
|
|
}
|
|
|
|
if (!pish->passwdFormat) {
|
|
D_PRINTF(stderr,"missing passwdFormat for command");
|
|
return returnerr(stderr,"missing passwdFormat for command\n");
|
|
}
|
|
|
|
if (!pish->addressFormat) {
|
|
D_PRINTF(stderr,"missing addressFormat for command");
|
|
return returnerr(stderr,"missing addressFormat for command\n");
|
|
}
|
|
|
|
/* check for required attrs */
|
|
if (!pish->filePattern) {
|
|
D_PRINTF(stderr,"missing file for SMTP command");
|
|
return returnerr(stderr,"missing file for SMTP command\n");
|
|
}
|
|
|
|
SmtpFilePrep (cmd, defparm);
|
|
|
|
/* see if we can resolve the mailserver addr */
|
|
if (resolve_addrs(pish->hostInfo.hostName, "tcp",
|
|
&(pish->hostInfo.host_phe),
|
|
&(pish->hostInfo.host_ppe),
|
|
&(pish->hostInfo.host_addr),
|
|
&(pish->hostInfo.host_type))) {
|
|
return returnerr (stderr, "Error resolving hostname '%s'\n",
|
|
pish->hostInfo.hostName);
|
|
} else {
|
|
pish->hostInfo.resolved = 1; /* mark the hostInfo resolved */
|
|
}
|
|
rangeSetFirstCount (&pish->loginRange, pish->loginRange.first,
|
|
pish->loginRange.span, pish->loginRange.sequential);
|
|
rangeSetFirstCount (&pish->domainRange, pish->domainRange.first,
|
|
pish->domainRange.span, pish->domainRange.sequential);
|
|
rangeSetFirstCount (&pish->addressRange, pish->addressRange.first,
|
|
pish->addressRange.span, pish->addressRange.sequential);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ======================================================================
|
|
These routine will become protcol specific.
|
|
Since SMTP is the most complex, they are here for now.
|
|
*/
|
|
|
|
/*
|
|
TRANSITION: handles all the old names-fields for POP, IMAP, SMTP, HTTP
|
|
This goes away when commands have protocol extensible fields
|
|
*/
|
|
int
|
|
pishParseNameValue (pmail_command_t cmd,
|
|
char *name,
|
|
char *tok)
|
|
{
|
|
pish_command_t *pish = (pish_command_t *)cmd->data;
|
|
D_PRINTF (stderr, "pishParseNameValue(name='%s' value='%s')\n", name, tok);
|
|
|
|
/* find a home for the attr/value */
|
|
if (cmdParseNameValue(cmd, name, tok))
|
|
; /* done */
|
|
else if (strcmp(name, "server") == 0)
|
|
pish->hostInfo.hostName = mystrdup (tok);
|
|
else if (strcmp(name, "smtpmailfrom") == 0)
|
|
pish->smtpMailFrom = mystrdup (tok);
|
|
else if (strcmp(name, "loginformat") == 0)
|
|
pish->loginFormat = mystrdup (tok);
|
|
else if (strcmp(name, "addressformat") == 0)
|
|
pish->addressFormat = mystrdup (tok);
|
|
else if (strcmp(name, "firstlogin") == 0)
|
|
pish->loginRange.first = atoi(tok);
|
|
else if (strcmp(name, "numlogins") == 0)
|
|
pish->loginRange.span = atoi(tok);
|
|
else if (strcmp(name, "sequentiallogins") == 0)
|
|
pish->loginRange.sequential = atoi(tok);
|
|
else if (strcmp(name, "firstdomain") == 0)
|
|
pish->domainRange.first = atoi(tok);
|
|
else if (strcmp(name, "numdomains") == 0)
|
|
pish->domainRange.span = atoi(tok);
|
|
else if (strcmp(name, "sequentialdomains") == 0)
|
|
pish->domainRange.sequential = atoi(tok);
|
|
else if (strcmp(name, "firstaddress") == 0)
|
|
pish->addressRange.first = atoi(tok);
|
|
else if (strcmp(name, "numaddresses") == 0)
|
|
pish->addressRange.span = atoi(tok);
|
|
else if (strcmp(name, "sequentialaddresses") == 0)
|
|
pish->addressRange.sequential = atoi(tok);
|
|
else if (strcmp(name, "portnum") == 0)
|
|
pish->hostInfo.portNum = atoi(tok);
|
|
else if (strcmp(name, "numrecips") == 0)
|
|
pish->numRecipients = atoi(tok);
|
|
else if (strcmp(name, "numrecipients") == 0)
|
|
pish->numRecipients = atoi(tok);
|
|
else if (strcmp(name, "file") == 0)
|
|
pish->filePattern = mystrdup (tok);
|
|
else if (strcmp(name, "passwdformat") == 0)
|
|
pish->passwdFormat = mystrdup (tok);
|
|
else if (strcmp(name, "searchfolder") == 0)
|
|
pish->imapSearchFolder = mystrdup (tok);
|
|
else if (strcmp(name, "searchpattern") == 0)
|
|
pish->imapSearchPattern = mystrdup (tok);
|
|
else if (strcmp(name, "searchrate") == 0)
|
|
pish->imapSearchRate = atoi(tok);
|
|
else if (strcmp(name, "useehlo") == 0)
|
|
if (atoi(tok)) {
|
|
pish->flags |= useEHLO;
|
|
} else {
|
|
pish->flags &= ~useEHLO;
|
|
}
|
|
else if (strcmp(name, "useauthlogin") == 0)
|
|
if (atoi(tok) > 0) {
|
|
pish->flags |= useAUTHLOGIN;
|
|
} else {
|
|
pish->flags &= ~useAUTHLOGIN;
|
|
}
|
|
else if (strcmp(name, "useauthplain") == 0)
|
|
if (atoi(tok) > 0) {
|
|
pish->flags |= useAUTHPLAIN;
|
|
} else {
|
|
pish->flags &= ~useAUTHPLAIN;
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* PROTOCOL specific */
|
|
void
|
|
pishStatsInit(mail_command_t *cmd, cmd_stats_t *p, int procNum, int threadNum)
|
|
{
|
|
assert (NULL != p);
|
|
|
|
if (!p->data) { /* create it */
|
|
p->data = mycalloc (sizeof (pish_stats_t));
|
|
} else { /* zero it */
|
|
memset (p->data, 0, sizeof (pish_stats_t));
|
|
}
|
|
|
|
if (cmd) { /* do sub-range calulations */
|
|
pish_command_t *pish = (pish_command_t *)cmd->data;
|
|
pish_stats_t *stats = (pish_stats_t *)p->data;
|
|
|
|
rangeSplit (&pish->loginRange, &stats->loginRange,
|
|
procNum, threadNum);
|
|
rangeSplit (&pish->domainRange, &stats->domainRange,
|
|
procNum, threadNum);
|
|
rangeSplit (&pish->addressRange, &stats->addressRange,
|
|
procNum, threadNum);
|
|
/* initialize sequential range */
|
|
stats->lastLogin = (stats->loginRange.sequential < 0)
|
|
? stats->loginRange.last+1 : stats->loginRange.first-1;
|
|
stats->lastDomain = (stats->domainRange.sequential < 0)
|
|
? stats->domainRange.last+1 : stats->domainRange.first-1;
|
|
stats->lastAddress = (stats->addressRange.sequential < 0)
|
|
? stats->addressRange.last+1 : stats->addressRange.first-1;
|
|
}
|
|
}
|
|
|
|
/* PROTOCOL specific */
|
|
void
|
|
pishStatsUpdate(protocol_t *proto,
|
|
cmd_stats_t *sum,
|
|
cmd_stats_t *incr)
|
|
{
|
|
pish_stats_t *ss = (pish_stats_t *)sum->data;
|
|
pish_stats_t *is = (pish_stats_t *)incr->data;
|
|
|
|
event_sum(&sum->idle, &incr->idle);
|
|
event_sum(&ss->connect, &is->connect);
|
|
event_sum(&ss->banner, &is->banner);
|
|
event_sum(&ss->login, &is->login);
|
|
event_sum(&ss->cmd, &is->cmd);
|
|
event_sum(&ss->msgread, &is->msgread);
|
|
event_sum(&ss->msgwrite, &is->msgwrite);
|
|
event_sum(&ss->logout, &is->logout);
|
|
|
|
event_reset(&incr->combined); /* figure out total */
|
|
event_sum(&incr->combined, &incr->idle);
|
|
event_sum(&incr->combined, &is->connect);
|
|
event_sum(&incr->combined, &is->banner);
|
|
event_sum(&incr->combined, &is->login);
|
|
event_sum(&incr->combined, &is->cmd);
|
|
event_sum(&incr->combined, &is->msgread);
|
|
event_sum(&incr->combined, &is->msgwrite);
|
|
event_sum(&incr->combined, &is->logout);
|
|
|
|
event_sum(&sum->combined, &incr->combined); /* add our total to sum-total*/
|
|
|
|
sum->totalerrs += incr->totalerrs;
|
|
sum->totalcommands += incr->totalcommands;
|
|
}
|
|
|
|
/* PROTOCOL specific */
|
|
void
|
|
pishStatsOutput(protocol_t *proto,
|
|
cmd_stats_t *ptimer,
|
|
char *buf)
|
|
{
|
|
char eventtextbuf[SIZEOF_EVENTTEXT];
|
|
pish_stats_t *stats = (pish_stats_t *)ptimer->data;
|
|
|
|
*buf = 0;
|
|
|
|
/* blocks=%ld is handled for us */
|
|
|
|
/* output proto independent total */
|
|
event_to_text(&ptimer->combined, eventtextbuf);
|
|
sprintf(&buf[strlen(buf)], "total=%s ", eventtextbuf);
|
|
|
|
event_to_text(&stats->connect, eventtextbuf);
|
|
sprintf(&buf[strlen(buf)], "conn=%s ", eventtextbuf);
|
|
|
|
event_to_text(&stats->banner, eventtextbuf);
|
|
sprintf(&buf[strlen(buf)], "banner=%s ", eventtextbuf);
|
|
|
|
event_to_text(&stats->login, eventtextbuf);
|
|
sprintf(&buf[strlen(buf)], "login=%s ", eventtextbuf);
|
|
|
|
event_to_text(&stats->cmd, eventtextbuf);
|
|
sprintf(&buf[strlen(buf)], "cmd=%s ", eventtextbuf);
|
|
|
|
event_to_text(&stats->msgwrite, eventtextbuf);
|
|
sprintf(&buf[strlen(buf)], "submit=%s ", eventtextbuf);
|
|
|
|
event_to_text(&stats->msgread, eventtextbuf);
|
|
sprintf(&buf[strlen(buf)], "retrieve=%s ", eventtextbuf);
|
|
|
|
event_to_text(&stats->logout, eventtextbuf);
|
|
sprintf(&buf[strlen(buf)], "logout=%s ", eventtextbuf);
|
|
|
|
/* output proto independent idle */
|
|
event_to_text(&ptimer->idle, eventtextbuf);
|
|
sprintf(&buf[strlen(buf)], "idle=%s ", eventtextbuf);
|
|
|
|
}
|
|
|
|
/* PROTOCOL specific */
|
|
void
|
|
pishStatsFormat (protocol_t *pp,
|
|
const char *extra, /* extra text to insert (client=) */
|
|
char *buf)
|
|
{
|
|
static char *timerList[] = { /* must match order of StatsOutput */
|
|
"total",
|
|
"conn", "banner", "login", "cmd", "submit", "retrieve", "logout",
|
|
"idle" };
|
|
|
|
char **tp;
|
|
char *cp = buf;
|
|
int ii;
|
|
|
|
/* Define the contents of each timer
|
|
These must all the same, to that the core time functions
|
|
can be qualified. We specify each one for reduce.pl to work right.
|
|
*/
|
|
|
|
for (ii=0, tp=timerList;
|
|
ii < (sizeof (timerList)/sizeof (timerList[0]));
|
|
++ii, ++tp) {
|
|
sprintf(cp, "<FORMAT %s TIMER=[%s]>%s</FORMAT>\n",
|
|
extra, *tp,
|
|
gs_eventToTextFormat); /* match event_to_text*/
|
|
for (; *cp; ++cp); /* skip to the end of the string */
|
|
}
|
|
/* BUG blocks matches clientSummary */
|
|
sprintf(cp, "<FORMAT %s PROTOCOL={%s}>blocks=blocks ",
|
|
extra, pp->name);
|
|
for (; *cp; ++cp); /* skip to the end of the string */
|
|
for (ii=0, tp=timerList; /* same as above list (for now) */
|
|
ii < (sizeof (timerList)/sizeof (timerList[0]));
|
|
++ii, ++tp) {
|
|
sprintf (cp, "%s=[%s] ", *tp, *tp);
|
|
for (; *cp; ++cp); /* skip to the end of the string */
|
|
}
|
|
strcat (cp, "</FORMAT>\n");
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Actual protocol handling code
|
|
*/
|
|
int
|
|
isSmtpResponseOK(char * s)
|
|
{
|
|
if (s[0] == '2' || s[0] == '3')
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
doSmtpCommandResponse(ptcx_t ptcx, SOCKET sock, char *command, char *response, int resplen)
|
|
{
|
|
int rc;
|
|
|
|
T_PRINTF(ptcx->logfile, command, strlen (command), "SMTP SendCommand");
|
|
rc = doCommandResponse(ptcx, sock, command, response, resplen);
|
|
if (rc == -1)
|
|
return rc;
|
|
T_PRINTF(ptcx->logfile, response, strlen(response),
|
|
"SMTP ReadResponse"); /* telemetry log. should be lower level */
|
|
/* D_PRINTF(stderr, "SMTP command=[%s] response=[%s]\n", command, response); */
|
|
if (!isSmtpResponseOK(response)) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
/* don't modify command (in case it could be re-tried) */
|
|
trimEndWhite (response); /* clean up for printing */
|
|
strcpy (ptcx->errMsg, "SmtpCommandResponse: got SMTP error response");
|
|
}
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const char basis_64[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
int
|
|
str_to_base64(const char *str, unsigned int len, char *buf, unsigned int buflen)
|
|
{
|
|
unsigned int bufused = 0;
|
|
int c1, c2, c3;
|
|
|
|
while (len) {
|
|
if (bufused >= buflen-4) {
|
|
/* out of space */
|
|
return -1;
|
|
}
|
|
|
|
c1 = (unsigned char)*str++;
|
|
buf[bufused++] = basis_64[c1>>2];
|
|
|
|
if (--len == 0) c2 = 0;
|
|
else c2 = (unsigned char)*str++;
|
|
buf[bufused++] = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
|
|
|
|
if (len == 0) {
|
|
buf[bufused++] = '=';
|
|
buf[bufused++] = '=';
|
|
break;
|
|
}
|
|
|
|
if (--len == 0) c3 = 0;
|
|
else c3 = (unsigned char)*str++;
|
|
|
|
buf[bufused++] = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
|
|
if (len == 0) {
|
|
buf[bufused++] = '=';
|
|
break;
|
|
}
|
|
|
|
--len;
|
|
buf[bufused++] = basis_64[c3 & 0x3F];
|
|
}
|
|
|
|
if (bufused >= buflen-2) {
|
|
/* out of space */
|
|
return -1;
|
|
}
|
|
buf[bufused] = '\0';
|
|
return bufused;
|
|
}
|
|
|
|
int
|
|
smtpAuthPlain(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, SOCKET sock)
|
|
{
|
|
char command[MAX_COMMAND_LEN];
|
|
char respBuffer[MAX_RESPONSE_LEN];
|
|
char mailUser[MAX_MAILADDR_LEN];
|
|
char userPasswd[MAX_MAILADDR_LEN];
|
|
unsigned long loginNum;
|
|
unsigned long domainNum;
|
|
char auth_msg[1024];
|
|
int auth_msg_len=0;
|
|
char base64_buf[1024];
|
|
int rc;
|
|
pish_stats_t *stats = (pish_stats_t *)ptimer->data;
|
|
pish_command_t *pish = (pish_command_t *)cmd->data;
|
|
|
|
/* generate a random username (with a mailbox on the server) */
|
|
domainNum = rangeNext (&stats->domainRange, stats->lastDomain);
|
|
stats->lastDomain = domainNum;
|
|
loginNum = rangeNext (&stats->loginRange, stats->lastLogin);
|
|
stats->lastLogin = loginNum;
|
|
|
|
sprintf(mailUser, pish->loginFormat, loginNum, domainNum);
|
|
sprintf(userPasswd, pish->passwdFormat, loginNum);
|
|
|
|
D_PRINTF(debugfile,"mailUser=[%.64s]\n", mailUser);
|
|
|
|
sprintf(command, "AUTH PLAIN%s", CRLF);
|
|
|
|
event_start(ptcx, &stats->cmd);
|
|
rc = doSmtpCommandResponse(ptcx, sock, command, respBuffer, sizeof(respBuffer));
|
|
event_stop(ptcx, &stats->cmd);
|
|
if (rc == -1) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->login.errs++;
|
|
strcat (ptcx->errMsg, "<SmtpLogin: failure sending AUTH PLAIN");
|
|
trimEndWhite (command);
|
|
returnerr(debugfile, "%s command=[%.99s] response=[%.99s]\n", /* ??? */
|
|
ptcx->errMsg, command, respBuffer);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* should now have a 3xx continue code */
|
|
|
|
/* authenticate id */
|
|
strcpy(auth_msg, ""); /* who you want to be (must have rights to do this) */
|
|
auth_msg_len += strlen("");
|
|
auth_msg[auth_msg_len++] = '\0';
|
|
/* authorize id */
|
|
strcpy(auth_msg + auth_msg_len, mailUser); /* who you are */
|
|
auth_msg_len += strlen(mailUser);
|
|
auth_msg[auth_msg_len++] = '\0';
|
|
/* password */
|
|
strcpy(auth_msg + auth_msg_len, userPasswd); /* your credentials */
|
|
auth_msg_len += strlen(userPasswd);
|
|
|
|
if (str_to_base64(auth_msg, auth_msg_len, base64_buf, sizeof(base64_buf)) == -1) {
|
|
stats->login.errs++;
|
|
strcpy (ptcx->errMsg, "SmtpLogin: Internal error encoding user");
|
|
returnerr(debugfile, "%s [%.199s]\n", ptcx->errMsg, mailUser); /* ??? */
|
|
return -1;
|
|
}
|
|
sprintf(command, "%s%s", base64_buf, CRLF);
|
|
|
|
event_start(ptcx, &stats->login);
|
|
rc = doSmtpCommandResponse(ptcx, sock, command, respBuffer, sizeof(respBuffer));
|
|
event_stop(ptcx, &stats->login);
|
|
if (rc == -1) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->login.errs++;
|
|
strcat (ptcx->errMsg, "<SmtpLogin: failure sending auth message");
|
|
returnerr(debugfile,"%s [%.199s]\n", ptcx->errMsg, mailUser); /* ??? */
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* should look for 2xx code for ok, 4xx is ldap error, 5xx means invalid login */
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
smtpAuthLogin(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, SOCKET sock)
|
|
{
|
|
char command[MAX_COMMAND_LEN];
|
|
char respBuffer[MAX_RESPONSE_LEN];
|
|
char mailUser[MAX_MAILADDR_LEN];
|
|
char userPasswd[MAX_MAILADDR_LEN];
|
|
unsigned long loginNum;
|
|
unsigned long domainNum;
|
|
char base64_buf[1024];
|
|
int rc;
|
|
pish_stats_t *stats = (pish_stats_t *)ptimer->data;
|
|
pish_command_t *pish = (pish_command_t *)cmd->data;
|
|
|
|
/* generate a random username (with a mailbox on the server) */
|
|
domainNum = rangeNext (&stats->domainRange, stats->lastDomain);
|
|
stats->lastDomain = domainNum;
|
|
loginNum = rangeNext (&stats->loginRange, stats->lastLogin);
|
|
stats->lastLogin = loginNum;
|
|
|
|
sprintf(mailUser, pish->loginFormat, loginNum, domainNum);
|
|
sprintf(userPasswd, pish->passwdFormat, loginNum);
|
|
|
|
D_PRINTF(debugfile,"mailUser=[%.64s]\n", mailUser);
|
|
|
|
sprintf(command, "AUTH LOGIN%s", CRLF);
|
|
|
|
event_start(ptcx, &stats->cmd);
|
|
rc = doSmtpCommandResponse(ptcx, sock, command, respBuffer, sizeof(respBuffer));
|
|
event_stop(ptcx, &stats->cmd);
|
|
if (rc == -1) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->login.errs++;
|
|
strcat (ptcx->errMsg, "<SmtpLogin: failure sending AUTH LOGIN");
|
|
trimEndWhite (command);
|
|
returnerr(debugfile, "%s command=[%.99s] response=[%.99s]\n", /* ??? */
|
|
ptcx->errMsg, command, respBuffer);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* should now have a 3xx continue code */
|
|
|
|
if (str_to_base64(mailUser, strlen(mailUser),
|
|
base64_buf, sizeof(base64_buf)) == -1) {
|
|
stats->login.errs++;
|
|
strcpy (ptcx->errMsg, "SmtpLogin: Internal error encoding user");
|
|
returnerr(debugfile, "%s [%.199s]\n", ptcx->errMsg, mailUser); /* ??? */
|
|
return -1;
|
|
}
|
|
sprintf(command, "%s%s", base64_buf, CRLF);
|
|
|
|
event_start(ptcx, &stats->cmd);
|
|
rc = doSmtpCommandResponse(ptcx, sock, command, respBuffer, sizeof(respBuffer));
|
|
event_stop(ptcx, &stats->cmd);
|
|
if (rc == -1) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->login.errs++;
|
|
strcat (ptcx->errMsg, "<SmtpLogin: failure sending user");
|
|
returnerr(debugfile,"%s [%.199s]\n", ptcx->errMsg, mailUser); /* ??? */
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* should now have a 3xx continue code */
|
|
|
|
if (str_to_base64(userPasswd, strlen(userPasswd),
|
|
base64_buf, sizeof(base64_buf)) == -1) {
|
|
stats->login.errs++;
|
|
strcpy (ptcx->errMsg, "SmtpLogin: Internal error encoding password");
|
|
returnerr(debugfile, "%s [%.199s]\n", ptcx->errMsg, userPasswd); /* ??? */
|
|
return -1;
|
|
}
|
|
sprintf(command, "%s%s", base64_buf, CRLF);
|
|
|
|
event_start(ptcx, &stats->login);
|
|
rc = doSmtpCommandResponse(ptcx, sock, command, respBuffer, sizeof(respBuffer));
|
|
event_stop(ptcx, &stats->login);
|
|
if (rc == -1) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->login.errs++;
|
|
strcat (ptcx->errMsg, "<SmtpLogin: failure sending password");
|
|
returnerr(debugfile,"%s user=%.99s pass=%.99s\n", /* ??? */
|
|
ptcx->errMsg, mailUser, userPasswd);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* should look for 2xx code for ok, 4xx is ldap error, 5xx means invalid login */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Entry point for running tests */
|
|
void *
|
|
sendSMTPStart(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer)
|
|
{
|
|
doSMTP_state_t *me = (doSMTP_state_t *)mycalloc (sizeof (doSMTP_state_t));
|
|
char respBuffer[MAX_RESPONSE_LEN];
|
|
char command[MAXCOMMANDLEN];
|
|
int numBytes;
|
|
int rc;
|
|
pish_command_t *pish = (pish_command_t *)cmd->data;
|
|
pish_stats_t *stats = (pish_stats_t *)ptimer->data;
|
|
|
|
if (!me) return NULL;
|
|
|
|
|
|
event_start(ptcx, &stats->connect);
|
|
ptcx->sock = connectSocket(ptcx, &pish->hostInfo, "tcp");
|
|
event_stop(ptcx, &stats->connect);
|
|
if (BADSOCKET(ptcx->sock)) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->connect.errs++;
|
|
returnerr(debugfile, "%s SMTP Couldn't connect to %s: %s\n",
|
|
ptcx->errMsg, pish->hostInfo.hostName, neterrstr());
|
|
}
|
|
myfree (me);
|
|
return NULL;
|
|
}
|
|
|
|
if (gf_abortive_close) {
|
|
if (set_abortive_close(ptcx->sock) != 0) {
|
|
returnerr (debugfile, "SMTP: WARNING: Could not set abortive close\n");
|
|
}
|
|
}
|
|
|
|
/* READ connect response from server */
|
|
event_start(ptcx, &stats->banner);
|
|
numBytes = readResponse(ptcx, ptcx->sock, respBuffer, sizeof(respBuffer));
|
|
event_stop(ptcx, &stats->banner);
|
|
if (numBytes <= 0) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->banner.errs++;
|
|
returnerr(debugfile,"%s SMTP Error reading banner: %s\n",
|
|
ptcx->errMsg, neterrstr());
|
|
}
|
|
doSMTPExit (ptcx, me);
|
|
return NULL;
|
|
}
|
|
if (isSmtpResponseOK(respBuffer) == 0) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->banner.errs++;
|
|
returnerr(debugfile, "%s Got SMTP ERROR response [%.99s]\n",
|
|
ptcx->errMsg, respBuffer);
|
|
}
|
|
doSMTPExit (ptcx, me);
|
|
return NULL;
|
|
}
|
|
D_PRINTF(debugfile,"read connect response\n");
|
|
|
|
|
|
if (pish->flags & useEHLO) {
|
|
/* send extended EHLO */
|
|
sprintf(command, "EHLO %s" CRLF, gs_thishostname);
|
|
} else {
|
|
/* send normal HELO */
|
|
sprintf(command, "HELO %s" CRLF, gs_thishostname);
|
|
}
|
|
event_start(ptcx, &stats->cmd);
|
|
rc = doSmtpCommandResponse(ptcx, ptcx->sock, command, respBuffer, sizeof(respBuffer));
|
|
event_stop(ptcx, &stats->cmd);
|
|
if (rc == -1) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->cmd.errs++;
|
|
trimEndWhite (command);
|
|
returnerr(debugfile, "%s SMTP HELO/EHLO [%.99s] ERROR reading response [%.99s]\n",
|
|
ptcx->errMsg, command, respBuffer);
|
|
}
|
|
doSMTPExit (ptcx, me);
|
|
return NULL;
|
|
}
|
|
|
|
if (pish->flags & useAUTHPLAIN) {
|
|
/* look for AUTH PLAIN LOGIN in respBuffer */
|
|
if (strstr(respBuffer, "AUTH PLAIN LOGIN") != NULL) {
|
|
/* FIX: time get base64 time and multiple round trips */
|
|
rc = smtpAuthPlain(ptcx, cmd, ptimer, ptcx->sock);
|
|
if (rc != 0) {
|
|
doSMTPExit (ptcx, me);
|
|
return NULL;
|
|
}
|
|
}
|
|
} else if (pish->flags & useAUTHLOGIN) {
|
|
/* look for AUTH LOGIN in respBuffer */
|
|
if (strstr(respBuffer, "AUTH=LOGIN") != NULL) {
|
|
/* FIX: time get base64 time and multiple round trips */
|
|
rc = smtpAuthLogin(ptcx, cmd, ptimer, ptcx->sock);
|
|
if (rc != 0) {
|
|
doSMTPExit (ptcx, me);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return me;
|
|
}
|
|
|
|
/*
|
|
* SEND A MESSAGE
|
|
*/
|
|
int
|
|
sendSMTPLoop(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, void *mystate)
|
|
{
|
|
doSMTP_state_t *me = (doSMTP_state_t *)mystate;
|
|
char command[MAXCOMMANDLEN];
|
|
char respBuffer[MAX_RESPONSE_LEN];
|
|
char rcptToUser[MAX_MAILADDR_LEN];
|
|
int rc, jj;
|
|
long addressNum;
|
|
long domainNum;
|
|
int numBytes;
|
|
pish_stats_t *stats = (pish_stats_t *)ptimer->data;
|
|
pish_command_t *pish = (pish_command_t *)cmd->data;
|
|
smtp_file_t *fileEntry;
|
|
|
|
/* send MAIL FROM:<username> */
|
|
if (pish->fileCount > 1) { /* randomly pick file */
|
|
int ff;
|
|
ff = (RANDOM() % pish->fileCount);
|
|
/*D_PRINTF(debugfile,"random file<%d=%d\n", pish->fileCount, ff);*/
|
|
fileEntry = ((smtp_file_t *)pish->files) + ff;
|
|
} else {
|
|
fileEntry = (smtp_file_t *)pish->files;
|
|
}
|
|
if (fileEntry->msgMailFrom != NULL) {
|
|
sprintf(command, "MAIL FROM:%s%s", fileEntry->msgMailFrom, CRLF);
|
|
} else {
|
|
sprintf(command, "MAIL FROM:<%s>%s", pish->smtpMailFrom, CRLF);
|
|
}
|
|
/*D_PRINTF(debugfile,"%s\n", command);*/
|
|
event_start(ptcx, &stats->cmd);
|
|
rc = doSmtpCommandResponse(ptcx, ptcx->sock, command, respBuffer, sizeof(respBuffer));
|
|
event_stop(ptcx, &stats->cmd);
|
|
if (rc == -1) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->cmd.errs++;
|
|
trimEndWhite (command);
|
|
returnerr(debugfile, "%s SMTP FROM [%.99s], ERROR reading [%.99s] response [%.99s]\n",
|
|
ptcx->errMsg, command, respBuffer);
|
|
}
|
|
doSMTPExit (ptcx, me);
|
|
return -1;
|
|
}
|
|
|
|
/* send RCPT TO:<username> for each recipient */
|
|
for (jj = 0; jj < pish->numRecipients; jj++) {
|
|
/* generate a random recipient (but with an account on the server) */
|
|
|
|
domainNum = rangeNext (&stats->domainRange, stats->lastDomain);
|
|
stats->lastDomain = domainNum;
|
|
addressNum = rangeNext (&stats->addressRange, stats->lastAddress);
|
|
stats->lastAddress = addressNum;
|
|
|
|
sprintf(rcptToUser, pish->addressFormat, addressNum, domainNum);
|
|
D_PRINTF(debugfile,"rcptToUser=%s\n", rcptToUser);
|
|
sprintf(command, "RCPT TO:<%s>%s", rcptToUser, CRLF);
|
|
event_start(ptcx, &stats->cmd);
|
|
rc = doSmtpCommandResponse(ptcx, ptcx->sock, command, respBuffer, sizeof(respBuffer));
|
|
event_stop(ptcx, &stats->cmd);
|
|
if (rc == -1) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->cmd.errs++;
|
|
trimEndWhite (command);
|
|
returnerr(debugfile, "%s SMTP RCPT [%.99s], ERROR reading [%.99s] response [%.99s]\n",
|
|
ptcx->errMsg, command, respBuffer);
|
|
}
|
|
doSMTPExit (ptcx, me);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* send DATA */
|
|
event_start(ptcx, &stats->cmd);
|
|
sprintf(command, "DATA%s", CRLF);
|
|
event_stop(ptcx, &stats->cmd);
|
|
if (doSmtpCommandResponse(ptcx, ptcx->sock, command, respBuffer, sizeof(respBuffer)) == -1 ||
|
|
respBuffer[0] != '3') {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->cmd.errs++;
|
|
returnerr(debugfile, "%s SMTP DATA ERROR, response [%.99s]\n",
|
|
ptcx->errMsg, respBuffer);
|
|
}
|
|
doSMTPExit (ptcx, me);
|
|
return -1;
|
|
}
|
|
|
|
D_PRINTF(debugfile, "data response %s\n", respBuffer);
|
|
|
|
/* send message */
|
|
event_start(ptcx, &stats->msgwrite);
|
|
numBytes = sendFile(ptcx, ptcx->sock,
|
|
NULL,
|
|
fileEntry->filename, fileEntry->offset,
|
|
MSG_TRAILER);
|
|
if (numBytes == -1) {
|
|
event_stop(ptcx, &stats->msgwrite);
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
returnerr(debugfile, "%s SMTP Error sending mail message: %s\n",
|
|
ptcx->errMsg, neterrstr());
|
|
stats->msgwrite.errs++;
|
|
}
|
|
doSMTPExit (ptcx, me);
|
|
return -1;
|
|
}
|
|
|
|
/* read server response */
|
|
numBytes = readResponse(ptcx, ptcx->sock, respBuffer, sizeof(respBuffer));
|
|
event_stop(ptcx, &stats->msgwrite);
|
|
if (numBytes <= 0) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
returnerr(debugfile,"%s SMTP Error reading send message response: %s\n",
|
|
ptcx->errMsg, neterrstr());
|
|
stats->msgwrite.errs++;
|
|
}
|
|
doSMTPExit (ptcx, me);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
sendSMTPEnd(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, void *mystate)
|
|
{
|
|
doSMTP_state_t *me = (doSMTP_state_t *)mystate;
|
|
char command[MAXCOMMANDLEN];
|
|
char respBuffer[MAX_RESPONSE_LEN];
|
|
int rc;
|
|
pish_stats_t *stats = (pish_stats_t *)ptimer->data;
|
|
|
|
if (BADSOCKET(ptcx->sock)) return; /* closed by previous error */
|
|
|
|
/* send QUIT */
|
|
sprintf(command, "QUIT%s", CRLF);
|
|
event_start(ptcx, &stats->logout);
|
|
rc = doSmtpCommandResponse(ptcx, ptcx->sock, command, respBuffer, sizeof(respBuffer));
|
|
event_stop(ptcx, &stats->logout);
|
|
if (rc == -1) {
|
|
if (gf_timeexpired < EXIT_FAST) {
|
|
stats->logout.errs++;
|
|
returnerr(debugfile, "%s SMTP QUIT ERROR, response [%.99s]\n",
|
|
ptcx->errMsg, respBuffer);
|
|
}
|
|
}
|
|
doSMTPExit (ptcx, me);
|
|
}
|
|
|
|
void
|
|
doSMTPExit (ptcx_t ptcx, doSMTP_state_t *me)
|
|
{
|
|
if (!BADSOCKET(ptcx->sock))
|
|
NETCLOSE(ptcx->sock);
|
|
ptcx->sock = BADSOCKET_VALUE;
|
|
|
|
myfree (me);
|
|
}
|