SMTProbe & SMTPRedirectToRelay

Try another host for delivery instead of bouncing if blocked by a DNSBL

 

  Introduction

Spammers become more and more aggressive and the operators of DNSBLs follow-up, and there seems to be an increasing tendency for black listing complete IP blocks instead of single addresses.

For this reason also regular mail services are collaterally damaged by all sorts of DNSBL's which list whole address blocks instead of single IP's.

Unfortunately, the operators of DNSBL's become increasingly incooperative in reducing this kind of collateral damage. As an operator of a small to medium sized mail service you have no chance to become delisted from SPEWS, Spamhaus, SORBS and MAPS - they don't even want to know about you having a problem because of their listing, and if you have the luck to receive a response, then they ask you to either get your ISP to resolve the problem or otherwise to change the ISP.

This situation might catch every small to medium mail service, and it is even more likely if it resides outside of western europe or the U.S.

See discussion at list.postfix.users:
if blocked by a DNSBL then try another host for delivery instead of bouncing

 

  Help yourself...

  • Here comes a set of two small C command line tools that assist in helping yourself.

  • SMTProbe tries to connect to the 1st MX of a mail domain and initiates a SMTP session with the HELO - MAIL FROM: - RCPT TO: sequence, but instead of DATA it sends an early QUIT.

  • This SMTProbe tool receives very reliable the error codes from foreign hosts, that may be related to RBL or other blocking.

  • You might want to call this tool by the mail filter shell script that is invoked before any SMTP transport actually happens. The idea is to SMTProbe every single recipient before actually sending out the mail.

  • If there is no error, then the script should simply go ahead as usual:

    sendmail -i "$@" // $@ expands to -f $(sender) -- $(recipient)

  • If there is an error, then SMTProbe calls SMTPRedirectToRelay that redirects the blocked message to the secondary relay host.

 

SMTProbe.c


//  SMTProbe.c

//

//  Part of the SMTProbe Package

//

//  Created by Rolf on 2006-10-18.

//  Copyright 2006 InstantWare - Dr. Rolf Jansen. All rights reserved.

//

//  Permission to use, copy, modify, and distribute this software for any

//  purpose with or without fee is hereby granted, provided that the above

//  copyright notice and this permission notice appear in all copies.

//

//  THIS SOFTWARE IS PROVIDED BY InstantWare - Dr. Rolf Jansen "AS IS"

//  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,

//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR

//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL InstantWare - Dr. Rolf Jansen

//  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR

//  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF

//  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS

//  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN

//  CONTRACT, STRICT 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 SUCH DAMAGE.



// Standard C includes

#include <stdlib.h>

#include <string.h>

#include <stdio.h>

#include <stdbool.h>

#include <errno.h>

#include <unistd.h>


// header files which must be included for

// high level DNS services and threaded

// (a)synchronous socket connections

#include <sys/time.h>

#include <pthread.h>

#include <netdb.h>

#include <fcntl.h>

#include <sys/select.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>


// header files for the low level nameserver interface

#include <sys/types.h>

#include <resolv.h>

#include <arpa/nameser.h>



// constants

#define defaultBufferSize  4096

#define maxThreads         25



// types

typedef struct MXRecord *MXPtr, **MXRef;

typedef struct MXRecord

{

   short pref;

   char  name[NS_MAXDNAME];

} MXRecord;



typedef struct MXRecordList *MXListPtr;

typedef struct MXRecordList

{

   short   count;

   MXPtr   entry[];

} MXRecordList;



typedef struct DomainRecord *DomainPtr, **DomainRef;

typedef struct DomainRecord

{

   short   count;         // number of recipients

   char*  *recipients;    // string ptr array of recipients @ this domain

   char*   domainName;    // the domain name


   short     B;           // balance indicator

   DomainPtr L, R;        // left and right branch of this node

} DomainRecord;


typedef struct sockaddr_in  *SocketAddressPtr;



//global variables

int         gRemoteRelayArgCount = 3; // at least the executable + PID + sender name

int         gLocalRecipientCount = 0;

char      **gRemoteRelayArgV;

char       *gLocalRelayArgL;

DomainPtr   gSender, gRecipients;


short           gThreadCount = 0;

pthread_mutex_t gThreadMutex = PTHREAD_MUTEX_INITIALIZER;



void *mallocclear(size_t size)

{

   return calloc(size/4 + 1, 4);

}



// code for looking up the MX hosts for a given domain

// returns an array of MXRecords sorted by preferences

int MXCompare(const void *A, const void *B)

{

   short a = (*(MXRef)A)->pref;

   short b = (*(MXRef)B)->pref;


   if (a < b)

      return -1;

   else if (a > b)

      return  1;

   else

      return  0;

}


MXListPtr MXLookup(char *domainName)

{

   int i, responseLen, resourceRecordCount;

   unsigned char response[defaultBufferSize];


   ns_msg    msgHandle;

   ns_rr     resourceRecord;


   MXPtr     MX;

   MXListPtr MXList = mallocclear(sizeof(short));


   if ((responseLen = res_query(domainName, ns_c_in, ns_t_mx, response, defaultBufferSize)) > 0)

      if (ns_initparse(response, responseLen, &msgHandle) == 0)

      {

         resourceRecordCount = ns_msg_count(msgHandle, ns_s_an);

         for(i = 0; i < resourceRecordCount; i++)

         {

            if (ns_parserr(&msgHandle, ns_s_an, i, &resourceRecord) == 0 && ns_rr_type(resourceRecord) == ns_t_mx)

            {

               MXList = realloc(MXList, sizeof(short) + (MXList->count + 1)*sizeof(MXPtr));

               MXList->entry[MXList->count++] = MX = mallocclear(sizeof(MXRecord));

               MX->pref = ns_get16(ns_rr_rdata(resourceRecord));

               ns_name_uncompress(ns_msg_base(msgHandle), ns_msg_end(msgHandle), ns_rr_rdata(resourceRecord) + NS_INT16SZ, MX->name, NS_MAXDNAME);

            }

         }

      }


   if (MXList->count > 1)

      qsort(&MXList->entry[0], MXList->count, sizeof(MXPtr), MXCompare);

   return MXList;

}


void MXListFree(MXListPtr MXList)

{

   short i;


   for (i = 0; i < MXList->count; i++)

      free(MXList->entry[i]);

   free(MXList);

}




// Collect domain wise all recipients in a balanced binary tree

bool AddDomainNode(char* recipient, char* domainName, DomainRef node)

{

   // variables

   bool      balanced;

   DomainPtr p0, p1, p2;


   // begin

   p0 = *node;


   if (!p0)

   {                                // Schlüssel nicht im Baum; füge ihn ein

      balanced = false;

      p0 = mallocclear(sizeof(DomainRecord));

      if (recipient)

      {

         p0->count = 1;

         p0->recipients = malloc(sizeof(char *));

         p0->recipients[0] = recipient;

      }

      p0->domainName = domainName;

   }


   else if (strcmp(domainName, p0->domainName) < 0)

   {

      balanced = AddDomainNode(recipient, domainName, &p0->L);


      if (!balanced)                // linker Ast wurde größer

         switch (p0->B)

         {

            case +1:                // +1 => linker Ast war kürzer als der rechter Ast,

               p0->B = 0;           //  und die Unausgeglichenheit wurde jetzt ausgeglichen

               balanced = true;

               break;


            case  0:                //  0 => beide Äste waren gleich lang,

               p0->B = -1;          //  und durch Einfügen wurde der linke Ast jetzt länger

               break;


            case -1:                // -1 => linker Ast war schon länger als der rechte Ast,

               p1 = p0->L;          //  und erneutes Ausgleichen wird notwendig


               if (p1->B == -1)

               {                    // einfache LL Rotation

                  p0->L = p1->R;

                  p1->R = p0;

                  p0->B = 0;

                  p0 = p1;

               }


               else

               {                    // doppelte LR Rotation

                  p2 = p1->R;

                  p1->R = p2->L;

                  p2->L = p1;

                  p0->L = p2->R;

                  p2->R = p0;


                  p0->B = (p2->B == -1) ? +1 : 0;

                  p1->B = (p2->B == +1) ? -1 : 0;


                  p0 = p2;

               }


               p0->B = 0;

               balanced = true;

               break;

         }

   }


   else if (strcmp(domainName, p0->domainName) > 0)

   {

      balanced = AddDomainNode(recipient, domainName, &p0->R);


      if (!balanced)                // rechter Ast wurde größer

         switch (p0->B)

         {

            case -1:                // -1 => rechter Ast war kürzer als der linke Ast,

               p0->B = 0;           //  und die Unausgeglichenheit wurde jetzt ausgeglichen

               balanced = true;

               break;


            case  0:                //  0 => beide Äste waren gleich lang,

               p0->B = +1;          //  und durch Einfügen wurde der rechte Ast jetzt länger

               break;


            case +1:                // +1 => rechter Ast war schon länger als der linke Ast,

               p1 = p0->R;          //  und erneutes Ausgleichen wird notwendig


               if (p1->B == +1)

               {                    // einfache RR Rotation

                  p0->R = p1->L;

                  p1->L = p0;

                  p0->B = 0;

                  p0 = p1;

               }


               else

               {                    // doppelte RL Rotation

                  p2 = p1->L;

                  p1->L = p2->R;

                  p2->R = p1;

                  p0->R = p2->L;

                  p2->L = p0;


                  p0->B = (p2->B == +1) ? -1 : 0;

                  p1->B = (p2->B == -1) ? +1 : 0;


                  p0 = p2;

               }


               p0->B = 0;

               balanced = true;

               break;

         }

   }


   else

   {                                // gefunden

      balanced = true;

      if (recipient)

      {

         p0->recipients = realloc(p0->recipients, (p0->count + 1) * sizeof(char *));

         p0->recipients[p0->count++] = recipient;

      }

   }


   *node = p0;

   return balanced;

}




