Jun 2009
In part 1 an examination of
creating an ultra simple single port single host port check program
was done. Part two looks at initial code reorganizing, a simple
Makefile then addresses some of the potential problems
with the bare bones version. Following is a recap of what needs to
be fixed:
additionally there are some features it could use:
After looking at the code; it made the most sense to break up the single program into at least one module then distribute where each of the changes should go:
main.cportcheck.cNote in this text only the bugs and some reorganization will be addressed. Here is the current full source listing for reference, in the text only affected sections will be referenced then at the end another full source listing with the changes made:
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
u_short port; /* user specified port number */
char addr[1023]; /* address */
struct sockaddr_in address; /* address structures */
short int sock = -1; /* the socket descriptor */
port = atoi(argv[1]);
addr = strncpy(addr, argv[2], 1023);
bzero((char *)&address, sizeof(address)); /* init addr struct */
address.sin_addr.s_addr = inet_addr(addr); /* assign the address */
address.sin_port = htons(port); /* translate int2port num */
/*
* Three simple steps:
* 1. Open the master socket locally
* 2. Try to connect to hostbyport, if it works
* print the successful message.
* 3. If no route then complain with vulgarity
* (it is just a rapid prototype after all)
* Otherwise do nothing.
*/
sock = socket(AF_INET, SOCK_STREAM, 0);
if(connect(sock,(struct sockaddr *)&address,sizeof(address)) == 0)
printf("%i is open on %s\n", port, argv[2]);
if (errno == 113) fprintf(stderr, "F*^k - no route to host\n");
close(sock);
return 0;
}
Before there are any major changes time to make the simplest
change to cut down on the full source listing: no further extra
header files will be needed so the includes can me moved into their
own local header then referenced within the source build directory.
To accomplish this task it is time to move portcheck.c
into it's own directory and split the headers:
mkdir portcheck/ mv sourcefile.c portcheck/portcheck_main.c cp *main.c portcheck.h vi *
The file portcheck.h should only have the following
in it:
#ifndef _PORTCHECK_H #define _PORTCHECK_H #include <sys/socket.h> #include <sys/time.h> #include <sys/types.h> #include <arpa/inet.h> #include <netinet/in.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <netdb.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #endif
and now portcheck_main.c has only:
#include "portcheck.h"
int main(int argc, char **argv)
{
u_short port; /* user specified port number */
char addr[1023]; /* address */
short int sock = -1; /* the socket descriptor */
struct hostent *host_info; /* host info structure */
struct sockaddr_in address; /* address structures */
port = atoi(argv[1]);
addr = strncpy(addr, argv[2], 1023);
bzero((char *)&address, sizeof(address)); /* init addr struct */
address.sin_addr.s_addr = inet_addr(addr); /* assign the address */
address.sin_port = htons(port); /* translate int2port num */
/*
* 1. Open the master socket locally
* 2. Connect to hostbyport works? print success
* 3. No route to host? complain with vulgarity
*/
sock = socket(AF_INET, SOCK_STREAM, 0);
if(connect(sock,(struct sockaddr *)&address,sizeof(address)) == 0)
printf("%i is open on %s\n", port, argv[2]);
if (errno == 113) fprintf(stderr, "F*^k - no route to host\n");
close(sock);
return 0;
}
Note that a few things changed; the variable declaration order for starters. This is a styling choice, sometimes ordering by size then alpha makes a compile run faster. Although the program can still be compiled using:
cd ~portcheck/
cc portcheck_main.c -o portcheck
Now is a good time to draft a simple Makefile for
the project; this will save time later on. In the source directory
a very simple Makefile will suffice:
CC=cc
all: portcheck
portcheck:
${CC} $@_main.c -o $@
clean:
rm -f a.out portcheck
rebuild: clean portcheck
So now all that has to be done is:
cd ~portcheck
make
With the preceding changes in place it is time to tackle the bugs.
First up is a usage message, instead of setting up an option
switcher just catching it first and exiting is the simplest
approach. A define of the usage message can be used so
the message can be reused in the input validation:
...
#define USAGE "Usage: portcheck port address"
int main(int argc, char **argv)
...
char addr[1023];
if (argv[1])
if (!strcmp(argv[1], "-u")) {
printf("%s\n", USAGE);
return 0;
}
...
Next: input validation, since the program will eventually accept hostnames as well it will check for the proper number of arguments immediately after the usage help code:
...
if (argv[1])
port = atoi(argv[1]);
else {
fprintf(stderr, "No port specified\n");
fprintf(stderr,"%s\n", USAGE);
return 1;
}
if (argv[2])
strncpy(addr, argv[2], 1023);
else {
fprintf(stderr, "No address specified\n");
fprintf(stderr,"%s\n", USAGE);
return 1;
}
...
Note that the usage message is also handled in the same step.
Time to make sure that a socket can even be assigned; this is
pretty simple since if the assignment fails the value will be
-1; all that needs to be done is to check on the value
immediately after assigning sock:
...
*/
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
fprintf(stderr, "Error: could not assign master socket\n");
exit (1);
}
...
So far so good and not too complicated. Although it is difficult to display the socket failure here is some example output of the input validation and usage message:
make rebuild rm -f a.out portcheck cc portcheck_main.c -o portcheck ./portcheck -u Usage: portcheck port address echo $? 0 ./portcheck Syntax error Usage: portcheck port address echo $? 1 ./portcheck 22 Syntax error Usage: portcheck port address echo $? 1 ./portcheck 22 192.168.1.3 22 is open on 192.168.1.3 echo $? 0
With the changes now complete the portcheck_main.c
file looks like:
#include "portcheck.h"
#define USAGE "Usage: portcheck port address"
int main(int argc, char **argv)
{
u_short port; /* user specified port number */
short int sock = -1; /* the socket descriptor */
struct hostent *host_info; /* host info structure */
struct sockaddr_in address; /* address structures */
char addr[1023];
if (argv[1])
if (!strcmp(argv[1], "-u")) {
printf("%s\n", USAGE);
return 0;
}
if (argv[1])
port = atoi(argv[1]);
else {
fprintf(stderr, "No port specified\n");
fprintf(stderr,"%s\n", USAGE);
return 1;
}
if (argv[2])
strncpy(addr, argv[2], 1023);
else {
fprintf(stderr, "No address specified\n");
fprintf(stderr,"%s\n", USAGE);
return 1;
}
bzero((char *)&address, sizeof(address)); /* init addr struct */
address.sin_addr.s_addr = inet_addr(addr); /* assign the address */
address.sin_port = htons(port); /* translate int2port num */
/*
* 1. Open the master socket locally
* 2. Connect to hostbyport works? print success
* 3. No route to host? complain with vulgarity
*/
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
fprintf(stderr, "Error: could not assign master socket\n");
exit (1);
}
if(connect(sock,(struct sockaddr *)&address,sizeof(address)) == 0)
printf("%i is open on %s\n", port, argv[2]);
if (errno == 113) fprintf(stderr, "F*^k - no route to host\n");
close(sock);
return 0;
}
With the three main potential problems solved the next text in
the series will address the improvements and breaking out the
actual check code from the main() function. To recap;
the improvements are: