fwder的作用主要是通过一个HTTP proxy,转发HTTPS的连接。它主要是用在透明代理上的。

#include <sys/socket.h>
#include <netdb.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

char proxy_addr[] = "127.0.0.1";
char proxy_port[] = "8120";

int main()
{
	sigignore(SIGCHLD);
	struct addrinfo *ai;
	struct addrinfo hints;
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE;
	int ret = getaddrinfo("::", "8443", &hints, &ai);
	if (ret)
	{
		fprintf(stderr, "failed to getaddrinfo: %s\n", gai_strerror(ret));
		return 1;
	}

	int s = -1;
	while (ai)
	{
		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
		if (s < 0)
		{
			ai = ai->ai_next;
			continue;
		}

		int val = 1;
		setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

		ret = bind(s, ai->ai_addr, ai->ai_addrlen);
		if (ret < 0)
		{
			close(s);
			s = -1;
			ai = ai->ai_next;
			continue;
		}
		break;
	}

	if (s < 0)
	{
		perror("failed to create and bind sock: ");
		return 1;
	}

	ret = listen(s, 10);
	if (ret < 0)
	{
		perror("failed to listen: ");
		return 1;
	}

	while (1)
	{
		struct sockaddr_storage cliaddr, myaddr;
		socklen_t cl = sizeof(cliaddr);
		socklen_t ml = sizeof(myaddr);
		int client = accept(s, (struct sockaddr *)&cliaddr, &cl);
		if (client < 0)
		{
			if (errno != ECONNABORTED)
				break;
		}

		char host[256];
		char serv[256];
		getnameinfo((struct sockaddr *)&cliaddr, cl, host, sizeof(host), 
				serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV);

		char myhost[256], myserv[256];
		getsockname(client, (struct sockaddr *)&myaddr, &ml);
		getnameinfo((struct sockaddr *)&myaddr, ml, 
				myhost, sizeof(myhost), myserv, sizeof(myserv),
				NI_NUMERICHOST | NI_NUMERICSERV);

		pid_t pid = fork();
		if (pid == 0)
		{
//			close(stdout);
//			close(stderr);
			struct addrinfo newhint, *res;
			memset(&newhint, 0, sizeof(newhint));
			newhint.ai_family = AF_UNSPEC;
			newhint.ai_socktype = SOCK_STREAM;
			newhint.ai_flags = AI_NUMERICHOST;
			ret = getaddrinfo(proxy_addr, proxy_port, &hints, &res);
			if (ret)
			{
//				fprintf(stderr, "[%d] failed to getaddrinfo: %s\n", getpid(), gai_strerror(ret));
				return 1;
			}

			if (strncmp(myhost, "::ffff:", 7) != 0)
			{
				return 1;
			}

			int xs = -1;
			while (res)
			{
				xs = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
				if (xs < 0)
				{
					res = res->ai_next;
					continue;
				}

				int val = 1;
				setsockopt(xs, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

				break;
			}

			if (s < 0)
			{
//				perror("failed to create sock: ");
				return 1;
			}

			ret = connect(xs, res->ai_addr, res->ai_addrlen);
			if (ret < 0)
			{
//				perror("failed to connect: ");
				return 1;
			}

			char *target = myhost + 7;
			char req[1500];
			char reply[1500];
			char *ptr = reply;
			if (strcmp(target, "78.16.49.15") == 0)
			{
				target = "199.59.148.87";
			}
			snprintf(req, sizeof(req), "CONNECT %s:%s HTTP/1.1\r\n\r\n", target, myserv);

			int linelen = 0;
			write(xs, req, strlen(req));
			int status = 0;
			while (1)
			{
				char c;
				ret = read(xs, &c, 1);
				if (ret <= 0)
					break;
				if (c == '\r')
				{
					read(xs, &c, 1);
					if (c != '\n')
					{
					}
					if (linelen == 0)
					{
						// reply end
						break;
					} else {
						if (strncmp(reply, "HTTP/", 5) == 0)
						{
							// reply line
							if (strstr(reply, " 200") != 0)
							{
								// OK
								status = 1;
//								printf("STATE OK\n");
							} else {
								status = 0;
//								printf("STATE ERROR\n");
							}
						} else {
							// other line
//							printf("OTHER LINE\n");
						}
					}
					linelen = 0;
//					printf("NEW LINE\n");
				} else {
					reply[linelen] = c;
					linelen++;
//					printf("%c", c);
				}
			}
			if (status > 0)
			{

				char buf[1500];
				int max = xs;
				if (client > xs) max = client;
				max++;
				int closed = 0;
				while (1)
				{
					fd_set inout;
					FD_ZERO(&inout);
					FD_SET(xs, &inout);
					FD_SET(client, &inout);
					/*fprintf(stderr, "ok1 : %d\n", FD_ISSET(client, &inout));*/
					/*fprintf(stderr, "ok2 : %d\n", FD_ISSET(xs, &inout));*/

					/*printf("before select\n");*/
					ret = select(max, &inout, NULL, NULL, NULL);
					/*printf("after select\n");*/
					if (ret == -1)
					{
						break;
					}

					if (FD_ISSET(xs, &inout))
					{
						/*printf("server data ready\n");*/
						int len = read(xs, buf, sizeof(buf));
						if (len <= 0)
						{
							closed++;
							shutdown(client, SHUT_WR);
						} else {
							/*printf("got server data: %d\n", len);*/
							ret = write(client, buf, len);
							if (ret < len)
								break;
						}
					}

					if (FD_ISSET(client, &inout))
					{
						/*printf("client data ready\n");*/
						int len = read(client, buf, sizeof(buf));
						if (len <= 0)
						{
							closed++;
							shutdown(xs, SHUT_WR);
						} else {
							/*printf("got data: %d bytes\n", len);*/
							ret = write(xs, buf, len);
							if (ret < len)
								break;
						}
					}

					if (closed >= 2)
						break;
				}
			}

			/*printf("EXIT\n");*/
			shutdown(xs, SHUT_RDWR);
			close(xs);
			
			shutdown(client, SHUT_RDWR);
			close(client);
			return 0;
		} else {
/*			printf("new child %d serving %s:%s, at %s:%s\n", pid, host, serv, 
					myhost, myserv);*/
			close(client);
		}
	}
	return 0;
}