Listening via Linux

by Solthae

Greeting, I bring you some simple C code that, when compiled, sets up a simple server on your system listening on a port of your request.  But first...

Why did I code this and send it away?  Without getting too long-winded, I simply wanted to provide an appetizer to the world of Linux network programming I've been getting into over the last year or so.  The texts I've read and the projects I've worked on have kept me reading and continuing them (not always common).  I'm hoping to turn on new people interested who've not yet had any neat code to play with.  I figured that the best way I could do that was by providing the most basic of code that would also be useful and entertaining.  The result: my simple 'listener.cc'.  Besides I love to see code in 2600.

What does listener do?  listener listens on whatever machine it is executed on (provided '&' to run in the background), waiting and listening (that'sthree) for connections to the specified port.  For example:

solthae@mars$> ./listener 2600 &

then:

solthae@mars$> telnet localhost 2600

will connect you to the listener program.

What happens afterwards is up to you.  How is that up to me?  You modify listener to so something other than what I provided by editing the code at the bottom of the 'for' loop.  You'll see:

while (fgets(buf, sizeof bug, rStream)) { ...

This continues to receive requests (for Telnet, requests are whatever was typed before pressing 'Enter') and storing them in the char buf[].  At that point you can process them at will.  Hopefully at this point the opportunities are beginning to come to you (your own personal <blank> server, making your own honeypot to stick on the Telnet port, perhaps begin work on a MUD, a joke-of-the-day echo server, etc.).

Since that's basically the whole shebang I'll leave you here.  I have faith in your intelligence and also didn't want to bore you with attempting to explain what the various strange calls are doing (socket(2), listen(2), bind(2), etc.).

Instead I left you with a program that doesn't support somethingas vital as multiple clients (see fork(2)).  I also hard-coded the families used, the specified services, and other goodies (such as broadcasting and general UDP which are not "hard-coded" but "not-coded").  These are for you to learn on your own can come highly recommended as interesting subjects to take up study (especially as just a hobby).

This, I hope, will send you out of your dark room or (unfortunately) deeper into the Internet to find out socket programming information.  Besides, it takes time to explain the whole concept (that's what books are for) as well as the specifics.  So just read the comments and the verbose variable names to follow along.  Either way I hope you enjoy the code (questions, comments, bitches, complaints to Dear 2600, I'll address them there).

Primary sources (not just the net):

TCP/IP Sockets in C by Donahoo & Calvert

Linux Socket Programming by Example by Warren W. Gay

Shout outs: The 2600tucson crew, Ashley, Noam Chomsky, Robitussin, Modest Mouse.


//*****************************************************************************
//
// listener.cc
//
// by solthae
// Simple server code that allows for remote connections.
// Can have various uses (honeypot, listener, mud server, etc.).
// I've hardcoded it to run on localhost with no specific service being run,
// in hopes that those wishing to mod it for multiple clients, specific
// services, etc. will follow up and learn more on their own.
//
// Compile: g++ -o listener listener.cc
//
// Usage: ./listener 2600 &
//
// This will leave a process running as seen with 'ps,' listening for 
// connections on port 2600.
//*****************************************************************************

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

// error() reports an error then exits program
void error(const char *err) {
	perror(err);
	exit (1);
}

int main(int argc, char **argv) {
	int z;
	int x;
	struct sockaddr_in serverAddress;  // AF_INET family
	struct sockaddr_in clientAddress;  // AF_INET family
	unsigned short portNumber;
	FILE *rStream = NULL;              // Read stream
	FILE *wStream = NULL;              // Write stream
	int s;                             // Socket
	int c;                             // Client socket
	char buf[4096];                    // IO buffer
	socklen_t addrlen;                 // For accept(2) when using g++

	// Check for correct argument usage
	if (argc != 2) {
		fprintf(stderr, "Usage: %s <port number>\n", argv[0]);
		exit (1);
	}

	// Assign supplied argument as port number
	portNumber = atoi(argv[1]);

	// Create a TCP/IP socket to use
	if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1 )
			error("socket(2)");
	
	// Fill in local address structure, that'd be our server address
	memset(&serverAddress, 0, sizeof(serverAddress));  // Clear out struct
	serverAddress.sin_family = AF_INET;                // Internet address family
	serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); // Any incoming interface
	serverAddress.sin_port = htons(portNumber);        // Local port to use

	// Bind to server address
	if ((z = bind(s, (struct sockaddr *) &serverAddress, sizeof(serverAddress))) == -1)
		error("bind(2)");

	// Make it a listening socket
	if ((z = listen(s, 10)) == -1)
		error("listen(2)");

	// The server loop
	for (;;) {
		// Wait for a connection
		addrlen = sizeof(clientAddress);

		if ((c = accept(s, (struct sockaddr *) &clientAddress, &addrlen)) == -1)
			error("accept(2)");

		// The read stream is where the clients requests are going to be coming in
		// through (don't mix them up)
		// Create read stream
		if (!(rStream = fdopen(c, "r"))) {
			close(c);
			continue;
		}

		// The write stream in where you are going to print you message (like requests)
		// to the client (don't mix them up)
		// Create write stream
		if (!(wStream = fdopen(dup(c), "w"))) {
			fclose(rStream);
			continue;
		}

		// Set both streams to line buffered mode
		setlinebuf(rStream);
		setlinebuf(wStream);

		printf("-------------------------------------\n");
		printf("- Put a telnet message here for fun -\n");
		printf("-------------------------------------\n");

		//	*** NOTE TO READERS ***
		// This is the main workhorse of the code.
		// This takes requests from the client through the read stream rStream.
		// You then can process these 'requests' (i.e., send text, etc.)
		// as a 'char buf[]' (i.e., string).
		//
		// Below:
		// Process 1 echo's sent command.
		// Process 2 prints 'stringlen'.
		// Process 3 goes through 'buf' one-by-one printing the chars.
		// Enjoy making creative ways to process 'buf' from different clients.
		//
		// Process clients requests
		while (fgets(buf, sizeof buf, rStream)) {
			printf("\nEcho: %s", buf);          // Process 1
			printf("\nSize: %d", strlen(buf));  // Process 2

			for (x = 0; x < strlen(buf); x++)
				printf("\n%c", buf[x]);
		}

		// Close client's connection
		fclose(wStream);
		shutdown(fileno(rStream), SHUT_RDWR);
		fclose(rStream);

	}

	// If control gets here there's a problem with time/space
	return 0;
}

Code: listener.cc

      

Return to $2600 Index