short doSMTPTransaction(int smtpSocket, char *sendBuffer, long sendBufferSize, char *receiveBuffer)

{

   long receiveSize;


   if (sendBuffer && send(smtpSocket, sendBuffer, sendBufferSize, 0) == -1)

      return 1000;


   else if ((receiveSize = recv(smtpSocket, receiveBuffer, defaultBufferSize - 1, 0)) < 0)

      return 2000;


   else

   {

      receiveBuffer[receiveSize] = '\0';

      return strtol(receiveBuffer, NULL, 10);

   }

}



int connectWithTimeout(int sock, const struct sockaddr *address, socklen_t addressLen, unsigned long timeoutSecs)

{

   if (timeoutSecs == 0)

      return connect(sock, address, addressLen);


   else

   {

      int            connectErr, connectResult = -1;

      socklen_t      connectErrLen = sizeof(connectErr);

      struct timeval timeout = {timeoutSecs, 0};

      fd_set         socketIOSet;


      // set socket to asynchronous mode

      fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, NULL) | O_NONBLOCK);

      if ((connectResult = connect(sock, address, addressLen)) < 0)

      {

         if (errno == EINPROGRESS)

         {

            FD_ZERO(&socketIOSet);

            FD_SET(sock, &socketIOSet);

            if ((connectResult = select(sock + 1, NULL, &socketIOSet, NULL, &timeout)) > 0)

            {

               getsockopt(sock, SOL_SOCKET, SO_ERROR, &connectErr, &connectErrLen);

               if (connectErr != 0)

                  return -1;

            }

            else

               return -1;

         }

         else

            return -1;

      }

      // reset socket to synchronous mode

      fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, NULL) & (~O_NONBLOCK));


      setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval));

      setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval));


      return connectResult;

   }

}



typedef struct ProbeThreadArgs *ProbeThreadArgPtr;

typedef struct ProbeThreadArgs

{

   struct sockaddr_in   address;

   DomainPtr            domainNode;

} ProbeThreadArgs;


void *SMTProbeThread(void *probeThreadArgs)

{

   SocketAddressPtr  address = &((ProbeThreadArgPtr)probeThreadArgs)->address;

   DomainPtr         node    =  ((ProbeThreadArgPtr)probeThreadArgs)->domainNode;


   short  i, smtpCode = 0;

   int    mutexResultCode;

   long   smtpSocket, sendBufferSize, domainStrLen, recipientListLen;

   char   *p, transferBuffer[defaultBufferSize];


   if (address->sin_port != 0 && (smtpSocket = socket(AF_INET, SOCK_STREAM, 0)) != -1)

   {

      if (connectWithTimeout(smtpSocket, (struct sockaddr *)address, sizeof(struct sockaddr_in), 15) != -1)

      {

         if ((smtpCode = doSMTPTransaction(smtpSocket, NULL, 0, transferBuffer)) > 299)

            goto closeSession;


         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "EHLO %s\r\n", gSender->domainName);

         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)

            goto closeSession;


         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "MAIL FROM:<%s@%s>\r\n", gSender->recipients[0], gSender->domainName);

         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)

            goto closeSession;


         for (i = 0; i < node->count; i++)

         {

            sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "RCPT TO:<%s@%s>\r\n", node->recipients[i], node->domainName);

            if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) == 421 || smtpCode == 554 || smtpCode >= 1000)

               goto closeSession;

         }


         sendBufferSize = sprintf(transferBuffer, "QUIT\r\n");

         smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer);


      closeSession:

         close(smtpSocket);

      }

   }


   // process the recipients list according to the received error codes

   mutexResultCode = pthread_mutex_lock(&gThreadMutex);

   domainStrLen = strlen(node->domainName) + 2;    // reserve space for the leading @ and the trailing '\0'

   if (smtpCode == 421 || smtpCode == 554)

   {                                               // got a policy rejection

      gRemoteRelayArgV = realloc(gRemoteRelayArgV, sizeof(char*) * (gRemoteRelayArgCount + node->count + 1));

      for (i = 0; i < node->count; i++)

      {

         gRemoteRelayArgV[gRemoteRelayArgCount + i] = p = malloc(strlen(node->recipients[i]) + domainStrLen);

         sprintf(p, "%s@%s", node->recipients[i], node->domainName);

      }

      gRemoteRelayArgCount += node->count;

   }


   else                                            // handle all other cases (OK and error replies) by the local MTA

   {

      recipientListLen = 1;                        // reserve space for the leading blank

      for (i = 0; i < node->count; i++)

         recipientListLen += strlen(node->recipients[i]) + domainStrLen;


      gLocalRelayArgL = realloc(gLocalRelayArgL, strlen(gLocalRelayArgL) + recipientListLen);

      for (i = 0; i < node->count; i++)

         sprintf(gLocalRelayArgL, "%s %s@%s", gLocalRelayArgL, node->recipients[i], node->domainName);


      gLocalRecipientCount += node->count;

   }


   gThreadCount--;

   mutexResultCode = pthread_mutex_unlock(&gThreadMutex);


   free(probeThreadArgs);

   pthread_exit(NULL);

}




void SMTProbeRecipientDomains(DomainPtr node)

{

   bool              ipOK = false;

   short             i;

   int               threadID;

   struct timespec   delay = {0, 10};

   pthread_t         probeThread;

   struct hostent   *host;

   MXListPtr         MXList;

   ProbeThreadArgPtr threadArgs = mallocclear(sizeof(ProbeThreadArgs));


   if (node)

   {

      SMTProbeRecipientDomains(node->L);



      MXList = MXLookup(node->domainName);


      for (i = 0; i < MXList->count && !ipOK; i++)

         if (!(ipOK = inet_aton(MXList->entry[i]->name, &threadArgs->address.sin_addr)))

            if (ipOK = ((host = gethostbyname(MXList->entry[i]->name)) != NULL))

               threadArgs->address.sin_addr = *(struct in_addr*)host->h_addr;


      MXListFree(MXList);


      if (!ipOK)        // did not found a valid MX IP, so try to connect to the @domain itself

         if (!(ipOK = inet_aton(node->domainName, &threadArgs->address.sin_addr)))

            if (ipOK = ((host = gethostbyname(node->domainName)) != NULL))

               threadArgs->address.sin_addr = *(struct in_addr*)host->h_addr;


      if (ipOK)

      {

         threadArgs->address.sin_port   = htons(25);

         threadArgs->address.sin_family = AF_INET;

      }

      else

         threadArgs->address.sin_addr.s_addr = 0;

      threadArgs->domainNode = node;


      // wait until the next free slot for a new thread

      while (gThreadCount >= maxThreads)

         nanosleep(&delay, NULL);

      gThreadCount++;

      threadID = pthread_create(&probeThread, NULL, SMTProbeThread, (void *)threadArgs);



      SMTProbeRecipientDomains(node->R);

   }

}



void SMTProbeDomainsFree(DomainPtr node)

{

   if (node)

   {

      SMTProbeDomainsFree(node->L);

      SMTProbeDomainsFree(node->R);


      if (node->recipients)

        free(node->recipients);

      free(node);

   }

}




int main(int argc, char *argv[])

