/*
File: smtp.c
Copyright (C) 1999,2000,2003,2004 by Wolfgang Zekoll <wzk@quietsche-entchen.de>
This source is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This source is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <signal.h>
#include <netdb.h>
#include <netinet/in.h>
#include <syslog.h>
#include <sys/time.h>
#include "smtp.h"
#include "acp.h"
#include "ip-lib.h"
#include "lib.h"
/*
* I/O functions - taken from ftp.proxy
*/
int getc_fd(smtp_t *x, int fd, int nooptime)
{
int c;
bio_t *bio;
if (fd == 0)
bio = &x->cbuf;
else if (fd == x->sin)
bio = &x->sbuf;
else {
syslog(LOG_NOTICE, "-PROXY: internal bio/fd error");
exit (1);
}
if (bio->here >= bio->len) {
int rc, max, bytes;
struct timeval tov;
fd_set available, fdset;
bio->len = bio->here = 0;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
max = fd;
bytes = 0;
while (1) {
available = fdset;
tov.tv_sec = x->config->timeout;
tov.tv_usec = 0;
if (debug >= 2)
fprintf (stderr, "select max= %d\n", max);
rc = select(max + 1, &available, (fd_set *) NULL, (fd_set *) NULL, &tov);
if (rc < 0) {
syslog(LOG_NOTICE, "-PROXY: select() error: %s", strerror(errno));
break;
}
else if (rc == 0) {
syslog(LOG_NOTICE, "-ERR: connection timed out: client= %s, server= %s",
x->client, x->servername);
return (-1);
}
if (FD_ISSET(fd, &available)) {
if ((bytes = read(fd, bio->buffer, sizeof(bio->buffer) - 2)) <= 0) {
if (debug != 0) {
if (bytes == 0)
fprintf (stderr, "received zero bytes on fd %d\n", fd);
else
fprintf (stderr, "received %d bytes on fd %d, errno= %d, error= %s\n", bytes, fd, errno, strerror(errno));
}
return (-1);
}
break;
}
}
bio->len = bytes;
bio->here = 0;
}
if (bio->here >= bio->len)
return (-1);
c = (unsigned char) bio->buffer[bio->here++];
return (c);
}
char *readline_fd(smtp_t *x, int fd, char *line, int size, int nooptime)
{
int c, k;
*line = 0;
size = size - 2;
c = getc_fd(x, fd, nooptime);
if (c < 0)
return (NULL);
else if (c == 1) {
strcpy(line, "\001");
return (line);
}
k = 0;
while (c > 0 && c != '\n' && c != 0) {
if (k < size)
line[k++] = c;
c = getc_fd(x, fd, nooptime);
}
line[k] = 0;
noctrl(line);
return (line);
}
char *cfgets(smtp_t *x, char *line, int size, int nooptime)
{
char *p;
*line = 0;
if ((p = readline_fd(x, 0, line, size, nooptime)) == NULL)
return (NULL);
else if (debug != 0)
fprintf (stderr, "CLI >>>: %s\n", p);
return (line);
}
int cfputs(smtp_t *x, char *line)
{
if (debug)
fprintf (stderr, ">>> CLI: %s\n", line);
/*
* Something to do: handler "\r\n" the same way as for the
* server.
*/
write(1, line, strlen(line));
write(1, "\r\n", 2);
return (0);
}
char *sfgets(smtp_t *x, char *line, int size)
{
char *p;
*line = 0;
if ((p = readline_fd(x, x->sin, line, size, 0)) == NULL)
return (NULL);
else if (debug != 0)
fprintf (stderr, "SVR >>>: %s\n", p);
return (line);
}
int sfputs(smtp_t *x, char *format, ...)
{
char buffer[4096]; /* Puffer auf 4k gesetzt, wegen langen Zeilen */
va_list ap;
va_start(ap, format);
vsnprintf (buffer, sizeof(buffer) - 10, format, ap);
va_end(ap);
if (debug)
fprintf (stderr, ">>> SVR: %s\n", buffer);
strcat(buffer, "\r\n");
write(x->sout, buffer, strlen(buffer));
return (0);
}
int sfputc(smtp_t *x, char *command, char *parameter, char *line, int size, char **here)
{
int rc;
char *p, buffer[300];
if (command != NULL && *command != 0) {
if (parameter != NULL && *parameter != 0)
snprintf (buffer, sizeof(buffer) - 2, "%s %s", command, skip_ws(parameter));
else
copy_string(buffer, command, sizeof(buffer));
sfputs(x, "%s", buffer);
}
if (sfgets(x, line, size) == NULL) {
if (debug != 0)
fprintf (stderr, "server disappered in sfputc(), pos #1\n");
return (-1);
}
rc = strtol(line, &p, 10);
p = skip_ws(p);
if (*p == '-') {
cfputs(x, line);
while (1) {
if (sfgets(x, line, size) == NULL) {
if (debug != 2)
fprintf (stderr, "server disappered in sfputc(), pos #2\n");
return (-1);
}
p = noctrl(line);
if (*p == ' ')
/* weiter */ ;
else if (atoi(line) == rc) {
if (strlen(line) == 3)
break;
else if (strlen(line) > 3 && line[3] == ' ')
break;
}
cfputs(x, line);
}
}
p = skip_ws(p);
if (here != NULL)
*here = p;
return (rc);
}
int reset_connection(smtp_t *x)
{
x->state = WAITING;
x->nrcpt = 0;
x->rcpt.here = 0;
/* *x->rcpt.list = 0; */
*x->sender = 0;
x->nrcpt = 0;
*x->jobid = 0;
*x->msgid = 0;
x->size = 0;
return (0);
}
char *get_emailadr(char *envelope, char *email, int size)
{
char *p, *r;
*email = 0;
/*
* Sonderfall: Es ist moeglich, dass die envelope Adresse `<>'
* ist. In diesem liefern wir sie unveraendert zurueck. Das
* beisst sich nicht mit Adressen der Form `<<>>', da geschachtelte
* spitze Klammern nicht unterstuetzt werden.
*/
if (strcmp(envelope, "<>") == 0) {
strcpy(email, "<>");
return (email);
}
p = envelope;
if (*p != '<')
return (email);
p++;
if ((r = strchr(p, '>')) == NULL)
return (email);
else if (r[1] != 0)
return (email);
get_quoted(&p, '>', email, size);
return (email);
}
int check_emailadr(char *emailadr)
{
char *domain;
if (strcmp(emailadr, "<>") == 0)
return (1);
else if (*emailadr == 0)
return (0);
/* else if (*emailadr == '@')
* return (0);
*/
else if ((domain = strchr(emailadr, '@')) == NULL)
return (0);
/* else if (domain[1] == '0')
* return (0);
*/
else if (strchr(&domain[1], '@') != NULL)
return (0);
else if (strchr(emailadr, '!') != NULL /* || strchr(emailadr, '%') != NULL */)
return (0);
else if (strchr(emailadr, ':') != NULL || strchr(emailadr, ',') != NULL)
return (0);
return (1);
}
int search_allowlist(char *emailadr, char *list)
{
char *p, *domain, pattern[200];
if (strcmp(emailadr, "<>") == 0)
return (1);
domain = strchr(emailadr, '@');
if (domain == NULL || *domain == 0) {
/*
* Das kann eigentlich nicht passieren, die E-Mail Adresse
* wurde schon auf genau ein @-Zeichen getestet.
*/
return (0);
}
else if (list == NULL || *list == 0) {
/*
* Kann eigentlich auch nicht vorkommen.
*/
return (0);
}
p = list;
while ((p = skip_ws(p)), *get_quoted(&p, ',', pattern, sizeof(pattern)) != 0) {
noctrl(pattern);
if (*pattern == '@' && strpcmp(domain, pattern) == 0)
return (1);
else if (strpcmp(emailadr, pattern) == 0)
return (1);
}
return (0);
}
int addrcpt(smtp_t *x, char *rcpt)
{
int len;
char adr[200];
/*
* Remove angle brackets from e-mail address ...
*/
copy_string(adr, &rcpt[1], sizeof(adr));
len = strlen(adr);
if (len > 0)
adr[len-1] = 0;
/*
* ... and add it to the list.
*/
len = strlen(rcpt = adr);
if ((x->rcpt.here + len + 10) >= x->rcpt.max) {
x->rcpt.max += 1024 + len;
x->rcpt.list = reallocate(x->rcpt.list, x->rcpt.max);
}
if (x->rcpt.here > 0)
x->rcpt.list[x->rcpt.here++] = ' ';
strcpy(&x->rcpt.list[x->rcpt.here], rcpt);
x->rcpt.here += len;
return (0);
}
/*
* spooldata() - liest entweder vom client (bei "fp == NULL")
* oder aus der angegebenen Datei. Das Ausgabeziel der Daten
* ist in beiden Faellen der SMTP-Server.
*/
int spooldata(smtp_t *x, FILE *fp)
{
int lineno, isheader;
char *p, line[2048];
lineno = 0;
isheader = 1;
while (1) {
if (fp != NULL) {
if (fgets(line, sizeof(line), fp) == NULL) {
/*
* Provide the end-of-data line and jump.
*/
strcpy(line, ".");
goto end;
}
}
else {
if (cfgets(x, line, sizeof(line), x->config->timeout) == NULL) {
syslog(LOG_NOTICE, "-CLIENT: client terminated while sending data");
return (-1);
}
}
noctrl(line);
/*
* Insert Received header line.
*/
if (lineno == 0) {
if (x->config->droppath != 2) {
char buffer[1024];
snprintf (buffer, sizeof(buffer) - 2, "Received: from %s (%s) by %s",
x->client, x->ipnum, x->hostname);
sfputs(x, "%s", buffer);
}
}
/*
* If we are in the mail header we (1) try to read the
* mail's message-id and (2) remove the recevied-path
* from the header if removal is configured.
*/
if (isheader == 1) {
if (*line == 0) {
isheader = 0;
if (*x->msgid == 0) {
unsigned long now;
now = time(NULL);
snprintf (x->msgid, sizeof(x->msgid) - 2, "%lu.%d.%d", now, getpid(), x->mailcount);
syslog(LOG_NOTICE, "no message id, assuming %s, client= %s", x->msgid, x->client);
}
}
else {
char *p, word[80];
p = line;
get_word(&p, word, sizeof(word));
strlwr(word);
if (strcmp(word, "message-id:") == 0) {
if (*x->msgid != 0) {
syslog(LOG_NOTICE, "duplicate message id, client= %s", x->client);
continue;
}
else {
if ((p = strchr(p, '<')) != NULL) {
p++;
get_quoted(&p, '>', x->msgid, sizeof(x->msgid));
}
}
}
else if (strcmp(word, "received:") == 0) {
if (x->config->droppath != 0)
continue;
}
}
}
/*
* Send current line to SMTP server. Notice: If we are
* reading from a file we have to provide an additional
* dot `.' if the line starts with a dot.
*/
p = line;
if (fp != NULL) {
if (*line == '.') {
char buffer[2050];
snprintf (p = buffer, sizeof(buffer), ".%s", line);
}
}
end:
if (sfputs(x, "%s", p) != 0) {
syslog(LOG_NOTICE, "-SERVER: server terminated while receiving data");
return (-1);
}
/*
* Did we reach the end of the mail?
*/
if (line[0] == '.' && line[1] == 0)
break;
x->size += strlen(p) + 2;
lineno++;
}
return (0);
}
int proxy_request(config_t *config)
{
int rc;
char *p, command[10], word[200], line[2048];
smtp_t *x;
x = allocate(sizeof(smtp_t));
get_client_info(0, x->ipnum, x->client);
x->mailcount = 0;
syslog(LOG_NOTICE, "connected to client: %s", x->ipnum);
if (*config->clientdir != 0) {
char logfile[200];
unsigned long now;
struct stat sbuf;
now = time(NULL);
snprintf (logfile, sizeof(logfile) - 2, "%s/%s", config->clientdir, x->ipnum);
if (stat(logfile, &sbuf) != 0 || (now - sbuf.st_mtime) >= config->accepttime) {
printf ("421 service unavailable - authenticate with POP3 first\r\n");
syslog(LOG_NOTICE, "-ERR: client not permitted: %s", x->ipnum);
goto end;
}
}
if (*config->server != 0) {
unsigned int port;
char server[200];
copy_string(server, config->server, sizeof(server));
port = get_port(server, 25);
if ((x->sin = openip(server, port)) < 0) {
printf ("451 Service unavailable\r\n");
syslog(LOG_NOTICE, "-ERR: can't connect to server: %s:%u, %s", server, port, strerror(errno));
exit (1);
}
x->sout = x->sin;
copy_string(x->servername, config->server, sizeof(x->servername));
}
else if (config->argv != NULL) {
int pid, pin[2], pout[2];
if (pipe(pin) != 0 || pipe(pout) != 0) {
printf ("451 Service unavailable\r\n");
syslog(LOG_NOTICE, "-PROXY: can't pipe(): %m");
exit (-1);
}
else if ((pid = fork()) < 0) {
printf ("451 Service unavailable\r\n");
syslog(LOG_NOTICE, "-PROXY: can't fork(): %m");
exit (-1);
}
else if (pid == 0) {
dup2(pin[1], 1);
close (pin[0]);
dup2(pout[0], 0);
close(pout[1]);
close (2);
execvp(config->argv[0], config->argv);
printf ("451 Service unavailable\r\n");
syslog(LOG_NOTICE, "-PROXY: can't execute: %s: %m", config->argv[0]);
exit (1);
}
else {
x->sin = pin[0];
close(pin[1]);
x->sout = pout[1];
close(pout[0]);
copy_string(x->servername, config->argv[0], sizeof(x->servername));
}
}
else {
printf ("451 Service unavailable\r\n");
syslog(LOG_NOTICE, "-PROXY: no server specified");
exit (1);
}
syslog(LOG_NOTICE, "connected to server: %s", x->servername);
/* Konfiguration uebernehmen
*/
x->config = config;
/* Hostnamen holen
*/
gethostname(word, sizeof(word));
getdomainname(line, sizeof(line));
snprintf (x->hostname, sizeof(x->hostname) - 2, word, line);
/* Greeting Message vom Sendmail Server lesen, und an
* Client schicken.
*/
sfgets(x, line, sizeof(line));
while (line[3] != ' ') {
if (sfgets(x, line, sizeof(line)) == NULL) {
syslog(LOG_NOTICE, "-SERVER: lost server while reading server greeting");
exit (1);
}
}
cfputs(x, line);
/* Wir stellen uns beim lokalen Sendmail Server vor. Die
* EHLO-replys werden 'verschluckt' und durch einen eigenen
* ersetzt.
*/
sfputs(x, "EHLO localhost");
while (sfgets(x, line, sizeof(line)) != NULL) {
if (line[3] == ' ')
break;
}
rc = atol(line);
if (rc != 250) {
syslog(LOG_NOTICE, "-SERVER: server HELO: status is not 250: %s", line);
cfputs(x, "421 service unavailable");
exit (1);
}
/*
* ** S M T P M A I N L O O P
*/
x->state = WAITING;
while (1) {
rc = 0; /* Server response code loeschen */
/*
* Naechstes Kommando vom Client holen.
*/
if (cfgets(x, line, sizeof(line), 0) == NULL) {
syslog(LOG_NOTICE, "-CLIENT: client closed connection");
break;
}
/* Kommando isolieren
*/
p = noctrl(line);
get_word(&p, command, sizeof(command));
strupr(command);
p = skip_ws(p);
/* QUIT ist immer moeglich.
*/
if (strcmp(command, "QUIT") == 0) {
rc = sfputc(x, "QUIT", "", line, sizeof(line), NULL);
cfputs(x, line);
x->state = SEND_QUIT;
break;
}
/* HELP
*/
else if (strcmp(command, "HELP") == 0) {
cfputs(x, "503 no help available");
}
/* NOOP
*/
else if (strcmp(command, "NOOP") == 0) {
rc = sfputc(x, "NOOP", "", line, sizeof(line), NULL);
cfputs(x, line);
}
/* RSET
*/
else if (strcmp(command, "RSET") == 0) {
rc = sfputc(x, "RSET", "", line, sizeof(line), NULL);
cfputs(x, line);
reset_connection(x);
syslog(LOG_NOTICE, "RSET command, client= %s", x->client);
}
/* ETRN
*/
else if (strcmp(command, "ETRN") == 0) {
if (x->config->etrn == 0) {
cfputs(x, "500 unrecognized command");
syslog(LOG_NOTICE, "-ERR: ETRN request rejected: client= %s", x->client);
}
else {
if (*get_word(&p, word, sizeof(word)) == 0)
cfputs(x, "500 ETRN needs parameter");
else {
rc = sfputc(x, "ETRN", word, line, sizeof(line), NULL);
cfputs(x, line);
if (rc != 250)
syslog(LOG_NOTICE, "-ERR: ETRN rejected by server, client= %s", x->client);
}
}
}
/* HELO und EHLO sind auch immer verfuegbar, aber nur
* einmal.
*/
else if (strcmp(command, "HELO") == 0 || strcmp(command, "EHLO") == 0) {
if (x->helloseen != 0)
cfputs(x, "503 duplicate HELO/EHLO");
else if (*get_word(&p, word, sizeof(word)) == 0) {
snprintf (line, sizeof(line) - 2, "501 %s requires domain name", command);
cfputs(x, line);
}
else {
if (strcmp(command, "HELO") == 0) {
snprintf (line, sizeof(line) - 2, "250 SMTP server v%s ready - %s [%s]", VERSION, x->client, x->ipnum);
cfputs(x, line);
}
else {
snprintf (line, sizeof(line) - 2, "250-SMTP server v%s ready - %s [%s]", VERSION, x->client, x->ipnum);
cfputs(x, line);
cfputs(x, "250-8BITMIME");
if (x->config->etrn != 0)
cfputs(x, "250-ETRN");
cfputs(x, "250 HELP");
}
x->helloseen = 1;
}
}
/* MAIL, SEND, SOML, SAML
*
* Laut RFC 821 kann das MAIL Kommando jederzeit abgesetzt
* werden, es macht dabei einen impliziten SMTP-Reset. Der
* real existierende Sendmail will davon aber nichts wissen.
*/
else if (strcmp(command, "MAIL") == 0 || strcmp(command, "SEND") == 0 ||
strcmp(command, "SOML") == 0 || strcmp(command, "SAML") == 0) {
get_quoted(&p, ':', word, sizeof(word));
if (strcasecmp(word, "FROM") != 0)
cfputs(x, "500 syntax error");
else if (*x->sender != 0)
cfputs(x, "503 sender already specified");
else {
int allowed;
char sender[200], emailadr[200];
p = skip_ws(p);
get_word(&p, sender, sizeof(sender));
strlwr(sender);
/*
* Wir machen ein paar grundsaetzliche Tests mit
* der Absenderadresse:
*
* - Ist die Adresse von spitzen Klammern
* umgeben?
* ...
*/
allowed = 1;
get_emailadr(sender, emailadr, sizeof(emailadr));
if (*emailadr == 0)
allowed = 0;
/*
* ...
* - Enthaelt die Adresse mindestens ein @-Zeichen?
* - Enthaelt die Adresse genau ein @-Zeichen?
* - Ist in der Adresse kein !- und kein %-Zeichen
* enthalten.
* ...
*/
else if (check_emailadr(emailadr) == 0)
allowed = 0;
/*
* ...
* - Schliesslich wird ggf. noch getestet,
* ob die Absenderadresse auch auf der
* allow-Liste steht.
*
* Mit den Empfaengeradressen werden die gleichen Tests
* durchgefuehrt.
*/
else if ((p = x->config->senderlist) == NULL || *p == 0)
allowed = 1; /* kein Adresstest */
else
allowed = search_allowlist(emailadr, x->config->senderlist);
if (allowed == 0) {
char line[300];
snprintf (line, sizeof(line) - 2, "550 not allowed: %s", sender);
cfputs(x, line);
syslog(LOG_NOTICE, "-FROM: sender not allowed: %s, client= %s", sender, x->client);
}
else {
char smtpcmd[20];
/*
* Der Absender ist soweit ok. Was wir jetzt ggf. machen muessen
* ist die Absenderdomain neu schreiben.
*/
copy_string(x->origsender, sender, sizeof(x->origsender));
if (*x->config->senderdomain != 0) {
char *p, newadr[80];
if ((p = strchr(sender, '@')) != NULL)
*p = 0;
p = sender;
if (*p == '<') /* Sollte eigentlich immer der Fall sein */
p++;
snprintf (newadr, sizeof(newadr) - 2, "<%s@%s>", p, x->config->senderdomain);
copy_string(sender, newadr, sizeof(sender));
}
snprintf (smtpcmd, sizeof(smtpcmd) - 2, "%s FROM:", command);
rc = sfputc(x, smtpcmd, sender, line, sizeof(line), NULL);
cfputs(x, line);
if (rc == 250) {
copy_string(x->sender, sender, sizeof(sender));
x->state = MAIL_SEEN;
syslog(LOG_NOTICE, "sender: %s", x->sender);
}
else {
syslog(LOG_NOTICE, "-FROM: sender rejected: %s, client= %s, resp= %s", sender, x->client, line);
}
}
}
}
/* RCPT
*/
else if (strcmp(command, "RCPT") == 0) {
get_quoted(&p, ':', word, sizeof(word));
if (strcasecmp(word, "TO") != 0)
cfputs(x, "500 syntax error");
else if (x->state != MAIL_SEEN && x->state != RCPT_SEEN)
cfputs(x, "503 specify sender first");
else {
int allowed;
char rcpt[200], emailadr[200];
p = skip_ws(p);
get_word(&p, rcpt, sizeof(rcpt));
strlwr(rcpt);
get_emailadr(rcpt, emailadr, sizeof(emailadr));
if (*emailadr == 0)
allowed = 0;
else if (check_emailadr(emailadr) == 0)
allowed = 0;
else if ((p = x->config->rcptlist) == NULL || *p == 0)
allowed = 1;
else
allowed = search_allowlist(emailadr, x->config->rcptlist);
if (allowed == 0) {
char line[300];
snprintf (line, sizeof(line) - 2, "550 no such user: %s", rcpt);
cfputs(x, line);
syslog(LOG_NOTICE, "-RCPT: recipient not allowed: %s, client= %s", rcpt, x->client);
}
else {
rc = sfputc(x, "RCPT TO:", rcpt, line, sizeof(line), NULL);
cfputs(x, line);
if (rc == 250 || rc == 251) {
addrcpt(x, rcpt);
x->nrcpt++;
x->state = RCPT_SEEN;
syslog(LOG_NOTICE, "rcpt: %s", rcpt);
}
else {
syslog(LOG_NOTICE, "-RCPT: recipient rejected: %s, client= %s, resp= %s", rcpt, x->client, line);
}
}
}
}
/* DATA
*/
else if (strcmp(command, "DATA") == 0) {
x->mailcount++;
if (x->state != RCPT_SEEN)
cfputs(x, "503 specify recipients first");
else {
FILE *fp;
if (*x->config->acp != 0) {
run_acp(x, line, sizeof(line));
rc = atoi(line);
if (rc == 250 || rc == 354)
/* ok for us, continue processing */ ;
else if (rc >= 400 && rc < 600) {
syslog(LOG_NOTICE, "-DATA: acp status: %s", line);
cfputs(x, line);
sfputc(x, "RSET", "", line, sizeof(line), NULL);
reset_connection(x);
if (rc == 421)
break;
continue;
}
else {
syslog(LOG_NOTICE, "-PROXY: acp status error: %s", line);
cfputs(x, "421 server error");
break;
}
}
if (*x->config->ccp == 0) {
rc = sfputc(x, "DATA", "", line, sizeof(line), NULL);
cfputs(x, line);
}
else {
/*
* Ok, in case that we have a command control
* program (a ccp) we have interesting things
* to do.
*
* First we create a temporary spool file to
* store the mail data from the client ...
*/
gettmpfile(x->spoolfile, sizeof(x->spoolfile));
cfputs(x, "354 send data");
if ((rc = receivemail(x)) < 0)
break;
else if (rc != 0)
copy_string(line, "421 server error", sizeof(line));
else {
/*
* ... and then we call the ccp and look at
* it's SMTP compatible return code.
*/
run_ccp(x, line, sizeof(line));
}
rc = atoi(line);
if (rc == 250 || rc == 354)
/* ok for us, let's pass the e-mail to the server */ ;
else if (rc >= 400 && rc < 600) {
/*
* Somethings wrong with the e-mail. Log the event,
* pass the message to the client, reset the connection
* to the server and continue.
*/
syslog(LOG_NOTICE, "-DATA: ccp status: %s", line);
cfputs(x, line);
sfputc(x, "RSET", "", line, sizeof(line), NULL);
reset_connection(x);
if (rc == 421)
break;
/*
* ... and stop processing this e-mail
*/
continue;
}
else {
/*
* Something is wrong with the ccp. We terminate
* with a 421 response code.
*/
syslog(LOG_NOTICE, "-PROXY: ccp status error: %s", line);
cfputs(x, "421 server error");
break;
}
/*
* If we made it here we have to send the DATA
* command to the server. Response checking is
* below.
*/
rc = sfputc(x, "DATA", "", line, sizeof(line), NULL);
}
if (rc == 354) {
fp = NULL;
if (*x->spoolfile != 0) {
if ((fp = fopen(x->spoolfile, "r")) == NULL) {
cfputs(x, "421 server error");
syslog(LOG_NOTICE, "-PROXY: can't open spoolfile: %s, error= %s", x->spoolfile, strerror(errno));
exit (1);
}
}
if ((rc = spooldata(x, fp)) == 0) {
sfgets(x, line, sizeof(line));
cfputs(x, line);
rc = atoi(line);
if (rc == 250) {
p = line;
get_word(&p, word, sizeof(word));
get_word(&p, x->jobid, sizeof(x->jobid));
}
}
if (fp != NULL) {
fclose (fp);
unlink(x->spoolfile);
}
}
syslog(LOG_NOTICE, "+MAIL: server= %s, client= %s, sender= %s, nrcpt= %d, size= %ld, message-id= <%s>, resp= %s",
x->servername, x->client, x->sender, x->nrcpt, x->size,
x->msgid, line);
reset_connection(x);
x->state = WAITING;
}
}
/*
* Other commands are not supported.
*/
else {
cfputs(x, "500 unrecognized command");
syslog(LOG_NOTICE, "-SMTP: received unknown command: %s", command);
}
/*
* Final status code checking - this assumes that the `rc'
* variable holds the current server status response code.
*/
if (rc == 421) {
syslog(LOG_NOTICE, "-ERR: sendmail returned 421, state= %d, command= %s", x->state, command);
break;
}
else if (rc == -1) {
syslog(LOG_NOTICE, "terminating (sendmail terminated)");
x->state = NO_SENDMAIL;
break;
}
}
if (x->state != SEND_QUIT && x->state != NO_SENDMAIL) {
sfputs(x, "QUIT", "");
sfgets(x, line, sizeof(line));
rc = atoi(line);
}
end:
syslog(LOG_NOTICE, "+OK: client %s disconnecting, %d mails", x->client, x->mailcount);
return (0);
}
syntax highlighted by Code2HTML, v. 0.9.1