home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Meeting Pearls 3
/
Meeting_Pearls_III.iso
/
Pearls
/
tcp
/
Networking
/
TCP
/
Mail
/
SMTPd
/
smtpd.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-06
|
15KB
|
828 lines
RCS_ID_C= "$Id: smtpd.c,v 1.7 1994/02/06 10:07:33 gwalter Rel $";
/*
* smtpd.c --- an example of TCP daemon for AmiTCP/IP
*
* $Log: smtpd.c,v $
* Revision 1.7 1994/02/06 10:07:33 gwalter
* Changed to use TCPLog from logger.lib
* Line buffer size increased
*
* Revision 1.6 1994/02/01 19:49:35 gwalter
* Parameters added, tidying
*
* Revision 1.5 1994/01/09 14:01:27 gwalter
* Timeouts, improved logging
*
* Revision 1.3 1993/12/30 16:38:18 gwalter
* Changed to display QUIT command from client.
*
* Revision 1.2 1993/12/21 09:44:14 gwalter
* Reference to pathnames.h removed
*
* Revision 1.1 1993/12/21 09:34:12 gwalter
* Initial revision
*
* Revision 1.1 1993/12/05 18:10:02 gwalter
* Initial revision
*
*
*/
#include "smtpd_rev.h"
char ApplicationID[] = "SMTPD";
const char version[] = VERSTAG;
char copyright[] =
"Copyright © 1993 Graham Walter, <amitcp@gwalter.demon.co.uk>\n"
"20 Kingstable St, Eton, Berks, England.\n";
#ifdef AMIGA
#if __SASC
#include <proto/socket.h>
#include <proto/dos.h>
#include <clib/exec_protos.h>
#include <pragmas/exec_sysbase_pragmas.h>
#elif __GNUC__
#include <inline/socket.h>
#include <inline/exec.h>
#else
#include <clib/socket_protos.h>
#endif
#endif /* AMIGA */
#include <errno.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/syslog.h>
#include <signal.h>
#include <dos/dos.h>
#include <exec/execbase.h>
#include <dos/var.h>
#include <dos/dostags.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <inetdlib.h>
#include <lineread.h>
#include "smtpd.h"
char * gethostname( char * host, long size );
extern struct ExecBase *SysBase;
BPTR Stdin = NULL;
BPTR Stdout = NULL;
BPTR Stderr = NULL;
char ** LocalHostAliases;
char * LocalHost;
char PeerName[MAX_HOSTLEN+1];
char * LogFileName;
char * RMailCommand = RMAIL_TEMPLATE;
int ErrorCount;
void
_STIdosStdio(void)
{
struct Process *p = (struct Process *)SysBase->ThisTask;
Stdin = p->pr_CIS;
Stdout = p->pr_COS;
Stderr = p->pr_CES ? p->pr_CES : Stdout;
}
/* Handle various mailer errors. */
static char * ApplicationErrorMsgs[] =
{
"" /*"Network fault"*/,
MAILERR_TEXT_BAD_RESPONSE,
MAILERR_TEXT_NOTEMP,
MAILERR_TEXT_INVALID_COMMAND,
MAILERR_TEXT_SEND_ERROR,
MAILERR_TEXT_NO_MEMORY,
MAILERR_TEXT_EXPECTING_HELO,
MAILERR_TEXT_TEMPWRITE,
MAILERR_TEXT_CONNECTION_LOST,
MAILERR_TEXT_SELECT,
MAILERR_TEXT_TIMEOUT,
MAILERR_TEXT_LOCALHOST,
MAILERR_TEXT_INVALID_ARGUMENTS,
MAILERR_TEXT_NO_DELIVERY,
MAILERR_TEXT_NOT_DELIVERED,
MAILERR_TEXT_COMMAND_REJECT
};
void
MailError(int Type, char * info )
{
char * errortext;
char syserrorbuffer[SHORT_BUFFER_LEN];
if( Type == 0 )
{
char * syserrortext;
if( errno < __sys_nerr)
syserrortext = (UBYTE *)__sys_errlist[errno];
else
syserrortext = (UBYTE *)__sys_errlist[0];
sprintf( syserrorbuffer, "%s %%s", syserrortext );
errortext = syserrorbuffer;
}
else
{
if( Type < sizeof( ApplicationErrorMsgs ) / sizeof( ApplicationErrorMsgs[0] ) )
errortext = ApplicationErrorMsgs[Type];
else
errortext = MAILERR_TEXT_UNKNOWN_ERROR;
}
TCPLog( LOG_ERR, errortext, (info) ? info: "" );
if( ++ErrorCount >= MAX_ERRORS )
{
TCPLog( LOG_CRIT, MAILERR_TEXT_ABORTING_MAXERRS );
exit( 20 );
}
}
/* Get the date, and format it for RFC822. */
char *
GetDate( void )
{
time_t clock;
clock = time(NULL);
return ctime(&clock);
}
char **
gethostent( void )
{
char ** nameblock;
char ** alias;
char local[STD_BUFFER_LEN];
struct hostent * hostent;
int i;
if( nameblock = calloc( MAX_ALIASES+2, sizeof( char * ) ) )
{
gethostname( local, sizeof( local ) );
if( hostent = gethostbyname( local ) )
{
nameblock[0] = hostent->h_name;
for( i = 1, alias = hostent->h_aliases; *alias; alias++ )
{
nameblock[i++] = strdup( *alias );
if( i > MAX_ALIASES )
break;
}
}
else
{
free( nameblock );
nameblock = NULL;
}
}
else
MailError( MAILERR_NO_MEMORY, NULL );
if( ! nameblock )
MailError( MAILERR_LOCALHOST, NULL );
return( nameblock );
}
int
StripCRLF( char * buffer )
{
int len;
len = strlen( buffer );
if( len && ( buffer[len-1] == '\n' ) )
buffer[--len] = '\0';
if( len && ( buffer[len-1] == '\r' ) )
buffer[--len] = '\0';
return( len );
}
/* Get a line from a socket */
int
GetLine( int s, char * buffer, struct LineRead * rl )
{
static int len = 0;
int nfds;
fd_set readfds;
fd_set excepfds;
static struct timeval timeout = { TIMEOUT, 0 };
for( ;; )
{
if( ! len )
{
FD_ZERO( &readfds );
FD_SET(s, &readfds );
FD_ZERO( &excepfds );
FD_SET(s, &excepfds );
if( (nfds = select(s + 1, &readfds, NULL, &excepfds, &timeout )) < 0)
{
MailError( MAILERR_SELECT, "GetLine" );
return( -1 );
}
if( ! nfds )
{
MailError( MAILERR_TIMEOUT, "GetLine" );
return( -1 );
}
}
if( ( len = lineRead( rl ) ) != 0 )
break;
if( ! rl->rl_Line )
{
return( -1 ); /* EOF */
}
}
if( len <= 0 )
{
MailError( 0, "GetLine" );
return( -1 );
}
strcpy( buffer, rl->rl_Line );
return( StripCRLF( buffer ) );
}
/* Simply send some text down the socket; check the write succeeds, but don't
check for any response. */
int
SendText(int s, char *Text)
{
if( send( s, Text, strlen(Text), 0 ) != strlen(Text) )
{
MailError( 0, "SendText" );
return( 0 );
}
return 1;
}
int
isCommand( char * buffer, char * Command )
{
int len = strlen( Command );
int Status = FALSE;
if( ( strnicmp( buffer, Command, len ) == 0 )
&& ( ( isspace( buffer[len] ) )
|| ( buffer[len] == '\0' ) ) )
{
Status = TRUE;
}
return( Status );
}
int StoreRecipient( char * to, char * from )
{
char * cp;
char * host;
char ** alias;
int length;
if( ! ( cp = strchr( from, ':' ) ) )
return( FALSE );
from = cp + 1;
if( *from == '<' )
{
if( ! ( cp = strchr( from, '>' ) ) )
return( FALSE );
from ++;
length = cp - from;
}
else
{
length = stcarg( from, " \r\n" );
}
memcpy( to, from, length );
to[length] = '\0';
if( strchr( to, '%' ) ) /* need to forward ? */
return( FALSE );
if( ! ( cp = strchr( to, '@' ) ) ) /* no host specified */
return( TRUE );
host = cp + 1;
length = strlen( host );
*cp = '\0';
for( alias = LocalHostAliases; *alias; alias++ )
{
if( stricmp( host, *alias ) == 0 ) /* this host */
return( TRUE );
if( ( strnicmp( host, *alias, length ) == 0 )
&& ( (*alias)[length] == '.' ) )
return( TRUE ); /* valid abbreviation */
}
return( FALSE );
}
int
LogMail( ULONG fh )
{
ULONG logfh;
static buffer[STD_BUFFER_LEN];
int len;
if( ! (logfh = Open( LogFileName, MODE_READWRITE ) ) )
{
TCPLog( LOG_WARNING, MAILERR_TEXT_LOG_OPEN_FAIL );
return( 1 );
}
Seek( logfh, 0, OFFSET_END );
Seek( fh, 0, OFFSET_BEGINNING );
while( len = Read( fh, buffer, sizeof( buffer ) ) )
{
Write( logfh, buffer, len );
}
Write( logfh, "\n", 1 );
Close( logfh );
return( 0 );
}
int
PassMailToRecipients( int s, struct LineRead * rl, char * From, ULONG rfh )
{
char * tfile;
ULONG tfh;
char * line;
char * reply;
char buffer[STD_BUFFER_LEN];
char Recipient[STD_BUFFER_LEN];
long len;
int SentCount;
int status = 0;
if( ! rfh )
{
SendText( s, SMTP_RESPONSE_NO_RECIPIENTS );
TCPLog( LOG_WARNING, MAILERR_TEXT_NO_RECIPIENTS );
return( 0 );
}
tfile = tmpnam( NULL );
if( ! ( tfh = Open( tfile, MODE_NEWFILE ) ) )
{
MailError( MAILERR_NOTEMP, "Data" );
return( 1 );
}
strcpy( buffer, GetDate() );
FPrintf( tfh, "From %s %s", From, buffer );
FPrintf( tfh, "Return-Path: <%s>\n", From );
FPrintf( tfh, "Received: ") ;
FPrintf( tfh, "from %s ", PeerName );
FPrintf( tfh, "by %s with SMTP\n\tid AA%05ld; %s",
LocalHost, 1, buffer );
if( ! SendText(s, SMTP_RESPONSE_RECEIVING ) )
{
Close( tfh );
return( 1 );
}
for(;;)
{
if( ( len = GetLine( s, buffer, rl ) ) < 0 )
{
MailError( 0, "Getting Mail Item Data" );
Close( tfh );
return( 1 );
}
line = buffer;
if( line[0] == '.' )
{
if( line[1] == '\0' )
{
break;
}
line++;
}
FPrintf( tfh, "%s\n", line );
}
Flush( tfh );
if( LogFileName )
LogMail( tfh );
Close( tfh );
Seek( rfh, 0, OFFSET_BEGINNING );
SentCount = 0;
while( status == 0
&& ( FGets( rfh, Recipient, sizeof( Recipient ) - 1 ) > 0 ) )
{
StripCRLF( Recipient );
sprintf( buffer, RMailCommand, tfile, Recipient );
if( SystemTags( buffer, TAG_DONE ) == 0 )
SentCount++;
else
MailError( MAILERR_NO_DELIVERY, Recipient );
}
if( SentCount )
{
DeleteFile( tfile );
reply = SMTP_RESPONSE_OK;
}
else
{
MailError( MAILERR_NOT_DELIVERED, tfile );
reply = SMTP_RESPONSE_NO_DELIVERY;
}
if( ! SendText(s, reply ) )
return( 1 );
return( 0 );
}
int ReadMailItem( int s, struct LineRead * rl )
{
char * From;
char * cp;
long len;
char * buffer;
char Recipient[STD_BUFFER_LEN] = "";
char rfname[L_tmpnam];
ULONG rfh = NULL;
int status = 0;
if( ! ( buffer = malloc( MAX_LINE_LENGTH ) ) )
{
return( 1 );
}
strcpy( buffer, rl->rl_Line + strlen( "MAIL FROM:" ) );
if( buffer[0] == '<' )
{
if( cp = strchr( buffer, '>' ) )
*cp = '\0';
cp = buffer + 1;
}
else cp = buffer;
StripCRLF( buffer );
From = strdup( cp );
if( ! SendText( s, SMTP_RESPONSE_OK ) )
{
free( buffer );
free( From );
return( 1 );
}
for(;;)
{
if( ( len = GetLine( s, buffer, rl ) ) < 0 )
{
status = 1;
break;
}
if( isCommand( buffer, SMTP_COMMAND_RCPT ) )
{
char * reply;
if( StoreRecipient( Recipient, buffer ) )
{
reply = SMTP_RESPONSE_OK;
if( ! rfh )
{
if( ! ( rfh = Open( tmpnam( rfname ), MODE_NEWFILE ) ) )
{
MailError( MAILERR_NOTEMP, "Recipients" );
status = 1;
break;
}
}
if( FPrintf( rfh, "%s\n", Recipient ) <= 0 )
{
MailError( MAILERR_TEMPWRITE, "Recipients (w)" );
reply = SMTP_RESPONSE_NOWRITE_RECIPIENT;
}
}
else
reply = SMTP_RESPONSE_WHOTHEHECK;
if( reply[0] != '2' )
MailError( MAILERR_COMMAND_REJECT, buffer );
if( ! SendText(s, reply ) )
{
status = 1;
break;
}
}
else if( isCommand( buffer, SMTP_COMMAND_RSET ) )
{
TCPLog( LOG_INFO, buffer );
if( ! SendText(s, SMTP_RESPONSE_OK ) )
{
status = 1;
break;
}
break;
}
else if( isCommand( buffer, SMTP_COMMAND_NOOP ) )
{
if( ! SendText(s, SMTP_RESPONSE_OK ) )
{
status = 1;
break;
}
}
else if( isCommand( buffer, SMTP_COMMAND_DATA ) )
{
if( ! rfh )
{
SendText( s, SMTP_RESPONSE_NO_RECIPIENTS );
TCPLog( LOG_WARNING, MAILERR_TEXT_NO_RECIPIENTS );
break;
}
Flush( rfh );
status = PassMailToRecipients( s, rl, From, rfh );
break;
}
else if( isCommand( buffer, SMTP_COMMAND_QUIT ) )
{
TCPLog( LOG_INFO, buffer );
sprintf( buffer, SMTP_RESPONSE_CLOSING, LocalHost );
SendText( s, buffer );
return( 1 );
}
else
{
MailError( MAILERR_INVALID_COMMAND, buffer );
status = 1;
break;
}
}
if( rfh )
{
Close( rfh );
DeleteFile( rfname );
}
free( buffer );
free( From );
return( status );
}
int
main(int argc, char **argv)
{
long len;
int s = server_socket;
#ifdef LOGGING
#include <netinet/in.h>
struct sockaddr_in sin;
int sval;
#endif
char buffer[STD_BUFFER_LEN];
struct LineRead * rl;
struct RDArgs *Args;
long ArgRes[ARGCOUNT];
register int i = 0;
for( i = 0; i < ARGCOUNT; ArgRes[i++] = NULL )
;
/* Look at command line arguments. */
#ifdef DEBUG
TCPLog( LOG_DEBUG, "starting, %ld arguments", argc );
if( argc )
TCPLog( LOG_DEBUG, "1st argument:%s", argv[0] );
#endif
if(!(Args = ReadArgs((UBYTE *)SMTPD_TEMPLATE, ArgRes, NULL)))
{
MailError( MAILERR_INVALID_ARGUMENTS, NULL );
return( 20 );
}
if( ArgRes[ARG_LOGFILE] )
{
LogFileName = strdup( (char*)(ArgRes[ARG_LOGFILE]) );
#ifdef DEBUG
TCPLog( LOG_DEBUG, "LogFile set to %s", LogFileName );
#endif
}
if( ArgRes[ARG_RMAIL] )
{
RMailCommand = strdup( (char*)(ArgRes[ARG_RMAIL]) );
#ifdef DEBUG
TCPLog( LOG_DEBUG, "RMailCommand set to %s", RMailCommand );
#endif
}
FreeArgs(Args);
if ( s == -1 )
{
#ifdef STANDALONE
struct sockaddr_in sin;
s = serveraccept( "smtp", &sin );
if ( s != -1 )
{
FPrintf( Stderr, "Accepted a connection from %s, port %ld\n",
inet_ntoa( sin.sin_addr ), sin.sin_port );
} else
#endif
return 1;
}
if( ! ( LocalHostAliases = gethostent( ) ) )
return( 1 );
LocalHost = LocalHostAliases[0];
#ifdef LOGGING /* unused for now */
sval = sizeof( sin );
if ( getpeername( 0, &sin, &sval ) < 0 )
fatal( "getpeername" );
#endif
if( ! ( rl = ( struct LineRead * )AllocMem( sizeof ( *rl ), 0 ) ) )
{
MailError( MAILERR_NO_MEMORY, NULL );
return( 1 );
}
initLineRead( rl, s, /*LF_REQLF*/ 1, RL_BUFSIZE );
sprintf( buffer, SMTP_RESPONSE_READY, LocalHost );
if( ! SendText( s, buffer ) )
return( 1 );
if( ( ( len = GetLine( s, buffer, rl ) ) <= strlen( SMTP_COMMAND_HELO ) + 1 )
|| ( ! isCommand( buffer, SMTP_COMMAND_HELO ) ) )
{
MailError( MAILERR_EXPECTING_HELO, buffer );
return( 1 );
}
TCPLog( LOG_INFO, buffer );
strcpy( PeerName, buffer + strlen( SMTP_COMMAND_HELO ) + 1 );
sprintf( buffer, SMTP_RESPONSE_OK_SUBS, LocalHost );
if( ! SendText( s, buffer ) )
return( 1 );
for(;;)
{
if( ( len = GetLine( s, buffer, rl ) ) < 0 )
{
MailError( MAILERR_CONNECTION_LOST, NULL );
return( 1 );
}
if( isCommand( buffer, SMTP_COMMAND_QUIT ) )
{
TCPLog( LOG_INFO, buffer );
sprintf( buffer, SMTP_RESPONSE_CLOSING, LocalHost );
if( ! SendText( s, buffer ) )
{
return( 1 );
}
break;
}
else if( isCommand( buffer, SMTP_COMMAND_NOOP ) )
{
if( ! SendText(s, SMTP_RESPONSE_OK ) )
{
return( 1 );
}
}
else if( isCommand( buffer, SMTP_COMMAND_RSET ) )
{
if( ! SendText(s, SMTP_RESPONSE_OK ) )
{
return( 1 );
}
}
else if( isCommand( buffer, SMTP_COMMAND_MAIL ) )
{
TCPLog( LOG_INFO, buffer );
if( ReadMailItem( s, rl ) )
return( 1 );
}
else
{
MailError( MAILERR_INVALID_COMMAND, buffer );
if( ! SendText(s, SMTP_RESPONSE_PARDON ) )
{
return( 1 );
}
}
}
#if 0
TCPLog( LOG_DEBUG, "Terminating normally" );
#endif
return 0;
}