{

   char   c, inName[256], outName[256];

   FILE   *in, *out;


   int    i;

   char   *p;


   long   relayStrLen;

   char   *relayStrFormat = "-f %s --";

   struct timespec  delay = {0, 10};


   res_init();


   // the command line arguments should come in the form

   // PID -f <sender> -- <recipient 1> <recipient 1> ... <recipient n>

   // since SMTProbe is meant to be called by a shell script

   // no error checking is done.

   int  senderArgIndex = 0;

   int  recipientsArgIndex = 0;

   for (i = 2; i < argc; i++)

   {

      if (senderArgIndex == 0 && strcmp(argv[i], "-f") == 0)

         senderArgIndex = ++i;


      else if (recipientsArgIndex == 0 && strcmp(argv[i], "--") == 0)

      {

         recipientsArgIndex = ++i;

         break;

      }

   }


   gRemoteRelayArgV    = malloc(sizeof(char*) * (gRemoteRelayArgCount + 2));

   gRemoteRelayArgV[0] = "SMTPRedirectToRelay";          // the file name of the redirection tool

   gRemoteRelayArgV[1] = argv[1];                        // copy the PID parameter for identification of the temp file

   relayStrLen = strlen(argv[senderArgIndex]) + 1;       // reserve space for the terminating '\0'

   gRemoteRelayArgV[2] = malloc(relayStrLen);

   strcpy(gRemoteRelayArgV[2], argv[senderArgIndex]);    // make a copy of the fully qualified senders e-mail address


   gLocalRelayArgL = malloc(relayStrLen);

   relayStrLen += strlen(relayStrFormat) - 2;            // subtract 2 for the %s in the format specifier

   sprintf(gLocalRelayArgL, relayStrFormat, argv[senderArgIndex]);



   gSender = mallocclear(sizeof(DomainRecord));          // set up the sender domain record

   gSender->count = 1;

   gSender->recipients = malloc(sizeof(char*));

   gSender->recipients[0] = p = argv[senderArgIndex];

   while(*p != '@' && *p != '%' && *p != '\0')

      p++;

   *p++ = '\0';

   gSender->domainName = p;



   gRecipients = mallocclear(sizeof(DomainRecord));      // set up the root node of the binary tree

   gRecipients->count = 1;                               // of the recipients domain records

   gRecipients->recipients = malloc(sizeof(char*));

   gRecipients->recipients[0] = p = argv[recipientsArgIndex];

   while(*p != '@' && *p != '%' && *p != '\0')

      p++;

   *p++ = '\0';

   gRecipients->domainName = p;


   for (i = recipientsArgIndex + 1; i < argc; i++)       // add the remaining recipient domains records

   {                                                     // to the domain records tree

      p = argv[i];

      while(*p != '@' && *p != '%' && *p != '\0')

         p++;

      *p++ = '\0';

      AddDomainNode(argv[i], p, &gRecipients);

   }


   SMTProbeRecipientDomains(gRecipients);                // probe the recipients


   while(gThreadCount > 0)                               // wait for completion

      nanosleep(&delay, NULL);


   SMTProbeDomainsFree(gRecipients);                     // clean up the recipients domain tree


   if (gRemoteRelayArgCount > 3)

   {

      sprintf(inName, "out.%s", argv[1]);                // put a copy of the e-mail text file into

      sprintf(outName, "RelayQueue/out.%s", argv[1]);    // our RelayQueue directory.

      if ((in  = fopen(inName,  "r")) && (out = fopen(outName, "w")))

      {

         while ((c = fgetc(in)) != EOF)

            fputc(c, out);

         fclose(in);

         fclose(out);

      }

      else

         return -1;                                      // fatal file system error


      if (fork() != 0)                                   // fork our process

      {                                                  // the original process continues here

         if (gLocalRecipientCount > 0)

         {

            printf("%s", gLocalRelayArgL);               // some recipients can be handled by the local MTA

            return 1;                                    // others need to be redirect to our relay MTA

         }

         else

            return 2;                                    // redirect message for all recipients to our relay MTA

      }


      else

      {                                                  // the forked out process continues here

         gRemoteRelayArgV[gRemoteRelayArgCount] = NULL;

         execvp(gRemoteRelayArgV[0], gRemoteRelayArgV);  // use it to call the SMTPRedirectToRelay tool

         return 0;

      }

   }


   else

      return 0;                                          // Normal operation -- no redirection necessary

}



SMTPRedirectToRelay.c


//  SMTPRedirectToRelay.c

//

//  Part of the SMTProbe Package

//

//  Created by Rolf on 2006-10-22.

//  Copyright 2006 InstantWare - Dr. Rolf Jansen. All rights reserved.

//

//  Permission to use, copy, modify, and distribute this software for any

//  purpose with or without fee is hereby granted, provided that the above

//  copyright notice and this permission notice appear in all copies.

//

//  THIS SOFTWARE IS PROVIDED BY InstantWare - Dr. Rolf Jansen "AS IS"

//  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,

//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR

//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL InstantWare - Dr. Rolf Jansen

//  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR

//  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF

//  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS

//  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN

//  CONTRACT, STRICT 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 SUCH DAMAGE.



// Standard C includes

#include <stdlib.h>

#include <string.h>

#include <stdio.h>

