Internet Programming with Windows - A Tutorial

Part 2

By Gandalf Gandalf@dhamma.org.uk (c) Mar 2000

 

Contents

Disclaimer
Introduction
RFC's
Simple POP Email Client
Sending Emails by SMTP
Using UDP/IP
Conclusion and What's Next


Disclaimer

Some of the programs presented in this tutorial may be illegal to use unless you have permission from the owner of the server you attempt to connect to and also to the recipient of any email's you send. If in doubt, don't do it. You can of course use these freely on your own network if you own one. If you want to try anything out, then consider setting up a Linux Box to act as a server so you can experiment in peace without any fear of reprisal. Everything presented in this tutorial should be treated as a theoretical discussion of what is possible. The author will not be held responsible for actions of anyone using material from this tutorial for illegal purposes.


Introduction

Hopefully we've all survived part one of the tutorial. Part two is going to start off pretty much where part one left off. We will start by putting together a more advanced program using TCP/IP that will allow you to retrieve emails from a POP account into a mail spool file. We will also develop a program to allow you to send emails via smtp. This will include falsifying header information. The programs are going to be fairly rudimentary, but will provide a nice framework for those who would like to develop their own email client.


RFC's

I've mentioned RFC's briefly in part one of the tutorial. We are now going to use information from various RFC's in some detail.

So what are RFC's ?

Every protocol that exists on the net, whether it's for handling mail, news, web site access or anything else you care to consider, there is an RFC for it.

RFC stands for Request for Comments, and the whole of the internet and it's associated protocols are based on them. If you want to develop or understand any Internet application, you need to read the associated RFC's.

You can find a list of all RFC's used so far in this tutorial at

http://www.exegesis.org.uk/gandalf/winsock/rfc/index.htm

The RFC's themselves are also downloadable or readable from links on the index page.

I will eventually be putting ALL rfc's online over a period of time, with a search facility.

OK, on with the tutorial, let's start with a bit of programming.


Simple POP Email Client

Let's continue where we left off in the first part of the tutuorial by developing another TCP/IP program. We will put together a basic framework to allow you to connect to, and negotiate downloading POP mail from your POP server. As usual, we will be presenting a piece of code that is functional rather then exhaustively error trapped to make it easier to understand. There are comments in the code to suggest areas that would need to be changed if you wanted to develop a robust POP client capable of handling attachments etc, but this will do to get you started. Later parts of the tutorial will be using this code to develop a full multiple POP account client with SMTP posting.

The program uses a log file called poplog.txt to store what's sent to and received back from the pop server so you can check for errors etc. It saves all mails to a text mail spool called mailbox.txt complete with headers.

Here's the code:-

