patch to work in php-fpm jail
patch to work in php-fpm jail

/* mini_sendmail - accept email on behalf of real sendmail /* mini_sendmail - accept email on behalf of real sendmail
** **
** All rights reserved. ** All rights reserved.
** **
** Redistribution and use in source and binary forms, with or without ** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions ** modification, are permitted provided that the following conditions
** are met: ** are met:
** 1. Redistributions of source code must retain the above copyright ** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer. ** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright ** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the ** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution. ** documentation and/or other materials provided with the distribution.
** **
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE. ** SUCH DAMAGE.
*/ */
   
   
/* These defines control some optional features of the program. If you /* These defines control some optional features of the program. If you
** don't want the features you can undef the symbols; some of them mean ** don't want the features you can undef the symbols; some of them mean
** a significant savings in executable size. ** a significant savings in executable size.
*/ */
#define DO_RECEIVED /* whether to add a "Received:" header */ /* whether to add a "Received:" header */
  #undef DO_RECEIVED
#define DO_GETPWUID /* whether to try a getpwuid() if getlogin() fails */ #define DO_GETPWUID /* whether to try a getpwuid() if getlogin() fails */
#define DO_MINUS_SP /* whether to implement the -s and -p flags */ #define DO_MINUS_SP /* whether to implement the -s and -p flags */
#define DO_DNS /* whether to do a name lookup on -s, or just IP# */ #define DO_DNS /* whether to do a name lookup on -s, or just IP# */
   
   
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netdb.h> #include <netdb.h>
   
#ifdef DO_RECEIVED #ifdef DO_RECEIVED
#include <time.h> #include <time.h>
#endif /* DO_RECEIVED */ #endif /* DO_RECEIVED */
#ifdef DO_GETPWUID #ifdef DO_GETPWUID
#include <pwd.h> #include <pwd.h>
#endif /* DO_GETPWUID */ #endif /* DO_GETPWUID */
   
