Socket Programming for Fun and Profit

by Darknite (darknite@kurir.net)

First of all, this is no article for experts since I'm no expert myself on either TCP/IP or C.  So all of those already familiar with the basics of socket programming may stop reading right now.  And by the way, I am not responsible for any actions taken due to the information within this article.  If you can't take responsibility for your own actions, what makes you think anyone else can?  The reason for me writing this article was both to learn and to give some useful and creative information to all hackers/wannabes around the globe.  Because even if every hacker doesn't write their own programs, they should be able to do so and understand the basics of them.  Our goal in the article will be to create a port scanner.  Simple and clean with just hostname lookup and no extra functions.

This article assumes some basic C programming skills from the reader along with some basic knowledge and understanding of the TCP/IP protocol.

Finding the Host

First of all, we'll have to ask ourselves "How does a port scanner work?"  The first thing a port scanner does is check the number of arguments given to the program.  Since I suppose you all know how to do that in C, I will skip the code for it.  After that it will take the hostname, (argv[1]), to see if it's valid.  We will use the gethostbyname(3) to process the given argument:

struct hostent *host = gethostbyname(argv[1]);

The definition of hostent is found in <netdb.h>, (as is the definition for gethostbyname()), and looks like this:

  /*
   * Structures returned by network data base library.  All addresses are
   * supplied in host order, and returned in network order (suitable for
   * use in system calls).
   */
struct hostent {
  char *h_name;                  /* official name of host */
  char **h_aliases;              /* alias list */
  int h_addrtype;                /* host address type */
  int h_length;                  /* length of address */
  char **h_addr_list;            /* list of addresses from name server */
  #define h_addr h_addr_list[0]  /* address, for backward compatiblity */
};

This means that our IP number for the host given in argv[1] is stored in host->h_name.

Let's write a little test program, getip.c:

#include <netdb.h>

void main(int argc, char **argv)
{
  struct hostent *victim;
  if (argc < 2) {
    printf("use with host as argument.\n");
    exit(-1);
  }

  victim = gethostbyname(argv[1]);
  if (!victim) {
    herror(argv[1]);
    exit(-1);
  }
  printf("%u.%u.%u.%u\n", (unsigned char) victim->h_addr[0]
         , (unsigned char) victim->h_addr[1]
         , (unsigned char) victim->h_addr[2]
         , (unsigned char) victim->h_addr[3]);
}

As you can see, all four segments of the IP number is stored into a separate byte.  So, now we have the target host's adress.  What should we do next?

Establish Connections

A brief description of what really happens when you connect via TCP/IP to a remote host is in order.

First of all, you initiate a socket - let's call it S.  This socket allocates a free port on your computer.  (It is the endpoint of the connection.)  Once you have initiated a socket on your computer, you can tell that socket to connect to a port on a host.  Let's say we would want to connect to the website at l0pht.com, here is what would happen (skipping nslookup):

  1. Initiate socket S.  (Get a free port, let's say you got 2222)
  2. Use S to connect to www.l0pht.com, port 80.
  3. In theory, your connection would look like: S --> www.l0pht.com:80
  4. But in reality, this is just: yourhost:2222 --> www.l0pht.com:80

So what we need to do is to create a socket and tell it where to connect.

A port scanner connects to every port between a specified range on a host to see which ports (services) are opened and which ones are closed.  Let's start to take a look at how to code this.

To create the socket we will use the function socket(2).  Here's the definition of socket(2):

int socket(int domain, int type, int protocol);

It returns an integer above zero (which is the socket handler), upon success or "-1" if it fails to create a socket.

Example usage of socket(2) is:

S = socket(AF_INET, SOCK_STREAM, 0)

The AF_INET is the ARPA Internet Protocol and the one we will use.  The type we will be using is SOCK_STREAM which provides a two-way connection based byte stream.

The protocol argument will not be used and is therefor set to "0", due to that most of the times there only exists one protocol to support the particular socket type within the protocol family.

The required header files for socket(2) and connect(2) is <sys/types.h> and <sys/socket.h>.

After the socket is created we will use the connect(2) to establish the connection to the target host.  The definition for connect(2):

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

sockfd is the socket descriptor/handler.  (S in our example).

Instead of the struct sockaddr we will use the struct sockaddr_in, (so also include <netinet/in.h>).

struct sockaddr_in looks like this:

/* Structure describing an Internet (IP) socket address. */
#define __SOCK_SIZE__ 16      /* sizeof(struct sockaddr)      */

struct sockaddr_in {
  short int sin_family;         /* Address family               */
  unsigned short int sin_port;  /* Port number                  */
  struct in_addr sin_addr;      /* Internet address             */

  /* Pad to size of 'struct sockaddr'. */
  unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)];
};

struct in_addr {
  __u32 s_addr;
};

By what you can see above, the sin_addr.s_addr is just an unsigned 32-bit number to represent the IP number (for example, 0x7F000001 is 127.0.0.1).

So how do we convert the result in host->h_addr given by gethostbyname(3)?

Easy, we'll just cast the host->h_addr with:

*(long *)(host->h_addr)

Finally, don't forget to use the htons(3) to convert it to reverse byte order on x86!

And the addrlen argument is just sizeof(sockaddr).

We will have to cast our sockaddr_in variable to a struct sockaddr when passing it to connect(2).

And of course, one final thing, don't forget to close down your socket.  Use close(2) with your socket as argument.

Like: close(s).  (The definition of close(2) is found in <unistd.h>.)

Writing the Code

Now your fingertips are itching to get down to business.  Don't let me hold you back.

You should, without problems, be able to write a port scanner or anything else - only your imagination sets the limit.

No guide is complete without that final piece of source code, so here it is:

portscan.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>

#define START 1
#define STOP 1024

void main(int argc, char **argv)
{
  int s, port;
  struct hostent *host;
  struct sockaddr_in victim;

  printf("PortScan v1.0 - By darknite[@brigade.dhs.org]\n");
  printf("For his socket-programming article, 1998\n");
  if (argc < 2) {
    printf("Usage: %s <hostname>\n", argv[0]);
    exit(-1);
  }

  host = gethostbyname(argv[1]);
  if (!host) {
    herror(argv[1]);
    exit(-1);
  }

  victim.sin_family = AF_INET;
  victim.sin_addr.s_addr = *(long *) (host->h_addr);

  for (port = START; port <= STOP; port++) {
    victim.sin_port = htons(port);
    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0) {
      printf("Error creating socket.\n");
      exit(-1);
    }

    if (!connect(s, (struct sockaddr *) &victim, sizeof(victim)))
      printf("port: %i\n", port);
    if (close(s)) {
      printf("Error closing socket.\n");
      exit(-1);
    }

  }
}

As I have said before, I am no expert on socket programming nor TCP/IP communication.  But I beleive this should be enough for anyone to get started with socket programming and to write some handy tools.  Since I only use Linux, everything in this article have been tested under Linux only, but I beleive that it should work fine on all other UNIX systems too.  (You might have noticed that when introducing a new function I have included the man section number for that function - use man as frequently as possible.)

Good luck with your programming.

Code: getip.c

Code: portscanner.c

Return to $2600 Index