Pages

E-mail Architecture Part II

However to directly talk SMTP commands to the target host and deliverthe mail is something that needs some more work.
Not that it cannot be done. netcat can be used along with HEREDOC forthis. But I am going to present a C program I wrote a little while ago.Let us take a look at it.
/* Copyright (c) 2007 Girish Venkatachalam
*
* 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.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include
#include
#include
#include
#include
#include
#include

#include
#include
#include
#include
#include

#include
#include

#define TRUE 1

struct mx
{
int prio;
char host[1024];
};

#ifndef HFIXEDSZ
# define HFIXEDSZ 12
#endif
#ifndef INT16SZ
# define INT16SZ sizeof(cit_int16_t)
#endif
#ifndef INT32SZ
# define INT32SZ sizeof(cit_int32_t)
#endif

int mxcomp(int p[],int n)
{
if (p[1] > p[2]) return(1);
else if (p[1] < p[2]) return(0);
else return(random() % n);
}



void sort_mxrecs (struct mx *mxrecs, int nmx)
{
int a, b;
struct mx t1, t2;

if (nmx < 2) return;

/*
for (a = nmx - 2; a >= 0; --a)
{
for (b = 0; b <= a; ++b)
{
if (mxcomp(mxrecs[b].prio,mxrecs[b+1].prio))
{
memcpy(&t1, &mxrecs[b], sizeof(struct mx));
memcpy(&t2, &mxrecs[b+1], sizeof(struct mx));
memcpy(&mxrecs[b], &t2, sizeof(struct mx));
memcpy(&mxrecs[b+1], &t1, sizeof(struct mx));
}
}
}
*/
}



int getmxip(char *domain,struct in_addr *ip)
{
union
{
u_char bytes[1024];
HEADER header;
} ans;

int ret;
unsigned char *startptr, *endptr, *ptr;
char expanded_buf[1024];
unsigned short prio, type;
int n = 0;
int qdcount;

struct mx *mxrecs = NULL;
struct hostent *h;
int nmx = 0;

h = (struct hostent *)malloc(sizeof(struct hostent));
ret = res_query (domain, C_IN, T_MX, (unsigned char *)ans.bytes,
sizeof(ans));
if (ret < 0)
{
mxrecs = (struct mx *)malloc(sizeof(struct mx));
mxrecs[0].prio = 0;
strcpy(mxrecs[0].host, domain);
h = gethostbyname(domain);
memcpy(ip,h->h_addr,h->h_length);
nmx = 0;
}
else
{
if (ret > sizeof(ans)) ret = sizeof(ans);

startptr = &ans.bytes[0];
endptr = &ans.bytes[ret];
ptr = startptr + HFIXEDSZ; /* skip header */

for (qdcount = ntohs(ans.header.qdcount); qdcount--;
ptr += ret + QFIXEDSZ)
{
if ((ret = dn_skipname(ptr, endptr)) < 0) return(0);
}

while(TRUE)
{
memset (expanded_buf, 0, sizeof(expanded_buf));
ret = dn_expand (startptr, endptr, ptr, expanded_buf,
sizeof(expanded_buf));
if (ret < 0) break;
ptr += ret;

GETSHORT (type, ptr);
ptr += INT16SZ + INT32SZ;
GETSHORT (n, ptr);

if (type != T_MX) ptr += n;
else
{
GETSHORT(prio, ptr);
ret = dn_expand(startptr, endptr, ptr, expanded_buf,
sizeof(expanded_buf));
ptr += ret;

++nmx;
if (mxrecs == NULL)
mxrecs = (struct mx *)malloc(sizeof(struct mx));
else
mxrecs = (struct mx *)realloc (mxrecs, (sizeof(struct mx) * nmx));

mxrecs[nmx - 1].prio = prio;
strcpy(mxrecs[nmx - 1].host, expanded_buf);
}
}
}
sort_mxrecs(mxrecs, nmx);

h = gethostbyname(mxrecs[0].host);

memcpy(ip,h->h_addr,h->h_length);

{char *ipadd;

ipadd = inet_ntoa(*ip);
}

free(mxrecs);
return(nmx);
}

int pipe_file_to_server(int s,char *filename,char *sender,
char *sname,char *recipient,char *subject) {
int fd,b,bs;

char buf[8192],envelope[8192];

#define MSG "Please terminate mail with "

/*
* SMTP envelope format from rfc 2821
* : Received: from bar.com by foo.com ; Thu, 21 May 1998
* : 05:33:29 -0700
* : Date: Thu, 21 May 1998 05:33:22 -0700
* : From: John Q. Public

* : Subject: The Next Meeting of the
* board
* : To: Jones@xyz.com
* :
* : Bill:
*
*/
envelope[0] = 0; /* Initialize the buffer */
snprintf(buf,sizeof(buf),"From: %s <%s>rn",sname,sender);
strncat(envelope,buf,strlen(buf));
snprintf(buf,sizeof(buf),"To: <%s>rn",recipient);
strncat(envelope,buf,strlen(buf));
snprintf(buf,sizeof(buf),"Subject: %srn",subject);
strncat(envelope,buf,strlen(buf));
bs = send(s,envelope,strlen(envelope),0);

if( bs <= 0) {
perror("send()");
}

/* Blank line between envelope & body */
snprintf(buf,sizeof(buf),"rn");
bs = send(s,buf,strlen(buf),0);

if( bs <= 0) {
perror("send()");
}

if(NULL == filename) {
write(1,MSG,strlen(MSG));
fd = 0;
}
else {
fd = open(filename,O_RDONLY);
if (-1 == fd ) {
errx(1,"Couldn't open mail text file [%s]",filename);
}
}

while((b = read(fd,buf,sizeof(buf))) > 0) {

bs = send(s,buf,b,0);
if( bs < 0) {
perror("send()");
}
}

if(fd != 0)
close(fd);

}

int send_line(int s, char *buf,int n) {

int bs,r;
char rbuf[512];
int status;

bs = send(s,buf,n,0);

if (-1 == bs) {
perror("send()");

}

r = recv(s,rbuf,sizeof(rbuf),0);

if(-1 == r) {
perror("recv()");
}
rbuf[r] = 0;


status = strtoll(rbuf,NULL,10);

if(421 == status) {
printf("We are being greylisted");
}

if(status > 400) {
errx(1,"Trouble for us, let us get out of the
game..Exiting");
}
return 0;


}

int smtp_auth(int s,struct in_addr *ip, char *userid, char *password) {


/* Fill in later*/
return 0;
}
int send_mail(struct in_addr *ip,char *sender, char *sname,
char *recipient,char
*subject, char *userid,char *password,char *file) {

char sbuf[512],rcvbuf[512];
int s,rl,ret;
struct sockaddr_in mailhost;
char *localdomain = "susmita.org";
socklen_t l;
int status;

mailhost.sin_addr = *ip;
/* This could be 587 also... */
mailhost.sin_port = htons(25);
mailhost.sin_family = AF_INET;

l = sizeof(struct sockaddr_in);

s = socket(PF_INET,SOCK_STREAM,0);

if( s < 0) {
perror("socket");
}

ret = connect(s, (struct sockaddr *)&mailhost,l);

if(-1 == ret) {
perror("connect");
}


rl = recv(s,rcvbuf,sizeof(rcvbuf),0);

if(-1 == rl) {
perror("recv()");
}
puts(rcvbuf);
status = strtoll(rcvbuf,NULL,10);

if(421 == status) {
errx(1,"We are being greylisted");
}

if(status > 400) {
errx(1,"Trouble for us, let us get out of the
game..Exiting");
}

smtp_auth(s,ip,userid,password);

snprintf(sbuf,sizeof(sbuf),"HELO %srn",localdomain);
send_line(s,sbuf,strlen(sbuf));
snprintf(sbuf,sizeof(sbuf),"MAIL FROM:<%s>rn",sender);
send_line(s,sbuf,strlen(sbuf));
snprintf(sbuf,sizeof(sbuf),"RCPT TO:<%s>rn",recipient);
send_line(s,sbuf,strlen(sbuf));
snprintf(sbuf,sizeof(sbuf),"DATArn",recipient);
send_line(s,sbuf,strlen(sbuf));

pipe_file_to_server(s,file,sender,sname,recipient,subject);
snprintf(sbuf,sizeof(sbuf),"rn.rn",subject);
send_line(s,sbuf,strlen(sbuf));

return 0;


}

int main(int argc, char **argv) {

char buf[1024],prompt[512];
char *userid,*sender,*recipient,*domainname,*subject;
char *password,*sname,*mailtxtfile=NULL;
int op,status;
struct in_addr ip;

if(argc < 2) {

errx(-1,"Usage: n%s -u n
-s n
-n n
-r n
-S n
[-f ]n ",argv[0]);
}


while((op = getopt(argc,argv,"u:s:n:r:S:f:")) != EOF) {

switch (op) {
case 'u':
userid = optarg;
break;
case 's':
sender = optarg;
break;
case 'n':
sname = optarg;
break;
case 'r':
recipient = optarg;
break;
case 'S':
subject = optarg;
break;
case 'f':
mailtxtfile = optarg;
break;
default:
errx(1,"Unknown option");

}

}

domainname = strchr(recipient,'@')+1;

snprintf(prompt,sizeof(prompt),"Password for [%s]:",userid);
password = getpass(prompt);
getmxip(domainname, &ip);
status = send_mail(&ip,sender,sname,recipient,subject,userid,password,
mailtxtfile);

if(0 != status) {

errx(1,"Sending mail failed");
perror("SMTP");
}


}


As you can see, it is a simple program that uses plain text commands for sending an e-mail with the right headers. You are free to tweak it to suit your taste.
Remember that sending e-mail has nothing to do with receiving mail. It uses a completely different wire protocol. POP3 is popular but IMAP is a much more powerful alternative. IMAP saves precious bandwidth as it has support for a powerful query language for scanning the headers.
E-mail headers are a topic in itself and it has attained even more significance with the growing spam and the various alternatives to control it. You can add custom headers with the prefix "X-" and a good look at the headers of your mail message can be quite educational.
Spam control is a topic of intense research activity and I cannot do justice to it in this article. However no mention of e-mail will be complete without a mention of spam as well.
Basically spam is unsolicited bulk mail. And the defintion of spam varies from individuals to individuals. I may love ice creams but you might not like it. Especially when you did not order for it. This makes spam control slightly more complicated. Due to this and various other reasons user feedback has become critical for spam control and it is not unusual to refer to a common database or "spam corpus" which is populated by humans. If you think a particular mail is spam and several others share this view, it is very likely another person will view it as spam as well.
There are several approaches to control/mitigate spam at the SMTP relay level itself using SMTP 4xx reply messages. This is called teergrubing, greytrapping,greylisting,whitelisting, blacklisting, greenlisting and so on.
Bayesian probability theory comes in handy and so does Markovian chains for identifying patterns in text and html tags.
Spam or no spam, e-mails have become an unalienable part of our modern communications infrastructure. I hope this article serves as a good introduction to the innards of this marvellous tool.
Bookmark and Share
my Site

0 comments:

Post a Comment