#include "version.h" #include "version.h"
   
   
/* Defines. */ /* Defines. */
#define SMTP_PORT 25 #define SMTP_PORT 25
#define DEFAULT_TIMEOUT 60 #define DEFAULT_TIMEOUT 60
   
   
/* Globals. */ /* Globals. */
static char* argv0; static char* argv0;
static char* fake_from; static char* fake_from;
static int parse_message, verbose; static int parse_message, verbose;
#ifdef DO_MINUS_SP #ifdef DO_MINUS_SP
static char* server; static char* server;
static short port; static short port;
#endif /* DO_MINUS_SP */ #endif /* DO_MINUS_SP */
static int timeout; static int timeout;
static int sockfd1, sockfd2; static int sockfd1, sockfd2;
static FILE* sockrfp; static FILE* sockrfp;
static FILE* sockwfp; static FILE* sockwfp;
static int got_a_recipient; static int got_a_recipient;
   
   
/* Forwards. */ /* Forwards. */
static void usage( void ); static void usage( void );
static char* slurp_message( void ); static char* slurp_message( void );
#ifdef DO_RECEIVED #ifdef DO_RECEIVED
static char* make_received( char* from, char* username, char* hostname ); static char* make_received( char* from, char* username, char* hostname );
#endif /* DO_RECEIVED */ #endif /* DO_RECEIVED */
static void parse_for_recipients( char* message ); static void parse_for_recipients( char* message );
static void add_recipient( char* recipient, int len ); static void add_recipient( char* recipient, int len );
static int open_client_socket( void ); static int open_client_socket( void );
static int read_response( void ); static int read_response( void );
static void send_command( char* command ); static void send_command( char* command );
static void send_data( char* data ); static void send_data( char* data );
static void send_done( void ); static void send_done( void );
static void sigcatch( int sig ); static void sigcatch( int sig );
static void show_error( char* cause ); static void show_error( char* cause );
   
   
int int
main( int argc, char** argv ) main( int argc, char** argv )
{ {
int argn; int argn;
char* message; char* message;
#ifdef DO_RECEIVED #ifdef DO_RECEIVED
char* received; char* received;
#endif /* DO_RECEIVED */ #endif /* DO_RECEIVED */
char* username; char* username;
char hostname[500]; char hostname[500];
char from[1000]; char from[1000];
int status; int status;
char buf[2000]; char buf[2000];
   
/* Parse args. */ /* Parse args. */
argv0 = argv[0]; argv0 = argv[0];
fake_from = (char*) 0; fake_from = (char*) 0;
parse_message = 0; parse_message = 0;
#ifdef DO_MINUS_SP #ifdef DO_MINUS_SP
server = "127.0.0.1"; server = "127.0.0.1";
port = SMTP_PORT; port = SMTP_PORT;
#endif /* DO_MINUS_SP */ #endif /* DO_MINUS_SP */
verbose = 0; verbose = 0;
timeout = DEFAULT_TIMEOUT; timeout = DEFAULT_TIMEOUT;
argn = 1; argn = 1;
while ( argn < argc && argv[argn][0] == '-' ) while ( argn < argc && argv[argn][0] == '-' )
{ {
if ( strncmp( argv[argn], "-f", 2 ) == 0 && argv[argn][2] != '\0' ) if ( strncmp( argv[argn], "-f", 2 ) == 0 && argv[argn][2] != '\0' )
fake_from = &(argv[argn][2]); fake_from = &(argv[argn][2]);
  else if ( strncmp( argv[argn], "-f", 2) == 0)
  fake_from = &(argv[++argn][0]);
else if ( strcmp( argv[argn], "-t" ) == 0 ) else if ( strcmp( argv[argn], "-t" ) == 0 )
parse_message = 1; parse_message = 1;
#ifdef DO_MINUS_SP #ifdef DO_MINUS_SP
else if ( strncmp( argv[argn], "-s", 2 ) == 0 && argv[argn][2] != '\0' ) else if ( strncmp( argv[argn], "-s", 2 ) == 0 && argv[argn][2] != '\0' )
server = &(argv[argn][2]); server = &(argv[argn][2]);
else if ( strncmp( argv[argn], "-p", 2 ) == 0 && argv[argn][2] != '\0' ) else if ( strncmp( argv[argn], "-p", 2 ) == 0 && argv[argn][2] != '\0' )
port = atoi( &(argv[argn][2]) ); port = atoi( &(argv[argn][2]) );
#endif /* DO_MINUS_SP */ #endif /* DO_MINUS_SP */
else if ( strncmp( argv[argn], "-T", 2 ) == 0 && argv[argn][2] != '\0' ) else if ( strncmp( argv[argn], "-T", 2 ) == 0 && argv[argn][2] != '\0' )
timeout = atoi( &(argv[argn][2]) ); timeout = atoi( &(argv[argn][2]) );
else if ( strcmp( argv[argn], "-v" ) == 0 ) else if ( strcmp( argv[argn], "-v" ) == 0 )
verbose = 1; verbose = 1;
else if ( strcmp( argv[argn], "-i" ) == 0 ) else if ( strcmp( argv[argn], "-i" ) == 0 )
; /* ignore */ ; /* ignore */
else if ( strcmp( argv[argn], "-oi" ) == 0 ) else if ( strcmp( argv[argn], "-oi" ) == 0 )
; /* ignore */ ; /* ignore */
else if ( strcmp( argv[argn], "--" ) == 0 ) else if ( strcmp( argv[argn], "--" ) == 0 )
; /* ignore */ ; /* ignore */
else else
usage(); usage();
++argn; ++argn;
} }
   
username = getlogin(); if ( fake_from == (char*) 0 ) {
if ( username == (char*) 0 ) username = getlogin();
{ if ( username == (char*) 0 ) {
#ifdef DO_GETPWUID #ifdef DO_GETPWUID
struct passwd* pw = getpwuid( getuid() ); struct passwd* pw = getpwuid( getuid() );
if ( pw == (struct passwd*) 0 ) if ( pw == (struct passwd*) 0 ) {
{ (void) fprintf( stderr, "%s: can't determine username\n", argv0 );
(void) fprintf( stderr, "%s: can't determine username\n", argv0 ); exit( 1 );
exit( 1 );  
} }
username = pw->pw_name; username = pw->pw_name;
#else /* DO_GETPWUID */ #else /* DO_GETPWUID */
(void) fprintf( stderr, "%s: can't determine username\n", argv0 ); (void) fprintf( stderr, "%s: can't determine username\n", argv0 );
exit( 1 ); exit( 1 );
#endif /* DO_GETPWUID */ #endif /* DO_GETPWUID */
} }
   
if ( gethostname( hostname, sizeof(hostname) - 1 ) < 0 )  
show_error( "gethostname" );  
   
if ( fake_from == (char*) 0 )  
(void) snprintf( from, sizeof(from), "%s@%s", username, hostname ); (void) snprintf( from, sizeof(from), "%s@%s", username, hostname );
else } else {
if ( strchr( fake_from, '@' ) == (char*) 0 ) if ( strchr( fake_from, '@' ) == (char*) 0 )
(void) snprintf( from, sizeof(from), "%s@%s", fake_from, hostname ); (void) snprintf( from, sizeof(from), "%s@%s", fake_from, hostname );
else else
(void) snprintf( from, sizeof(from), "%s", fake_from ); (void) snprintf( from, sizeof(from), "%s", fake_from );
  }
   
  if ( gethostname( hostname, sizeof(hostname) - 1 ) < 0 )
  show_error( "gethostname" );
   
   
/* Strip off any angle brackets in the from address. */ /* Strip off any angle brackets in the from address. */
while ( from[0] == '<' ) while ( from[0] == '<' )
(void) strcpy( from, &from[1] ); (void) strcpy( from, &from[1] );
while ( from[strlen(from)-1] == '>' ) while ( from[strlen(from)-1] == '>' )
from[strlen(from)-1] = '\0'; from[strlen(from)-1] = '\0';
   
message = slurp_message(); message = slurp_message();
#ifdef DO_RECEIVED #ifdef DO_RECEIVED
received = make_received( from, username, hostname ); received = make_received( from, username, hostname );
#endif /* DO_RECEIVED */ #endif /* DO_RECEIVED */
   
(void) signal( SIGALRM, sigcatch ); (void) signal( SIGALRM, sigcatch );
   
(void) alarm( timeout ); (void) alarm( timeout );
sockfd1 = open_client_socket(); sockfd1 = open_client_socket();
   
sockfd2 = dup( sockfd1 ); sockfd2 = dup( sockfd1 );
sockrfp = fdopen( sockfd1, "r" ); sockrfp = fdopen( sockfd1, "r" );
sockwfp = fdopen( sockfd2, "w" ); sockwfp = fdopen( sockfd2, "w" );
   
/* The full SMTP protocol is spelled out in RFC821, available at /* The full SMTP protocol is spelled out in RFC821, available at
** http://www.faqs.org/rfcs/rfc821.html ** http://www.faqs.org/rfcs/rfc821.html
** The only non-obvious wrinkles: ** The only non-obvious wrinkles:
** - The commands are terminated with CRLF, not newline. ** - The commands are terminated with CRLF, not newline.
** - Newlines in the data file get turned into CRLFs. ** - Newlines in the data file get turned into CRLFs.
** - Any data lines beginning with a period get an extra period prepended. ** - Any data lines beginning with a period get an extra period prepended.
*/ */
   
status = read_response(); status = read_response();
if ( status != 220 ) if ( status != 220 )
{ {
(void) fprintf( (void) fprintf(
stderr, "%s: unexpected initial greeting %d\n", argv0, status ); stderr, "%s: unexpected initial greeting %d\n", argv0, status );
exit( 1 ); exit( 1 );
} }
   
(void) snprintf( buf, sizeof(buf), "HELO %s", hostname ); (void) snprintf( buf, sizeof(buf), "HELO %s", hostname );
send_command( buf ); send_command( buf );
status = read_response(); status = read_response();
if ( status != 250 ) if ( status != 250 )
{ {
(void) fprintf( (void) fprintf(
stderr, "%s: unexpected response %d to HELO command\n", stderr, "%s: unexpected response %d to HELO command\n",
argv0, status ); argv0, status );
exit( 1 ); exit( 1 );
} }
   
(void) snprintf( buf, sizeof(buf), "MAIL FROM:<%s>", from ); (void) snprintf( buf, sizeof(buf), "MAIL FROM:<%s>", from );
send_command( buf ); send_command( buf );
status = read_response(); status = read_response();
if ( status != 250 ) if ( status != 250 )
{ {
(void) fprintf( (void) fprintf(
stderr, "%s: unexpected response %d to MAIL FROM command\n", stderr, "%s: unexpected response %d to MAIL FROM command\n",
argv0, status ); argv0, status );
exit( 1 ); exit( 1 );
} }
   
got_a_recipient = 0; got_a_recipient = 0;
for ( ; argn < argc; ++argn ) for ( ; argn < argc; ++argn )
add_recipient( argv[argn], strlen( argv[argn] ) ); add_recipient( argv[argn], strlen( argv[argn] ) );
if ( parse_message ) if ( parse_message )
parse_for_recipients( message ); parse_for_recipients( message );
if ( ! got_a_recipient ) if ( ! got_a_recipient )
{ {
(void) fprintf( stderr, "%s: no recipients found\n", argv0 ); (void) fprintf( stderr, "%s: no recipients found\n", argv0 );
exit( 1 ); exit( 1 );
} }
   
send_command( "DATA" ); send_command( "DATA" );
status = read_response(); status = read_response();
if ( status != 354 ) if ( status != 354 )
{ {
(void) fprintf( (void) fprintf(
stderr, "%s: unexpected response %d to DATA command\n", stderr, "%s: unexpected response %d to DATA command\n",
argv0, status ); argv0, status );
exit( 1 ); exit( 1 );
} }
   
#ifdef DO_RECEIVED #ifdef DO_RECEIVED
send_data( received ); send_data( received );
#endif /* DO_RECEIVED */ #endif /* DO_RECEIVED */
send_data( message ); send_data( message );
send_done(); send_done();
status = read_response(); status = read_response();
if ( status != 250 ) if ( status != 250 )
{ {
(void) fprintf( (void) fprintf(
stderr, "%s: unexpected response %d to DATA\n", argv0, status ); stderr, "%s: unexpected response %d to DATA\n", argv0, status );
exit( 1 ); exit( 1 );
} }
   
send_command( "QUIT" ); send_command( "QUIT" );
status = read_response(); status = read_response();
if ( status != 221 ) if ( status != 221 )
(void) fprintf( (void) fprintf(
stderr, "%s: unexpected response %d to QUIT command - ignored\n", stderr, "%s: unexpected response %d to QUIT command - ignored\n",
argv0, status ); argv0, status );
   
(void) close( sockfd1 ); (void) close( sockfd1 );
(void) close( sockfd2 ); (void) close( sockfd2 );
   
exit( 0 ); exit( 0 );
} }
   
   
static void static void
usage( void ) usage( void )
{ {
#ifdef DO_MINUS_SP #ifdef DO_MINUS_SP
#ifdef DO_DNS #ifdef DO_DNS
char* spflag = "[-s<server>] [-p<port>] "; char* spflag = "[-s<server>] [-p<port>] ";
#else /* DO_DNS */ #else /* DO_DNS */
char* spflag = "[-s<server_ip>] [-p<port>] "; char* spflag = "[-s<server_ip>] [-p<port>] ";
#endif /* DO_DNS */ #endif /* DO_DNS */
#else /* DO_MINUS_SP */ #else /* DO_MINUS_SP */
char* spflag = ""; char* spflag = "";
#endif /* DO_MINUS_SP */ #endif /* DO_MINUS_SP */
(void) fprintf( stderr, "usage: %s [-f<name>] [-t] %s[-T<timeout>] [-v] [address ...]\n", argv0, spflag ); (void) fprintf( stderr, "usage: %s [-f<name>] [-t] %s[-T<timeout>] [-v] [address ...]\n", argv0, spflag );
exit( 1 ); exit( 1 );
} }
   
   
static char* static char*
slurp_message( void ) slurp_message( void )
{ {
char* message; char* message;
int message_size, message_len; int message_size, message_len;
int c; int c;
   
message_size = 5000; message_size = 5000;
message = (char*) malloc( message_size ); message = (char*) malloc( message_size );
if ( message == (char*) 0 ) if ( message == (char*) 0 )
{ {
(void) fprintf( stderr, "%s: out of memory\n", argv0 ); (void) fprintf( stderr, "%s: out of memory\n", argv0 );
exit( 1 ); exit( 1 );
} }
message_len = 0; message_len = 0;
   
for (;;) for (;;)
{ {
c = getchar(); c = getchar();
if ( c == EOF ) if ( c == EOF )
break; break;
if ( message_len + 1 >= message_size ) if ( message_len + 1 >= message_size )
{ {
message_size *= 2; message_size *= 2;
message = (char*) realloc( (void*) message, message_size ); message = (char*) realloc( (void*) message, message_size );
if ( message == (char*) 0 ) if ( message == (char*) 0 )
{ {
(void) fprintf( stderr, "%s: out of memory\n", argv0 ); (void) fprintf( stderr, "%s: out of memory\n", argv0 );
exit( 1 ); exit( 1 );
} }
} }
message[message_len++] = c; message[message_len++] = c;
} }
message[message_len] = '\0'; message[message_len] = '\0';
   
return message; return message;
} }
   
   
#ifdef DO_RECEIVED #ifdef DO_RECEIVED
static char* static char*
make_received( char* from, char* username, char* hostname ) make_received( char* from, char* username, char* hostname )
{ {
int received_size; int received_size;
char* received; char* received;
time_t t; time_t t;
struct tm* tmP; struct tm* tmP;
char timestamp[100]; char timestamp[100];
   
t = time( (time_t*) 0 ); t = time( (time_t*) 0 );
tmP = localtime( &t ); tmP = localtime( &t );
(void) strftime( timestamp, sizeof(timestamp), "%a, %d %b %Y %T %Z", tmP ); (void) strftime( timestamp, sizeof(timestamp), "%a, %d %b %Y %T %Z", tmP );
received_size = received_size =
500 + strlen( from ) + strlen( hostname ) * 2 + strlen( VERSION ) + 500 + strlen( from ) + strlen( hostname ) * 2 + strlen( VERSION ) +
strlen( timestamp ) + strlen( username ); strlen( timestamp ) + strlen( username );
received = (char*) malloc( received_size ); received = (char*) malloc( received_size );
if ( received == (char*) 0 ) if ( received == (char*) 0 )
{ {
(void) fprintf( stderr, "%s: out of memory\n", argv0 ); (void) fprintf( stderr, "%s: out of memory\n", argv0 );
exit( 1 ); exit( 1 );
} }
(void) snprintf( (void) snprintf(
received, received_size, received, received_size,
"Received: (from %s)\n\tby %s (%s);\n\t%s\n\t(sender %s@%s)\n", "Received: (from %s)\n\tby %s (%s);\n\t%s\n\t(sender %s@%s)\n",
from, hostname, VERSION, timestamp, username, hostname ); from, hostname, VERSION, timestamp, username, hostname );
return received; return received;
} }
#endif /* DO_RECEIVED */ #endif /* DO_RECEIVED */
   
   
static void static void
parse_for_recipients( char* message ) parse_for_recipients( char* message )
{ {
/* A little finite-state machine that parses the message, looking /* A little finite-state machine that parses the message, looking
** for To:, Cc:, and Bcc: recipients. ** for To:, Cc:, and Bcc: recipients.
*/ */
int state; int state;
#define ST_BOL 0 #define ST_BOL 0
#define ST_OTHERHEAD 1 #define ST_OTHERHEAD 1
#define ST_T 2 #define ST_T 2
#define ST_C 3 #define ST_C 3
#define ST_B 4 #define ST_B 4
#define ST_BC 5 #define ST_BC 5
#define ST_RECIPHEAD 6 #define ST_RECIPHEAD 6
#define ST_RECIPS 7 #define ST_RECIPS 7
char* cp; char* cp;
char* bcc; char* bcc;
char* recip; char* recip;
   
state = ST_BOL; state = ST_BOL;
bcc = (char*) 0; bcc = (char*) 0;
for ( cp = message; *cp != '\0'; ++cp ) for ( cp = message; *cp != '\0'; ++cp )
{ {
switch ( state ) switch ( state )
{ {
case ST_BOL: case ST_BOL:
switch ( *cp ) switch ( *cp )
{ {
case '\n': case '\n':
return; return;
case 'T': case 'T':
case 't': case 't':
state = ST_T; state = ST_T;
break; break;
case 'C': case 'C':
case 'c': case 'c':
state = ST_C; state = ST_C;
break; break;
case 'B': case 'B':
case 'b': case 'b':
state = ST_B; state = ST_B;
bcc = cp; bcc = cp;
break; break;
default: default:
state = ST_OTHERHEAD; state = ST_OTHERHEAD;
break; break;
} }
break; break;
case ST_OTHERHEAD: case ST_OTHERHEAD:
switch ( *cp ) switch ( *cp )
{ {
case '\n': case '\n':
state = ST_BOL; state = ST_BOL;
break; break;
} }
break; break;
case ST_T: case ST_T:
switch ( *cp ) switch ( *cp )
{ {
case '\n': case '\n':
state = ST_BOL; state = ST_BOL;
break; break;
case 'O': case 'O':
case 'o': case 'o':
state = ST_RECIPHEAD; state = ST_RECIPHEAD;
break; break;
default: default:
state = ST_OTHERHEAD; state = ST_OTHERHEAD;
break; break;
} }
break; break;
case ST_C: case ST_C:
switch ( *cp ) switch ( *cp )
{ {
case '\n': case '\n':
state = ST_BOL; state = ST_BOL;
break; break;
case 'C': case 'C':
case 'c': case 'c':
state = ST_RECIPHEAD; state = ST_RECIPHEAD;
break; break;
default: default:
state = ST_OTHERHEAD; state = ST_OTHERHEAD;
break; break;
} }
break; break;
case ST_B: case ST_B:
switch ( *cp ) switch ( *cp )
{ {
case '\n': case '\n':
state = ST_BOL; state = ST_BOL;
bcc = (char*) 0; bcc = (char*) 0;
break; break;
case 'C': case 'C':
case 'c': case 'c':
state = ST_BC; state = ST_BC;
break; break;
default: default:
state = ST_OTHERHEAD; state = ST_OTHERHEAD;
bcc = (char*) 0; bcc = (char*) 0;
break; break;
} }
break; break;
case ST_BC: case ST_BC:
switch ( *cp ) switch ( *cp )
{ {
case '\n': case '\n':
state = ST_BOL; state = ST_BOL;
bcc = (char*) 0; bcc = (char*) 0;
break; break;
case 'C': case 'C':
case 'c': case 'c':
state = ST_RECIPHEAD; state = ST_RECIPHEAD;
break; break;
default: default:
state = ST_OTHERHEAD; state = ST_OTHERHEAD;
bcc = (char*) 0; bcc = (char*) 0;
break; break;
} }
break; break;
case ST_RECIPHEAD: case ST_RECIPHEAD:
switch ( *cp ) switch ( *cp )
{ {
case '\n': case '\n':
state = ST_BOL; state = ST_BOL;
bcc = (char*) 0; bcc = (char*) 0;
break; break;
case ':': case ':':
state = ST_RECIPS; state = ST_RECIPS;
recip = cp + 1; recip = cp + 1;
break; break;
default: default:
state = ST_OTHERHEAD; state = ST_OTHERHEAD;
bcc = (char*) 0; bcc = (char*) 0;
break; break;
} }
break; break;
case ST_RECIPS: case ST_RECIPS:
switch ( *cp ) switch ( *cp )
{ {
case '\n': case '\n':
add_recipient( recip, ( cp - recip ) ); add_recipient( recip, ( cp - recip ) );
state = ST_BOL; state = ST_BOL;
if ( bcc != (char*) 0 ) if ( bcc != (char*) 0 )
{ {
/* Elide the Bcc: line, and reset cp. */ /* Elide the Bcc: line, and reset cp. */
(void) strcpy( bcc, cp + 1 ); (void) strcpy( bcc, cp + 1 );
cp = bcc - 1; cp = bcc - 1;
bcc = (char*) 0; bcc = (char*) 0;
} }
break; break;
case ',': case ',':
add_recipient( recip, ( cp - recip ) ); add_recipient( recip, ( cp - recip ) );
recip = cp + 1; recip = cp + 1;
break; break;
} }
break; break;
} }
} }
} }
   
   
static void static void
add_recipient( char* recipient, int len ) add_recipient( char* recipient, int len )
{ {
char buf[1000]; char buf[1000];
int status; int status;
   
/* Skip leading whitespace. */ /* Skip leading whitespace. */
while ( len > 0 && ( *recipient == ' ' || *recipient == '\t' ) ) while ( len > 0 && ( *recipient == ' ' || *recipient == '\t' ) )
{ {
++recipient; ++recipient;
--len; --len;
} }
   
/* Strip off any angle brackets. */ /* Strip off any angle brackets. */
while ( len > 0 && *recipient == '<' ) while ( len > 0 && *recipient == '<' )
{ {
++recipient; ++recipient;
--len; --len;
} }
while ( len > 0 && recipient[len-1] == '>' ) while ( len > 0 && recipient[len-1] == '>' )
--len; --len;
   
(void) snprintf( buf, sizeof(buf), "RCPT TO:<%.*s>", len, recipient ); (void) snprintf( buf, sizeof(buf), "RCPT TO:<%.*s>", len, recipient );
send_command( buf ); send_command( buf );
status = read_response(); status = read_response();
if ( status != 250 && status != 251 ) if ( status != 250 && status != 251 )
{ {
(void) fprintf( (void) fprintf(
stderr, "%s: unexpected response %d to RCPT TO command\n", stderr, "%s: unexpected response %d to RCPT TO command\n",
argv0, status ); argv0, status );
exit( 1 ); exit( 1 );
} }
got_a_recipient = 1; got_a_recipient = 1;
} }
   
   
#if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED) #if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED)
#define USE_IPV6 #define USE_IPV6
#endif #endif
   
