Projects/Software/TunTapIO/rawsock.c

From Qontrol.nl Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
/**********************************************************************
* 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;
		}
	}
}