#include <stdbool.h>

#include <errno.h>

#include <unistd.h>


// header files which must be included for

// high level DNS services and socket connections

#include <netdb.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>


// constants

#define defaultBufferSize  4096



short doSMTPTransaction(int smtpSocket, char *sendBuffer, long sendBufferSize, char *receiveBuffer)

{

   long receiveSize = 0;


   if (sendBuffer != NULL && send(smtpSocket, sendBuffer, sendBufferSize, 0) == -1)

      return 1000;


   else if (receiveBuffer != NULL && (receiveSize = recv(smtpSocket, receiveBuffer, defaultBufferSize - 1, 0)) < 0)

      return 2000;


   if (receiveSize != 0)

   {

      receiveBuffer[receiveSize] = '\0';

      return strtol(receiveBuffer, NULL, 10);

   }


   else

      return 0;

}



// argv[0]:    size of head in bytes

// argv[1]:    pid of calling process => temp file extension => ReleayQueue/out.PID

// argv[2]:    fully qualified senders accress

// argv[3..n]: fully qualified recipient addresses

int main(int argc, char *argv[])

{

   short  i, smtpCode = 0, resultCode = 1;

   long   smtpSocket, sendBufferSize;

   char   c = 0;

   char   inName[256], transferBuffer[defaultBufferSize];

   FILE   *in;


   struct hostent    *host = NULL;

   struct sockaddr_in address;


   char   *smtpMyHostHELOName = "myHELOdomain.com";                                 // replace with your valid HELO identifier

   char   *smtpRelayHostName  = "smtp.relayserver.com";                             // replace with address of the relay server

   char   *smtpAuthLoginID    = "U01UUF9Mb2dpbl9JRAo=\r\n";                         // replace with your base 64 encoded SMTP login ID

   char   *smtpAuthPassword   = "ZG9uJ3QgdXNlIDEyMzQ1Njc4OSBhcyBwYXNzd29yZAo=\r\n"; // replace with your base 64 encoded SMTP auth password


   sprintf(inName, "RelayQueue/out.%s", argv[1]);

   if ((in  = fopen(inName,  "r")) != NULL)

   {

      if (!inet_aton(smtpRelayHostName, &address.sin_addr))

         if ((host = gethostbyname(smtpRelayHostName)))

            address.sin_addr = *(struct in_addr*)host->h_addr;

         else

            return 1;


      address.sin_port   = htons(25);

      address.sin_family = AF_INET;


      if ((smtpSocket = socket(AF_INET, SOCK_STREAM, 0)) != -1)

      {

         if (connect(smtpSocket, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) != -1)

         {

            if ((smtpCode = doSMTPTransaction(smtpSocket, NULL, 0, transferBuffer)) > 299)

               goto closeSession;


            sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "EHLO %s\r\n", smtpMyHostHELOName);

            if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)

               goto closeSession;


            sendBufferSize = sprintf(transferBuffer, "AUTH LOGIN\r\n");

            if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 334)

               goto closeSession;


            if ((smtpCode = doSMTPTransaction(smtpSocket, smtpAuthLoginID, strlen(smtpAuthLoginID), transferBuffer)) != 334)

               goto closeSession;


            if ((smtpCode = doSMTPTransaction(smtpSocket, smtpAuthPassword, strlen(smtpAuthPassword), transferBuffer)) != 235)

               goto closeSession;


            sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "MAIL FROM:<%s>\r\n", argv[2]);

            if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)

               goto closeSession;


            for (i = 3; i < argc; i++)

            {

               sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "RCPT TO:<%s>\r\n", argv[i]);

               if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)

                  goto closeSession;

            }


            sendBufferSize = sprintf(transferBuffer, "DATA\r\n");

            if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) != 354)

               goto closeSession;


            while (c != EOF)

            {

               for (i = 0; i < defaultBufferSize && (c = fgetc(in)) != EOF; i++)

                  transferBuffer[i] = c;

               smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, i, NULL);

            }


            sendBufferSize = sprintf(transferBuffer, "\r\n.\r\nQUIT\r\n");

            if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) == 250)

               resultCode = 0;


         closeSession:

            close(smtpSocket);

            fclose(in);

         }

      }


      remove(inName);

   }


   return resultCode;

}

 

Imprint / Impressum

Dr. Rolf Jansen
Rua Reginaldo de Lima, 98 - Parque São Diogo
09732-550 São Bernardo do Campo
São Paulo - Brazil
rj(at)cyclaero.com
Cyclaero Blog (German)