static int static int
open_client_socket( void ) open_client_socket( void )
{ {
#ifdef USE_IPV6 #ifdef USE_IPV6
struct sockaddr_in6 sa; struct sockaddr_in6 sa;
#else /* USE_IPV6 */ #else /* USE_IPV6 */
struct sockaddr_in sa; struct sockaddr_in sa;
#endif /* USE_IPV6 */ #endif /* USE_IPV6 */
int sa_len, sock_family, sock_type, sock_protocol; int sa_len, sock_family, sock_type, sock_protocol;
int sockfd; int sockfd;
   
sock_type = SOCK_STREAM; sock_type = SOCK_STREAM;
sock_protocol = 0; sock_protocol = 0;
sa_len = sizeof(sa); sa_len = sizeof(sa);
(void) memset( (void*) &sa, 0, sa_len ); (void) memset( (void*) &sa, 0, sa_len );
   
#ifdef USE_IPV6 #ifdef USE_IPV6
   
{ {
#ifdef DO_MINUS_SP #ifdef DO_MINUS_SP
struct sockaddr_in sa4; struct sockaddr_in sa4;
struct addrinfo hints; struct addrinfo hints;
char portstr[10]; char portstr[10];
int gaierr; int gaierr;
struct addrinfo* ai; struct addrinfo* ai;
struct addrinfo* ai2; struct addrinfo* ai2;
struct addrinfo* aiv4; struct addrinfo* aiv4;
struct addrinfo* aiv6; struct addrinfo* aiv6;
#endif /* DO_MINUS_SP */ #endif /* DO_MINUS_SP */
   
sock_family = PF_INET6; sock_family = PF_INET6;
   
#ifdef DO_MINUS_SP #ifdef DO_MINUS_SP
(void) memset( (void*) &sa4, 0, sizeof(sa4) ); (void) memset( (void*) &sa4, 0, sizeof(sa4) );
if ( inet_pton( AF_INET, server, (void*) &sa4.sin_addr ) == 1 ) if ( inet_pton( AF_INET, server, (void*) &sa4.sin_addr ) == 1 )
{ {
sock_family = PF_INET; sock_family = PF_INET;
sa4.sin_port = htons( port ); sa4.sin_port = htons( port );
sa_len = sizeof(sa4); sa_len = sizeof(sa4);
(void) memmove( &sa, &sa4, sa_len ); (void) memmove( &sa, &sa4, sa_len );
} }
else if ( inet_pton( AF_INET6, server, (void*) &sa.sin6_addr ) != 1 ) else if ( inet_pton( AF_INET6, server, (void*) &sa.sin6_addr ) != 1 )
{ {
#ifdef DO_DNS #ifdef DO_DNS
(void) memset( &hints, 0, sizeof(hints) ); (void) memset( &hints, 0, sizeof(hints) );
hints.ai_family = PF_UNSPEC; hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
(void) snprintf( portstr, sizeof(portstr), "%d", port ); (void) snprintf( portstr, sizeof(portstr), "%d", port );
if ( (gaierr = getaddrinfo( server, portstr, &hints, &ai )) != 0 ) if ( (gaierr = getaddrinfo( server, portstr, &hints, &ai )) != 0 )
{ {
(void) fprintf( (void) fprintf(
stderr, "%s: getaddrinfo %s - %s\n", argv0, server, stderr, "%s: getaddrinfo %s - %s\n", argv0, server,
gai_strerror( gaierr ) ); gai_strerror( gaierr ) );
exit( 1 ); exit( 1 );
} }
   
/* Find the first IPv4 and IPv6 entries. */ /* Find the first IPv4 and IPv6 entries. */
aiv4 = (struct addrinfo*) 0; aiv4 = (struct addrinfo*) 0;
aiv6 = (struct addrinfo*) 0; aiv6 = (struct addrinfo*) 0;
for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next ) for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
{ {
switch ( ai2->ai_family ) switch ( ai2->ai_family )
{ {
case PF_INET: case PF_INET:
if ( aiv4 == (struct addrinfo*) 0 ) if ( aiv4 == (struct addrinfo*) 0 )
aiv4 = ai2; aiv4 = ai2;
break; break;
case PF_INET6: case PF_INET6:
if ( aiv6 == (struct addrinfo*) 0 ) if ( aiv6 == (struct addrinfo*) 0 )
aiv6 = ai2; aiv6 = ai2;
break; break;
} }
} }
   
/* If there's an IPv4 address, use that, otherwise try IPv6. */ /* If there's an IPv4 address, use that, otherwise try IPv6. */
if ( aiv4 != (struct addrinfo*) 0 ) if ( aiv4 != (struct addrinfo*) 0 )
{ {
if ( sizeof(sa) < aiv4->ai_addrlen ) if ( sizeof(sa) < aiv4->ai_addrlen )
{ {
(void) fprintf( (void) fprintf(
stderr, "%s - sockaddr too small (%lu < %lu)\n", stderr, "%s - sockaddr too small (%lu < %lu)\n",
server, (unsigned long) sizeof(sa), server, (unsigned long) sizeof(sa),
(unsigned long) aiv4->ai_addrlen ); (unsigned long) aiv4->ai_addrlen );
exit( 1 ); exit( 1 );
} }
sock_family = aiv4->ai_family; sock_family = aiv4->ai_family;
sock_type = aiv4->ai_socktype; sock_type = aiv4->ai_socktype;
sock_protocol = aiv4->ai_protocol; sock_protocol = aiv4->ai_protocol;
sa_len = aiv4->ai_addrlen; sa_len = aiv4->ai_addrlen;
(void) memmove( &sa, aiv4->ai_addr, sa_len ); (void) memmove( &sa, aiv4->ai_addr, sa_len );
goto ok; goto ok;
} }
if ( aiv6 != (struct addrinfo*) 0 ) if ( aiv6 != (struct addrinfo*) 0 )
{ {
if ( sizeof(sa) < aiv6->ai_addrlen ) if ( sizeof(sa) < aiv6->ai_addrlen )
{ {
(void) fprintf( (void) fprintf(
stderr, "%s - sockaddr too small (%lu < %lu)\n", stderr, "%s - sockaddr too small (%lu < %lu)\n",
server, (unsigned long) sizeof(sa), server, (unsigned long) sizeof(sa),
(unsigned long) aiv6->ai_addrlen ); (unsigned long) aiv6->ai_addrlen );
exit( 1 ); exit( 1 );
} }
sock_family = aiv6->ai_family; sock_family = aiv6->ai_family;
sock_type = aiv6->ai_socktype; sock_type = aiv6->ai_socktype;
sock_protocol = aiv6->ai_protocol; sock_protocol = aiv6->ai_protocol;
sa_len = aiv6->ai_addrlen; sa_len = aiv6->ai_addrlen;
(void) memmove( &sa, aiv6->ai_addr, sa_len ); (void) memmove( &sa, aiv6->ai_addr, sa_len );
goto ok; goto ok;
} }
   
(void) fprintf( (void) fprintf(
stderr, "%s: no valid address found for host %s\n", argv0, server ); stderr, "%s: no valid address found for host %s\n", argv0, server );
exit( 1 ); exit( 1 );
   
ok: ok:
freeaddrinfo( ai ); freeaddrinfo( ai );
#else /* DO_DNS */ #else /* DO_DNS */
(void) fprintf( (void) fprintf(
stderr, "%s: bad server IP address %s\n", argv0, server ); stderr, "%s: bad server IP address %s\n", argv0, server );
exit( 1 ); exit( 1 );
#endif /* DO_DNS */ #endif /* DO_DNS */
} }
#else /* DO_MINUS_SP */ #else /* DO_MINUS_SP */
sa.sin6_addr = in6addr_any; sa.sin6_addr = in6addr_any;
sa.sin6_port = htons( SMTP_PORT ); sa.sin6_port = htons( SMTP_PORT );
#endif /* DO_MINUS_SP */ #endif /* DO_MINUS_SP */
   
sa.sin6_family = sock_family; sa.sin6_family = sock_family;
   
} }
   
#else /* USE_IPV6 */ #else /* USE_IPV6 */
   
{ {
#ifdef DO_MINUS_SP #ifdef DO_MINUS_SP
struct hostent *he; struct hostent *he;
#else /* DO_MINUS_SP */ #else /* DO_MINUS_SP */
char local_addr[4] = { 127, 0, 0, 1 }; char local_addr[4] = { 127, 0, 0, 1 };
#endif /* DO_MINUS_SP */ #endif /* DO_MINUS_SP */
   
sock_family = PF_INET; sock_family = PF_INET;
   
#ifdef DO_MINUS_SP #ifdef DO_MINUS_SP
sa.sin_addr.s_addr = inet_addr( server ); sa.sin_addr.s_addr = inet_addr( server );
sa.sin_port = htons( port ); sa.sin_port = htons( port );
if ( (int32_t) sa.sin_addr.s_addr == -1 ) if ( (int32_t) sa.sin_addr.s_addr == -1 )
{ {
#ifdef DO_DNS #ifdef DO_DNS
he = gethostbyname( server ); he = gethostbyname( server );
if ( he == (struct hostent*) 0 ) if ( he == (struct hostent*) 0 )
{ {
(void) fprintf( (void) fprintf(
stderr, "%s: server name lookup of '%s' failed - %s\n", stderr, "%s: server name lookup of '%s' failed - %s\n",
argv0, server, hstrerror( h_errno ) ); argv0, server, hstrerror( h_errno ) );
exit( 1 ); exit( 1 );
} }
sock_family = he->h_addrtype; sock_family = he->h_addrtype;
(void) memmove( &sa.sin_addr, he->h_addr, he->h_length ); (void) memmove( &sa.sin_addr, he->h_addr, he->h_length );
#else /* DO_DNS */ #else /* DO_DNS */
(void) fprintf( (void) fprintf(
stderr, "%s: bad server IP address %s\n", argv0, server ); stderr, "%s: bad server IP address %s\n", argv0, server );
exit( 1 ); exit( 1 );
#endif /* DO_DNS */ #endif /* DO_DNS */
} }
#else /* DO_MINUS_SP */ #else /* DO_MINUS_SP */
(void) memmove( &sa.sin_addr, local_addr, sizeof(local_addr) ); (void) memmove( &sa.sin_addr, local_addr, sizeof(local_addr) );
sa.sin_port = htons( SMTP_PORT ); sa.sin_port = htons( SMTP_PORT );
#endif /* DO_MINUS_SP */ #endif /* DO_MINUS_SP */
   
sa.sin_family = sock_family; sa.sin_family = sock_family;
} }
   
#endif /* USE_IPV6 */ #endif /* USE_IPV6 */
   
sockfd = socket( sock_family, sock_type, sock_protocol ); sockfd = socket( sock_family, sock_type, sock_protocol );
if ( sockfd < 0 ) if ( sockfd < 0 )
show_error( "socket" ); show_error( "socket" );
   
if ( connect( sockfd, (struct sockaddr*) &sa, sa_len ) < 0 ) if ( connect( sockfd, (struct sockaddr*) &sa, sa_len ) < 0 )
show_error( "connect" ); show_error( "connect" );
   
return sockfd; return sockfd;
} }
   
   
static int static int
read_response( void ) read_response( void )
{ {
char buf[10000]; char buf[10000];
char* cp; char* cp;
int status; int status;
   
for (;;) for (;;)
{ {
(void) alarm( timeout ); (void) alarm( timeout );
if ( fgets( buf, sizeof(buf), sockrfp ) == (char*) 0 ) if ( fgets( buf, sizeof(buf), sockrfp ) == (char*) 0 )
{ {
(void) fprintf( stderr, "%s: unexpected EOF\n", argv0 ); (void) fprintf( stderr, "%s: unexpected EOF\n", argv0 );
exit( 1 ); exit( 1 );
} }
if ( verbose ) if ( verbose )
(void) fprintf( stderr, "<<<< %s", buf ); (void) fprintf( stderr, "<<<< %s", buf );
for ( status = 0, cp = buf; *cp >= '0' && *cp <= '9'; ++cp ) for ( status = 0, cp = buf; *cp >= '0' && *cp <= '9'; ++cp )
status = 10 * status + ( *cp - '0' ); status = 10 * status + ( *cp - '0' );
if ( *cp == ' ' ) if ( *cp == ' ' )
break; break;
if ( *cp != '-' ) if ( *cp != '-' )
{ {
(void) fprintf( (void) fprintf(
stderr, "%s: bogus reply syntax - '%s'\n", argv0, buf ); stderr, "%s: bogus reply syntax - '%s'\n", argv0, buf );
exit( 1 ); exit( 1 );
} }
} }
return status; return status;
} }
   
   
static void static void
send_command( char* command ) send_command( char* command )
{ {
(void) alarm( timeout ); (void) alarm( timeout );
if ( verbose ) if ( verbose )
(void) fprintf( stderr, ">>>> %s\n", command ); (void) fprintf( stderr, ">>>> %s\n", command );
(void) fprintf( sockwfp, "%s\r\n", command ); (void) fprintf( sockwfp, "%s\r\n", command );
(void) fflush( sockwfp ); (void) fflush( sockwfp );
} }
   
   
static void static void
send_data( char* data ) send_data( char* data )
{ {
int bol; int bol;
   
for ( bol = 1; *data != '\0'; ++data ) for ( bol = 1; *data != '\0'; ++data )
{ {
if ( bol && *data == '.' ) if ( bol && *data == '.' )
putc( '.', sockwfp ); putc( '.', sockwfp );
bol = 0; bol = 0;
if ( *data == '\n' ) if ( *data == '\n' )
{ {
putc( '\r', sockwfp ); putc( '\r', sockwfp );
bol = 1; bol = 1;
} }
putc( *data, sockwfp ); putc( *data, sockwfp );
} }
if ( ! bol ) if ( ! bol )
(void) fputs( "\r\n", sockwfp ); (void) fputs( "\r\n", sockwfp );
} }
   
   
static void static void
send_done( void ) send_done( void )
{ {
(void) fputs( ".\r\n", sockwfp ); (void) fputs( ".\r\n", sockwfp );
(void) fflush( sockwfp ); (void) fflush( sockwfp );
} }
   
   
static void static void
sigcatch( int sig ) sigcatch( int sig )
{ {
(void) fprintf( stderr, "%s: timed out\n", argv0 ); (void) fprintf( stderr, "%s: timed out\n", argv0 );
exit( 1 ); exit( 1 );
} }
   
   
static void static void
show_error( char* cause ) show_error( char* cause )
{ {
char buf[5000]; char buf[5000];
   
(void) snprintf( buf, sizeof(buf), "%s: %s", argv0, cause ); (void) snprintf( buf, sizeof(buf), "%s: %s", argv0, cause );
perror( buf ); perror( buf );
exit( 1 ); exit( 1 );
} }
   
comments