Projects/Software/TunTapIO/rawsock.c

From Qontrol.nl Wiki
Jump to: navigation, search
/**********************************************************************
* rsio - raw socket to stdin/out proxy                                *
* Written by: Ivo Smits <Ivo@UFO-Net.nl>                              *
*                                                                     *
* Compile using: gcc rawsock.c -o rsio                                *
*                                                                     *
* Many thanks to:                                                     *
*  - http://www.blug.linux.no/rfc1149/                                *
*  - http://linux.about.com/od/commands/l/blcmdl2_select.htm          *
*  - http://bochs.sourceforge.net/ (the iodev/eth_linux.cc module)    *
*                                                                     *
**********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#ifndef HAVE_NETINET_IN_H
	#include <netinet/in.h>
#endif

#include <sys/ioctl.h>

#include <errno.h>
#include <dirent.h>

#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <netinet/in.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <linux/types.h>
#include <linux/filter.h>
#include <linux/if_ether.h>

// The main program, this is where all the magic happens
int main(int argc, char** argv) {
	int DumpStats = 1;
	int IncludePLen = 1;

	//Parse command line arguments
	if (argc > 1) {
		if (strcmp(argv[1], "-h") == 0) {
			fprintf(stderr, "rsio - raw socket to stdio proxy\n");
			fprintf(stderr, "Usage: %s INTF [-raw|-ip] [CLEN] [-prom]\n", argv[0]);
			fprintf(stderr, "  INTF:  the name of the network interface to connect to\n");
			fprintf(stderr, "  -raw:  raw ethernet communication (default)\n");
			fprintf(stderr, "  -tun:  IP level communication\n");
			fprintf(stderr, "  CLEN:  capture size (should be the same as the mtu, default: %d)\n", ETH_FRAME_LEN);
			fprintf(stderr, "  -prom: enable promiscious mode (warning, won't be disabled)\n");
			fprintf(stderr, "Note that the arguments should be in exactly THIS order.\n");
			fprintf(stderr, "Report bugs to <Ivo@UFO-Net.nl>\n");
			exit(0);
		}
	} else {
		//Application won't run without arguments
		fprintf(stderr, "Try: %s -h\n", argv[0]);
		exit(1);
	}

	int sock; //The socket

	int CaptureLen = ETH_FRAME_LEN;
	{
		int linktype = 0; //Default to ethernet packets (0=ethernet, 1=IP)
		if (argc > 2 && strcmp(argv[2], "-ip") == 0) linktype = 1;

		if (linktype == 1) CaptureLen = ETH_DATA_LEN;

		//Different capture length?
		if (argc > 3) CaptureLen = atoi(argv[3]);
		if (CaptureLen < 1) {
			fprintf(stderr, "Capture length %d invalid!\n", CaptureLen);
			exit(1);
		}
		fprintf(stderr, "Max packet length: %d\n", CaptureLen);

		//Open the socket!
		int socktype, sockproto;
#warning "ToDo: add some more protocols from /usr/include/linux/if_ether.h!"
		if (linktype == 1) {
			fprintf(stderr, "Warning: IP mode does not catch sent packets!\n");
#warning "IP mode does not catch sent packets!"
			socktype = SOCK_DGRAM;
			sockproto = htons(ETH_P_IP);
		} else {
			socktype = SOCK_RAW;
			sockproto = htons(ETH_P_ALL);
		}
		if ((sock = socket(PF_PACKET, socktype, sockproto)) == -1) {
			fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
			exit(1);
		}
	}

	//Bind to given interface by name
	{
		const char *dev; //Device to connect to
		strcpy(dev, argv[1]);

		//Translate interface name to index
		struct ifreq ifr;
		memset(&ifr, 0, sizeof(ifr));
		strcpy(ifr.ifr_name, dev);
		if (ioctl(sock, SIOCGIFINDEX, &ifr) == -1) {
			fprintf(stderr, "Failed to get index for interface: '%s'\n", dev);
			close(sock);
			exit(1);
		}

		// Bind to given interface
		struct sockaddr_ll sll;
		memset(&sll, 0, sizeof(sll));
		sll.sll_family = AF_PACKET;
		sll.sll_ifindex = ifr.ifr_ifindex;
		if (bind(sock, (struct sockaddr *)&sll, (socklen_t)sizeof(sll)) == -1) {
			fprintf(stderr, "Could not bind to interface '%s': %s\n", dev, strerror(errno));
			close(sock);
			exit(1);
		}

#warning "Disable promiscious mode on exit! (signal handler?)"
		if (argc > 5 && strcmp(argv[5], "-prom") == 0) {
			register int Ret;
			if (Ret = ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
				fprintf(stderr, "Failed to get interface flags: '%s'\n", Ret);
				close(sock);
				exit(1);
			}
			ifr.ifr_flags |= IFF_PROMISC;
			if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) {
				fprintf(stderr, "Failed to set interface flags for promisc mode: '%s'\n", Ret);
				close(sock);
				exit(1);
			}
			fprintf(stderr, "Promiscious mode enabled!\n");
		}
	}

	fprintf(stderr, "Proxy ready for action!\n");

	fd_set fdsRead, fdsWrite; //FileDescriptor sets for select()
	unsigned char RBuf[CaptureLen], WBuf[CaptureLen]; //Data buffers
	int RBufLen = 0, WBufLen = 0; //Packet length
	int PLen;
	
	//Start an infinite loop
	while (1) {
		if (WBufLen < 0 || RBufLen < 0) {
			fprintf(stderr, "WBufLen < 0 or RBufLen < 0 :|\n");
			exit(2);
		}

		FD_ZERO(&fdsRead);			//Clear FD set
		if (RBufLen == 0) FD_SET(sock, &fdsRead);	//Add socket
		if (WBufLen == 0) FD_SET(0, &fdsRead);		//Add stdin

		FD_ZERO(&fdsWrite);			//Clear FD set
		if (WBufLen > 0) FD_SET(sock, &fdsWrite);	//Add socket
		if (RBufLen > 0) FD_SET(1, &fdsWrite);		//Add stdout

		if (select(sock + 1, &fdsRead, &fdsWrite, NULL, NULL) < 0) {
			if (errno == EAGAIN || errno == EINTR) {
				continue; //Retry select
			} else {
				fprintf(stderr, "An unhandled error occured in select: %d", errno);
				exit(5);
			}
		}

		//Read from socket
		if (FD_ISSET(sock, &fdsRead) && RBufLen == 0) {
			RBufLen = read(sock, RBuf, CaptureLen);
			if (RBufLen == 0) {
				fprintf(stderr, "End of file on socket\n");
				exit(0);
			} else if (RBufLen < 0) {
				fprintf(stderr, "Some error occured while reading from socket: %d\n", RBufLen);
				exit(4);
			}
			if (DumpStats != 0) fprintf(stderr, "R: %d\n", RBufLen);
		}
		//Write to socket
		if (FD_ISSET(sock, &fdsWrite) && WBufLen > 0) {
			write(sock, WBuf, WBufLen);
			if (DumpStats != 0) fprintf(stderr, "W: %d\n", WBufLen);
			WBufLen = 0;
		}

		//Read from stdin (FD 0)
		if (FD_ISSET(0, &fdsRead) && WBufLen == 0) {
			if (IncludePLen == 1) {
				WBufLen = read(0, &PLen, 4);
			}
			if (IncludePLen == 0 || PLen > CaptureLen) {
				PLen = CaptureLen;
			}
			if (IncludePLen == 0 || WBufLen > 0) {
				WBufLen = read(0, WBuf, PLen);
			}
			if (WBufLen == 0) {
				fprintf(stderr, "End of file on stdin\n");
				exit(0);
			} else if (WBufLen < 0) {
				fprintf(stderr, "Some error occured while reading stdin: %d\n", WBufLen);
				exit(4);
			}
		}
		//Write to stdout (FD 1)
		if (FD_ISSET(1, &fdsWrite) && RBufLen > 0) {
			if (IncludePLen == 1) write(1, &RBufLen, 4);
			write(1, RBuf, RBufLen);
			RBufLen = 0;
		}
	}
}