/*
 * popclnt1.c
 *
 *
 * A very basic pop client that retrieves mail from an account into
 * a multi pop retrieval client in later parts of the tutorial with all
 * the extra error handling that's needed. Use this as a framework to
 * develop your own pop client. This program doesn't delete any mails
 * from the pop mail server, so you can try it out without being afraid
 * of losing any emails!
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include <winsock.h>

#define WIN32_LEAN_AND_MEAN

void handle_error(void);
void pop_error(void);
void write_file(int file, char *p);

int main (int argc, char *argv[])
{
  const long BUF_LEN=60000;

  /* 60000 char buffer. In real life we would have to allow for a much
   * larger buffer than this to allow retrieval of long messages which
   * might have attachments. We will be doing this in one of the later
   * parts of the tutorial by using synchronised data flow with a smaller
   * buffer that downloads mails in smaller chunks.
   */

  const char POP_ERR_MSG[]="-ERR";          /* POP Server error response */
  const int LOG_FILE=1;                     /* write to log file */
  const int MAIL_FILE=2;                    /* write to mail file */
  const int SCREEN=4;                       /* display on screen */
  WORD wVersionRequested;                   /* socket dll version info */
  WSADATA wsaData;                          /* socket lib initialisation */
  SOCKET sock;                              /* socket details */
  struct sockaddr_in address;               /* socket address stuff */
  struct hostent * host;                    /* host stuff */
  int err;                                  /* error trapping */
  float socklib_ver;                        /* socket dll version */
  char * File_Buf;                          /* file buffer */
  char * Rec_Buf;                           /* recieve buffer */

  /* Fill in to suit your own account, increase array sizes if needed */

  char hostname[20] = "pop.wherever.net";   /* hostname for pop server */
  char username[20] = "username";           /* username */
  char password[20] = "password";           /* password */

  int i,j;                                  /* general counters */
  char tempbuf[20] = "";                    /* temp string buffer for parsing */
  int num_flag=0;                           /* string parse flag */
  time_t now;                               /* from time.h */

  /* allocate dynamic storage from the heap */

  File_Buf = malloc(BUF_LEN * sizeof(char) + 1);
  Rec_Buf = malloc(BUF_LEN * sizeof(char) + 1);

  wVersionRequested = MAKEWORD( 1, 1 );

  if ( WSAStartup( wVersionRequested, &wsaData ) != 0 )
    handle_error();

  /* Check socket DLL supports 1.1 or higher */

  socklib_ver = HIBYTE( wsaData.wVersion ) / 10.0;
  socklib_ver += LOBYTE( wsaData.wVersion );

  if ( socklib_ver   {
    printf ("Error: socket library must support 1.1 or greater\n");
    WSACleanup();
    return 0;
  }

  /* get date and time to write to log file */

  time(&now);

  sprintf(File_Buf,"Pop Client Session started %.24s.\n\n", ctime(&now));
  write_file(LOG_FILE | SCREEN, File_Buf);

  /* open a socket based on TCP/IP */

  if ( (sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET )
    handle_error();

  address.sin_family=AF_INET;               /* internet */
  address.sin_port = htons(110);            /* pop port */

  sprintf(File_Buf,"Connecting to %s\n", hostname);
  write_file(LOG_FILE | SCREEN, File_Buf);

  /* DNS on hostname */

  if ( (host=gethostbyname(hostname)) == NULL )
    handle_error();

  /* IP address for hostname */

  address.sin_addr.s_addr=*((unsigned long *) host->h_addr);

  /* try to connect to pop server */

  if ( (connect(sock,(struct sockaddr *) &address, sizeof(address))) != 0)
  handle_error();

  sprintf(File_Buf, "Connected to %s\n", hostname);
  write_file(LOG_FILE | SCREEN, File_Buf);

  err=recv(sock,Rec_Buf,BUF_LEN,0);             /* get response from server */
  sprintf(File_Buf,"received: %s", Rec_Buf);    /* should start +OK */
  write_file(LOG_FILE, File_Buf);

  if ( strstr(Rec_Buf,POP_ERR_MSG) != NULL )    /* check for errors */
    pop_error();

  strcpy(Rec_Buf,"USER ");
  strcat(Rec_Buf, username);
  strcat(Rec_Buf, "\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);     /* send username */
  sprintf(File_Buf,"sent: %s",Rec_Buf);
  write_file(LOG_FILE, File_Buf);

  err=recv(sock,Rec_Buf,BUF_LEN,0);             /* should recieve +OK */
  sprintf(File_Buf,"received: %s", Rec_Buf);
  write_file(LOG_FILE, File_Buf);

  if ( strstr(Rec_Buf,POP_ERR_MSG) != NULL )    /* check for errors */
    pop_error();

  strcpy(Rec_Buf,"PASS ");
  strcat(Rec_Buf, password);
  strcat(Rec_Buf, "\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);     /* send password */
  sprintf(File_Buf,"sent: %s",Rec_Buf);
  write_file(LOG_FILE, File_Buf);

  err=recv(sock,Rec_Buf,BUF_LEN,0);             /* should recieve +OK */
  sprintf(File_Buf,"received: %s",Rec_Buf);
  write_file(LOG_FILE, File_Buf);

  if ( strstr(Rec_Buf,POP_ERR_MSG) != NULL )    /* check for errors */
    pop_error();

  strcpy(Rec_Buf,"STAT\r\n");                  /* send stat command */
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
  sprintf(File_Buf,"sent: %s", Rec_Buf);
  write_file(LOG_FILE, File_Buf);

  /*
   * should recieve +OK x y where x = number of messages
   * and y is total octets. Note the buffers we have allocated
   * would not be sufficient for a real pop client. They
   * should be dynamically allocated here after checking the
   * size of the waiting emails.
   */

  err=recv(sock,Rec_Buf,BUF_LEN,0);
  sprintf(File_Buf,"received: %s", Rec_Buf);
  write_file(LOG_FILE, File_Buf);

  if ( strstr(Rec_Buf,POP_ERR_MSG) != NULL )    /* check for errors */
    pop_error();

  /*
   * Now we need to work out how many emails we have to retrieve
   * and set up a loop to get them all.
   */

  i = j = 0;

  while (i   {
    if (Rec_Buf[i] > '0' && Rec_Buf[i]     {
      while (Rec_Buf[i] != ' ')                   /* space is end of number */
      {
        tempbuf[j] = Rec_Buf[i];
        j++;
        i++;
      }

      num_flag=1;
      tempbuf[j] = '\0';
    }

    i++;
  }

  i = atoi(tempbuf);                            /* number of emails */

  printf("There are %d message(s) waiting\r\n", i);

  /*
   * At this point we should really be checking the length of each email
   * on the POP server and then either allocate dynamic memory, or better
   * still, try synchronised access which we will be doing in a later
   * part of the tutorial. Until then, let's keep it simple. If you do
   * have an email larger than the buffer, it won't do anything else
   * other than crash the program!
   */

  for( j=1; j<=i; j++)
  {
    sprintf(Rec_Buf, "RETR %d\r\n", j);         /* add number */
    printf("Retrieving message number %d\n", j);
    err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
    sprintf(File_Buf,"sent: %s ",Rec_Buf);
    write_file(LOG_FILE, File_Buf);

    err=recv(sock,Rec_Buf,BUF_LEN,0);           /* +OK message will follow */
    sprintf(File_Buf,"received: %s ",Rec_Buf);
    write_file(LOG_FILE, File_Buf);

    if ( strstr(Rec_Buf,POP_ERR_MSG) != NULL )  /* check for errors */
      pop_error();

    err=recv(sock,Rec_Buf,BUF_LEN,0);           /* retrieve the mail */
    sprintf(File_Buf,"%s",Rec_Buf);
    write_file(MAIL_FILE, File_Buf);
  }

  printf ("All done\n\n");

  /* send quit command then clean up */

  strcpy(Rec_Buf,"QUIT\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
  sprintf(File_Buf,"sent: %s", Rec_Buf);
  write_file(LOG_FILE, File_Buf);

  /* deallocate buffer memory */

  free(File_Buf);
  free(Rec_Buf);

  WSACleanup();
  return 0;
}

void handle_error(void)
{
  /*
   * Errors are handled by calling the WSAGetLastError routine which
   * will return the last error as one of the following. As we develop
   * this tutorial, we will go into much more detail on what they mean
   * and what caused them.
   */

  switch ( WSAGetLastError() )
  {
    case WSANOTINITIALISED :
      printf("Unable to initialise socket.\n");
    break;
    case WSAEAFNOSUPPORT :
      printf("The specified address family is not supported.\n");
    break;
    case WSAEADDRNOTAVAIL :
      printf("Specified address is not available from the local machine.\n");
    break;
    case WSAECONNREFUSED :
      printf("The attempt to connect was forcefully rejected.\n");
      break;
    case WSAEDESTADDRREQ :
      printf("address destination address is required.\n");
    break;
    case WSAEFAULT :
      printf("The namelen argument is incorrect.\n");
    break;
    case WSAEINVAL :
      printf("The socket is not already bound to an address.\n");
    break;
    case WSAEISCONN :
      printf("The socket is already connected.\n");
    break;
    case WSAEADDRINUSE :
      printf("The specified address is already in use.\n");
    break;
    case WSAEMFILE :
      printf("No more file descriptors are available.\n");
    break;
    case WSAENOBUFS :
      printf("No buffer space available. The socket cannot be created.\n");
    break;
    case WSAEPROTONOSUPPORT :
      printf("The specified protocol is not supported.\n");
      break;
    case WSAEPROTOTYPE :
      printf("The specified protocol is the wrong type for this socket.\n");
    break;
    case WSAENETUNREACH :
      printf("The network can't be reached from this host at this time.\n");
    break;
    case WSAENOTSOCK :
       printf("The descriptor is not a socket.\n");
    break;
    case WSAETIMEDOUT :
      printf("Attempt timed out without establishing a connection.\n");
    break;
    case WSAESOCKTNOSUPPORT :
       printf("Socket type is not supported in this address family.\n");
    break;
    case WSAENETDOWN :
      printf("Network subsystem failure.\n");
    break;
    case WSAHOST_NOT_FOUND :
      printf("Authoritative Answer Host not found.\n");
    break;
    case WSATRY_AGAIN :
      printf("Non-Authoritative Host not found or SERVERFAIL.\n");
     break;
    case WSANO_RECOVERY :
       printf("Non recoverable errors, FORMERR, REFUSED, NOTIMP.\n");
    break;
    case WSANO_DATA :
      printf("Valid name, no data record of requested type.\n");
    break;
      case WSAEINPROGRESS :
      printf("address blocking Windows Sockets operation is in progress.\n");
    break;
    case WSAEINTR :
      printf("The (blocking) call was canceled via WSACancelBlockingCall().\n");
    break;
    default :
      printf("Unknown error.\n");
     break;
  }

  WSACleanup();
  exit(0);
}

void pop_error(void)
{
  printf("POP Error, Check the Log File for Details\r\n\r\n");

  WSACleanup();
  exit(0);
}

void write_file(int file_type, char *p)
{
  const int LOG_FILE=1;                         /* write to log file */
  const int MAIL_FILE=2;                        /* write to mail file */
  const int SCREEN=4;                           /* display on screen */

  if( (file_type & LOG_FILE) != 0 )             /* add to logs */
  {
    FILE *fp=fopen("poplog.txt","a+");
    fprintf(fp,"%s\n",p);
    fclose(fp);
  }

  if( (file_type & MAIL_FILE) != 0 )            /* add to mailbox */
  {
    FILE *fp=fopen("mailbox.txt","a+");
    fprintf(fp,"%s\n",p);
    fclose(fp);
  }

  if ( (file_type & SCREEN) != 0 )              /* screen */
  {
    printf("%s\n",p);
  }
}


Log File Example after connecting to a POP Server:-

Pop Client Session started Tue Mar 28 22:27:06 2000.

Connecting to pop.freeuk.net

Connected to pop.freeuk.net

received: +OK CPOP v2.3 Ready for Action

sent: USER xxxxxxxx

received: +OK

xxxxxxxx

sent: PASS xxxxxxxx

received: +OK

xxxxxxxx

sent: STAT

received: +OK 1 7165

sent: RETR 1

received: +OK

1

sent: QUIT


Sending Email's with SMTP

Now that we've seen how to retrieve emails from a POP server, we'll take a look at how to send them with SMTP. Once again using TCP/IP. As for the POP client, the program is a basic framework for you to develop further. This will be our last TCP/IP example for a while. Hopefully, the three programs we have presented so far will allow you to develop your own TCP/IP applications by reading the relevant RFC's to allow you to establish and use the required protocol.

Most email programs available for windows won't allow you to modify certain headers and often put in additional header fields you might not want to use. With this program, you can set up the "From:" field if you don't want your real email address to be seen. It is theoretically possible to use it to log onto SMTP servers where you don't have an account and send emails from their server. Many SMTP servers do not check user details, although a large number of UK ISP's do check you are using one of their Dial Up Accounts before letting you use SMTP to protect themselves from email spammers. You might of course be logged in their log files if you aren't careful!

Anyway, let's take an example. The following program sends an email to whoever has been specified in the RCPT TO: field, in this case, it comes to my account. However, when I recieve it it will appear to have come from Bill Clinton and should have been sent to President Putin.

Here's the source:-

/*
 * smtp1.c
 *
 * (c) Mar 2000 by Gandalf
 *
 * A very basic smtp program to send emails from a text file. Try it out by
 * sending an email to one of your mail accounts. Make your own email headers
 * up with this, or add a few that maybe your current email program doesn't
 * allow. This example will send an email to whoever you specify in the RCPT TO
 * field, but will appear to be intended for someone else, and of course it
 * will also appear to come from someone different to yourself!
 * This is a technique often used by spammers. I don't agree with spamming,
 * but the technique can be useful for other purposes.
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include <winsock.h>

#define WIN32_LEAN_AND_MEAN

void handle_error(void);
void smtp_error(void);
void write_file(int file, char *p);

int main (int argc, char *argv[])
{
  const int BUF_LEN=5000;                /* send and recieve buffers */
  const int LOG_FILE=1;                  /* write to log file */
  const int SCREEN=4;                    /* display on screen */

  WORD wVersionRequested;                /* socket dll version info */
  WSADATA wsaData;                       /* socket lib initialisation */
  SOCKET sock;                           /* socket details */
  struct sockaddr_in address;            /* socket address stuff */
  struct hostent * host;                 /* host stuff */
  int err;                               /* error trapping */
  float socklib_ver;                     /* socket dll version */
  char * File_Buf;                       /* file buffer */
  char * Rec_Buf;                        /* recieve buffer */

  /* Change to use your own smtp host */

  char hostname[20] = "relay.where.net"; /* hostname for smtp server */

  time_t now;                            /* from time.h */

  /* allocate dynamic storage from the heap */

  File_Buf = malloc(BUF_LEN * sizeof(char) + 1);
  Rec_Buf = malloc(BUF_LEN * sizeof(char) + 1);

  wVersionRequested = MAKEWORD( 1, 1 );

  if ( WSAStartup( wVersionRequested, &wsaData ) != 0 )
    handle_error();

  /* Check socket DLL supports 1.1 or higher */

  socklib_ver = HIBYTE( wsaData.wVersion ) / 10.0;
  socklib_ver += LOBYTE( wsaData.wVersion );

  if ( socklib_ver   {
    printf ("Error: socket library must support 1.1 or greater\n");
    WSACleanup();
    return 0;
  }

  /* get date and time to write to log file */

  time(&now);
  sprintf(File_Buf,"SMTP Session started %.24s.\n\n", ctime(&now));
  write_file(LOG_FILE | SCREEN, File_Buf);

  /* open a socket based on TCP/IP */

  if ( (sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET )
    handle_error();

  address.sin_family=AF_INET;               /* internet */
  address.sin_port = htons(25);             /* smtp port */

  sprintf(File_Buf,"Connecting to %s\n", hostname);
  write_file(LOG_FILE | SCREEN, File_Buf);

  /* DNS on hostname */

  if ( (host=gethostbyname(hostname)) == NULL )
    handle_error();

  /* IP address for hostname */

  address.sin_addr.s_addr=*((unsigned long *) host->h_addr);

  /* Connect to smtp server */

  if ( (connect(sock,(struct sockaddr *) &address, sizeof(address))) != 0)
    handle_error();

  sprintf(File_Buf, "Connected to %s\n", hostname);
  write_file(LOG_FILE | SCREEN, File_Buf);

  err=recv(sock,Rec_Buf,BUF_LEN,0);             /* get response from server */
  sprintf(File_Buf,"received: %s", Rec_Buf);    /* write to logs */
  write_file(LOG_FILE | SCREEN, File_Buf);

  /*
   * OK, now we send the usual expected string to the smtp host. This
   * should be "HELO whatever.wewant.com\r\n" Some smtp hosts check
   * the domain out properly, but most don't. As long as it checks a
   * rudimentary test to see if it could be a valid domain, the smtp
   * host will usually accept it!
   */

  sprintf(Rec_Buf, "HELO microsoft.com\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);     /* send expected response */
  write_file(LOG_FILE | SCREEN, Rec_Buf);

  err=recv(sock,Rec_Buf,BUF_LEN,0);             /* get response from server */
  sprintf(File_Buf,"received: %s", Rec_Buf);    /* write to logs */
  write_file(LOG_FILE | SCREEN, File_Buf);

  sprintf(Rec_Buf, "MAIL FROM: bclinton@whitehouse.gov\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);     /* send who it's from */
  write_file(LOG_FILE | SCREEN, Rec_Buf);

  err=recv(sock,Rec_Buf,BUF_LEN,0);             /* get response from server */
  sprintf(File_Buf,"received: %s", Rec_Buf);    /* write to logs */
  write_file(LOG_FILE | SCREEN, File_Buf);

  sprintf(Rec_Buf, "RCPT TO: gandalf@dhamma.org.uk\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);     /* send who it's to. This is */
  write_file(LOG_FILE | SCREEN, Rec_Buf);       /* the last required field */

  err=recv(sock,Rec_Buf,BUF_LEN,0);             /* get response from server */
  sprintf(File_Buf,"received: %s", Rec_Buf);    /* write to logs */
  write_file(LOG_FILE | SCREEN, File_Buf);

  sprintf(Rec_Buf, "DATA\r\n");                 /* Send a DATA command which */
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);     /* tells the server that all */
  write_file(LOG_FILE | SCREEN, Rec_Buf);       /* else we send is going to
                                                 * be our message with headers
                                                 * and the mesaage body.
                                                 */

  err=recv(sock,Rec_Buf,BUF_LEN,0);             /* get response from server */
  sprintf(File_Buf,"received: %s", Rec_Buf);    /* write to logs */
  write_file(LOG_FILE | SCREEN, File_Buf);

  /*
   * OK. At this point we can add as many headers as we want. Check out
   * the RFC's for all common headers
   */

  sprintf(Rec_Buf, "From: Bill Clinton <bclinton@whitehouse.gov>\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
  write_file(LOG_FILE | SCREEN, Rec_Buf);

  sprintf(Rec_Buf, "To: Putin <putin@kremlin.ru>\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
  write_file(LOG_FILE | SCREEN, Rec_Buf);

  sprintf(Rec_Buf, "Subject: Congratulations\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
  write_file(LOG_FILE | SCREEN, Rec_Buf);

  /* Done with headers, so let's add the message body */

  sprintf(Rec_Buf, "\r\nDear President Putin\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
  write_file(LOG_FILE | SCREEN, Rec_Buf);

  sprintf(Rec_Buf, "\r\nCongratulations on your recent appointment ");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
  write_file(LOG_FILE | SCREEN, Rec_Buf);

  sprintf(Rec_Buf, "to President of Russia\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
  write_file(LOG_FILE | SCREEN, Rec_Buf);

  sprintf(Rec_Buf, "\r\nI remain, your most faithful servent\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
  write_file(LOG_FILE | SCREEN, Rec_Buf);

  sprintf(Rec_Buf, "\r\nBill");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
  write_file(LOG_FILE | SCREEN, Rec_Buf);

  /*
   * Once we have finished, we send a CRLF followed by a full stop and
   * a second CRLF to tell the smtp host we have completed the message, which
   * will then get queued and sent.
   */

  sprintf(Rec_Buf, "\r\n.\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);
  write_file(LOG_FILE | SCREEN, Rec_Buf);

  err=recv(sock,Rec_Buf,BUF_LEN,0);             /* get response from server */
  sprintf(File_Buf,"received: %s", Rec_Buf);    /* write to logs */
  write_file(LOG_FILE | SCREEN, File_Buf);

  sprintf(Rec_Buf, "QUIT\r\n");
  err=send(sock,Rec_Buf,strlen(Rec_Buf),0);     /* send a quit command */
  write_file(LOG_FILE | SCREEN, Rec_Buf);
  err=recv(sock,Rec_Buf,BUF_LEN,0);             /* get response from server */
  sprintf(File_Buf,"received: %s", Rec_Buf);    /* write to logs */
  write_file(LOG_FILE | SCREEN, File_Buf);

  /*
   * Off course, if you wanted to use this as a genuine email posting
   * program, you would use the mail spool file we generated from our
   * last program, and would pick up the relevent headers to reply.
   * The message could be edited using a standard text editor, then read
   * in by this program. We've kept it simple though so you can see exactly
   * what is happening.
   */

  /* deallocate buffer memory */

  free(File_Buf);
  free(Rec_Buf);

  WSACleanup();
  return 0;
}

void handle_error(void)
{
  /* Cut and paste in the same error handler as used in the POP program */
}

void smtp_error(void)
{
  printf("SMTP Error, Check the Log File for Details\r\n\r\n");

  WSACleanup();
  exit(0);
}

void write_file(int file_type, char *p)
{
  const int LOG_FILE=1;                         /* write to log file */
  const int SCREEN=4;                           /* display on screen */

  if( (file_type & LOG_FILE) != 0 )             /* add to logs */
  {
    FILE *fp=fopen("poplog.txt","a+");
    fprintf(fp,"%s\n",p);
    fclose(fp);
  }

  if ( (file_type & SCREEN) != 0 )              /* screen */
  {
    printf("%s\n",p);
  }
}

Okay, let's take a look at the log file (smtplog.txt):-

SMTP Session started Wed Mar 29 00:35:41 2000.

Connecting to relay.freeuk.net

Connected to relay.freeuk.net

received: 220 scrabble.freeuk.net ESMTP Exim 3.12 #1 Wed, 29 Mar 2000 00:34:12 +0100

HELO whitehouse.gov

received: 250 scrabble.freeuk.net Hello whitehouse.gov [212.126.154.153]

34:12 +0100

MAIL FROM: bclinton@whitehouse.gov

received: 250 <bclinton@whitehouse.gov> is syntactically correct

154.153]

34:12 +0100

RCPT TO: gandalf@dhamma.org.uk

received: 250 <gandalf@dhamma.org.uk> is syntactically correct

154.153]

34:12 +0100

DATA

received: 354 Enter message, ending with "." on a line by itself

.153]

34:12 +0100

From: Bill Clinton <bclinton@whitehouse.gov>

To: Putin <putin@kremlin.ru>

Subject: Congratulations

Dear President Putin

Congratulations on your recent appointment

to President of Russia


I remain, your most faithful servent


Bill

.

received: 250 OK id=12a5Uy-0004ku-00

ul servent

QUIT

received: 221 scrabble.freeuk.net closing connection


Using UDP/IP

So far we've looked at using TCP/IP, which as I mentioned in part one of the tutorial, is a STREAMING protocol, ie it uses handshaking. You send data and it is acknowledged by the other end prior to you sending more. This way, if packets get lost or become corrupted, the other end can notify you to resend the packet. We're now going to take a look at UDP/IP which is a DATAGRAM protocol. You send out a packet and never bother to check if it gets to it's destination. Obviously this is a less reliable protocol, but is much faster. There aren't too many services running over the Internet using UDP. However, we'll develop an example to illustrate how to use it. I did have in mind using TFTP as an example, but we need to understand the UDP protocol in depth to be able to use this. So, we'll take a simple one. You probably won't find a server out there on the Internet which offer's an echo service, but if you have a Linux Box, then enable port 7 in inetd.conf to try this out.

All it does is send a string via UDP/IP and receives an echo of the same string back from the server. Here's the code :-

/*
 * ech.c
 *
 * (c) Mar 2000 by Gandalf
 *
 * A simple program to illustrate UDP/IP. Server's don't usually
 * run an echo service as it can be used for DoS attacks. However,
 * if you have access to a Linux Box, you can enable this service
 * in your inetd.conf to try it.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock.h>

#define WIN32_LEAN_AND_MEAN

void handle_error(void);

int main (int argc, char **argv)
{
  WORD wVersionRequested;             /* socket dll version info */
  WSADATA wsaData;                    /* data for socket lib initialisation */
  SOCKET sock;                        /* socket details */
  const int BUF_LEN=100;              /* Buffer size for transfers */
  struct sockaddr_in address;         /* socket address stuff */
  int err;                            /* error trapping */
  float socklib_ver;                  /* socket dll version */
  char * Buf;                         /* send buffer */
  char * Rec_Buf;                     /* receive buffer */

  wVersionRequested = MAKEWORD( 1, 1 );

  /*
   * We need to call the WSAStartup routine BEFORE we try to use any of
   * the Winsock dll calls.
   */

  if ( WSAStartup( wVersionRequested, &wsaData ) != 0 )
    handle_error();

  /* Check socket DLL supports 1.1 or higher */

  socklib_ver = HIBYTE( wsaData.wVersion ) / 10.0;
  socklib_ver += LOBYTE( wsaData.wVersion );

  /* Find out what our maximum IP packet size is for datagrams and allocate
   * our buffers
   */

  Buf = malloc(wsaData.iMaxUdpDg * sizeof(char) + 1);
  Rec_Buf = malloc(wsaData.iMaxUdpDg * sizeof(char) + 1);

  if ( socklib_ver   {
    printf ("\nError: socket library must support 1.1 or greater.\n");
    WSACleanup();    /* clean up before exit */
    exit(0);
  }

  /*
   * Open a socket. The AF_INET parameter tells windows we want to use the
   * internet. Other parameters for different networking can be chosen e.g.
   * for netbios, IPX etc. The SOCK_DGRAM parameter lets windows know we want
   * to use UDP rather than TCP, and the final parameter will always be
   * zero for what we want to do and tells windows to use whatever
   * default communication protocol has been established (eg PPP and IP)
   */

  if ( (sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET )
    handle_error();

  /* We now need to initialise a couple of variables in the address
   * structure. Once again, to tell windows we are using the internet,
   * and also what port we want to use when connecting to a remote
   * computer. In this case it is port 7 which is the standard port for
   * echo. The htons routine is used to convert the way Intel chips
   * store data in memory, which is different compared to many other computers.
   * The standard is based on Motorola format.
   */

  address.sin_family=AF_INET;            /* internet */
  address.sin_port = htons(7);           /* port 7 for the echo server */

  /*
   * The last example showed using a host name, and used the winsock
   * dll routine gethostbyname to resolve an IP address. This example
   * shows how to use an IP address direct using the inet_addr routine.
   */

  address.sin_addr.s_addr=inet_addr("192.168.19.3");

  /* Now we're ready to actually connect to the echo server itself */

  if ( (connect(sock,(struct sockaddr *) &address, sizeof(address))) != 0)
    handle_error();

  /*
   * We should be connected to the echo server at this point and whatever
   * we send to it should be echoed back. We use sendto and recvfrom with
   * UDP/IP.
   */

  strcpy(Buf, "Hi There Mr Echo!");
  printf("\nSent: %s\n", Buf);
  err=sendto(sock,Buf,BUF_LEN,0,(struct sockaddr *)&address,sizeof(address));
  err=sizeof(address);
  recvfrom(sock,Rec_Buf,BUF_LEN,0,(struct sockaddr *)&address,&err);
  printf("\nReceived: %s\n", Rec_Buf);

  /* deallocate buffer memory */

  free(Buf);
  free(Rec_Buf);

  /* Always call WSACleanup before exiting */

  WSACleanup();
  exit(0);
}

void handle_error(void)
{
  /* Cut and paste in the same error handler as used in the POP program */
}


Conclusion and What's Next

So, we are moving a bit closer towards programming our own hacking applications. We hopefully fully understand TCP/IP and UDP/IP by now, or at least enough to write our own programs.

I fully intended to introduce ICMP at this point with our own versions of PING and TRACEROUTE. However, after developing them, I realised that for the reader of this tutorial to understand how they work would require a greater knowledge of the IP protocol, so we'll be leaving these examples until part three of the tutorial.

We will be looking at the IP protocol in depth in the next part of the tutorial. In fact, we will be taking a very deep look at all of the protocols. It's going to be pretty heavy going as we can't really go much further without getting into protocols to a bit level. So get prepared.

As usual, we will be presenting a few programs to illustrate this. We will be developing a logging program to watch out for TCP, UDP and ICMP activity on your own computer. We will also expand this further to allow us to monitor the protocols to the bit level.

It will eventually grow into a powerful firewall by the end of the tutorial.

Hope you're enjoying things so far!

As usual, send any greets, complaints or whatever to gandalf@dhamma.org.uk

The Rota

BlueCrab Ltd