/*
 * fuzzing proxy - network-level fuzzing injection proxy
 *
 * Copyright (C) 2016 Andy Green <andy@warmcat.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation:
 *  version 2.1 of the License.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA  02110-1301  USA
 *
 *
 * fuzxy is designed to go on the client path
 *
 * [ client <-> fuzxy ] <-> server
 *
 * you can arrange that with, eg,
 *
 *  http_proxy=localhost:8880
 *
 * env var before starting the client.
 *
 * Even though he is on the client side, he is able to see and change traffic
 * in both directions, and so fuzz both the client and the server.
 */

#if defined(_WIN32) && defined(EXTERNAL_POLL)
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#define poll(fdArray, fds, timeout)  WSAPoll((LPWSAPOLLFD)(fdArray), (ULONG)(fds), (INT)(timeout))
#endif

#include "lws_config.h"

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include "../lib/libwebsockets.h"

#ifdef _WIN32
#include <io.h>
#include "gettimeofday.h"
#else
#include <syslog.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/socket.h>
#endif

#if defined(__NetBSD__)
#include <netinet/in.h>
#endif

#define MAX_FUZZ_BUF (1024 * 1024)

enum types {
	FZY_S_DEAD		= 0,
	FZY_S_LISTENING		= 1,
	FZY_S_ACCEPTED		= 2,
	FZY_S_PROXIED		= 3,
	FZY_S_ONWARD		= 4,
};

enum proxy_parser_states {
	FZY_PP_CONNECT		= 0,
	FZY_PP_ADDRESS		= 1,
	FZY_PP_PORT		= 2,
	FZY_PP_CRLFS		= 3,
};

enum fuzzer_parser_states {
	FZY_FP_SEARCH		= 0,
	FZY_FP_SEARCH2		= 1,
	FZY_FP_INJECT_PREPARE	= 2,
	FZY_FP_INJECT		= 3,
	FZY_FP_PENDING		= 4,
};

struct ring {
	char buf[4096];
	int head;
	int tail;
};

struct state {
	enum types type;
	enum proxy_parser_states pp;
	int ppc;

	struct ring in;

	char address[256];
	int port;

	enum fuzzer_parser_states fp;
	int fuzc;
	int pending;

	int twin; /* must be fixed up when arrays lose guys */
	unsigned int outbound:1; /* from local -> remote */
	unsigned int is_pending:1;

	unsigned char buf[MAX_FUZZ_BUF];
	unsigned int inject_len;
};

struct test {
	const char *s[3];
	int len[2];
	unsigned int swallow:1;
};

int force_exit = 0;
int which = 5;

static const struct test tests[] = {
	{ { NULL, "\x0d\x0a\x0d\x0a",
	    "{ 0xd9, 0x87, 0xd2, 0x88, 0xd2, (248){ 0x89, 0xd2 }, 0x0d, 0x0a },"
	  }, { 0, 4 }, 1 },
	{ { NULL, "\x0d\x0a\x0d\x0a",
	    "{ 0xd9, 0x87, 0xd2, 0x88, 0xd2, (1373){ 0x89, 0xd2 }, 0x0d, 0x0a },"
	  }, { 0, 4 }, 1 },
	{ { NULL, "\x0d\x0a\x0d\x0a",
	    "{ 0xd9, 0x87, 0xd2, 0x88, 0xd2, (16967){ 0x89, 0xd2 }, (87){ 0xe2, 0x82, 0xac }, 0x0d, 0x0a },"
	  }, { 0, 4 }, 1 },
	{ { NULL, "\x0d\x0a\x0d\x0a",
		"0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, "
		"0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, "
		"0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x3a, 0x20, 0x77, 0x65, "
		"0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, "
		"0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x53, 0x65, "
		"0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x4b, 0x65, 0x79, 0x3a, "
		"0x20, 0x64, 0x47, 0x68, 0x6c, 0x49, 0x48, 0x4e, 0x68, 0x62, 0x58, 0x42, 0x73, 0x5a, 0x53, 0x42, "
		"0x75, 0x62, 0x32, 0x35, 0x6a, 0x5a, 0x51, 0x3d, 0x3d, 0x0d, 0x0a, 0x4f, 0x72, 0x69, 0x67, 0x69, "
		"0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, "
		"0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, "
		"0x65, 0x74, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x33, 0x0d, 0x0a, "
		"0xef, 0xbb, 0xbf, 0xc2, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x48, 0x54, "
		"0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x32, "
		"0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, "
		"0x3a, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, "
		"0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x0d, 0x0a, "
	  }, { 0, 4 }, 1 },
	{ { NULL, "\x0d\x0a\x0d\x0a",
		"(20){0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, "
		"0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, "
		"0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x3a, 0x20, 0x77, 0x65, "
		"0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, "
		"0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x53, 0x65, "
		"0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x4b, 0x65, 0x79, 0x3a, "
		"0x20, 0x64, 0x47, 0x68, 0x6c, 0x49, 0x48, 0x4e, 0x68, 0x62, 0x58, 0x42, 0x73, 0x5a, 0x53, 0x42, "
		"0x75, 0x62, 0x32, 0x35, 0x6a, 0x5a, 0x51, 0x3d, 0x3d, 0x0d, 0x0a, 0x4f, 0x72, 0x69, 0x67, 0x69, "
		"0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, "
		"0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, "
		"0x65, 0x74, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x33, 0x0d, 0x0a, "
		"0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, "
		"0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, "
		"0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x3a, 0x20, 0x77, 0x65, "
		"0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, "
		"0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x53, 0x65, "
		"0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x4b, 0x65, 0x79, 0x3a, "
		"0x20, 0x64, 0x47, 0x68, 0x6c, 0x49, 0x48, 0x4e, 0x68, 0x62, 0x58, 0x42, 0x73, 0x5a, 0x53, 0x42, "
		"0x75, 0x62, 0x32, 0x35, 0x6a, 0x5a, 0x51, 0x3d, 0x3d, 0x0d, 0x0a, 0x4f, 0x72, 0x69, 0x67, 0x69, "
		"0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, "
		"0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, "
		"0x65, 0x74, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x33, 0x0d, 0x0a, "
		"0xc2, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, "
		"0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x30, "
		"0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x3a, 0x20, 0x77, "
		"0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, "
		"0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x53, "
		"0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x4b, 0x65, 0x79, "
		"0x3a, 0x20, 0x64, 0x47, 0x68, 0x6c, 0x49, 0x48, 0x4e, 0x68, 0x62, 0x58, 0x42, 0x73, 0x5a, 0x53, "
		"0x42, 0x75, 0x62, 0x32, 0x35, 0x6a, 0x5a, 0x51, 0x3d, 0x3d, 0x0d, 0x0a, 0x4f, 0x72, 0x69, 0x67, "
		"0x69, 0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x32, 0x37, 0x2e, 0x30, "
		"0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, "
		"0x6b, 0x65, 0x74, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x33, 0x0d, "
		"0x0a, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, "
		"0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x30, "
		"0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x3a, 0x20, 0x77, "
		"0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, "
		"0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x53, "
		"0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x4b, 0x65, 0x79, "
		"0x3a, 0x20, 0x64, 0x47, 0x68, 0x6c, 0x49, 0x48, 0x4e, 0x68, 0x62, 0x58, 0x42, 0x73, 0x5a, 0x53, "
		"0x42, 0x75, 0x62, 0x32, 0x35, 0x6a, 0x5a, 0x51, 0x3d, 0x3d, 0x0d, 0x0a, 0x4f, 0x72, 0x69, 0x67, "
		"0x69, 0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x32, 0x37, 0x2e, 0x30, "
		"0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, "
		"0x6b, 0x65, 0x74, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x33, 0x0d, "
		"0x0a, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, "
		"0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x30, "
		"0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x3a, 0x20, 0x77, "
		"0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, "
		"0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x53, "
		"0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x4b, 0x65, 0x79, "
		"0x3a, 0x20, 0x64, 0x47, 0x68, 0x6c, 0x49, 0x48, 0x4e, 0x68, 0x62, 0x58, 0x42, 0x73, 0x5a, 0x53, "
		"0x42, 0x75, 0x62, 0x32, 0x35, 0x6a, 0x5a, 0x51, 0x3d, 0x3d, 0x0d, 0x0a, 0x4f, 0x72, 0x69, 0x67, "
		"0x69, 0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x32, 0x37, 0x2e, 0x30, "
		"0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, "
		"0x6b, 0x65, 0x74, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x33, 0x0d, "
		"0x0a, 0xc0, 0x80, 0xef, 0xb7, 0x90, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x20, "
		"0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, "
		"0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, "
		"0x64, 0x65, 0x3a, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, "
		"0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, "
		"0x64, 0x65, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, "
		"0x74, 0x2d, 0x4b, 0x65, 0x79, 0x3a, 0x20, 0x64, 0x47, 0x68, 0x6c, 0x49, 0x48, 0x4e, 0x68, 0x62, "
		"0x58, 0x42, 0x73, 0x5a, 0x53, 0x42, 0x75, 0x62, 0x32, 0x35, 0x6a, 0x5a, 0x51, 0x3d, 0x3d, 0x0d, "
		"0x0a, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, "
		"0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, "
		"0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, "
		"0x3a, 0x20, 0x31, 0x33, 0x0d, 0x0a, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x20, "
		"0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, "
		"0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, "
		"0x64, 0x65, 0x3a, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, "
		"0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, "
		"0x64, 0x65, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, "
		"0x74, 0x2d, 0x4b, 0x65, 0x79, 0x3a, 0x20, 0x64, 0x47, 0x68, 0x6c, 0x49, 0x48, 0x4e, 0x68, 0x62, "
		"0x58, 0x42, 0x73, 0x5a, 0x53, 0x42, 0x75, 0x62, 0x32, 0x35, 0x6a, 0x5a, 0x51, 0x3d, 0x3d, 0x0d, "
		"0x0a, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, "
		"0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, "
		"0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, "
		"0x3a, 0x20, 0x31, 0x33, 0x0d, 0x0a, 0xc2, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, "
		"0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, "
		"0x20, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, "
		"0x61, 0x64, 0x0d, 0x0a, }"
	}, { 0, 4 }, 1 },
	{ { NULL, "\x0d\x0a\x0d\x0a",
		"0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, "
		"0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, "
		"0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x3a, 0x20, 0x77, 0x65, "
		"0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, "
		"0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x53, 0x65, "
		"0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x4b, 0x65, 0x79, 0x3a, "
		"0x20, 0x64, 0x47, 0x68, 0x6c, 0x49, 0x48, 0x4e, 0x68, 0x62, 0x58, 0x42, 0x73, 0x5a, 0x53, 0x42, "
		"0x75, 0x62, 0x32, 0x35, 0x6a, 0x5a, 0x51, 0x3d, 0x3d, 0x0d, 0x0a, 0x4f, 0x72, 0x69, 0x67, 0x69, "
		"0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, "
		"0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, "
		"0x65, 0x74, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x33, 0x0d, 0x0a, "
		"0xef, 0xbb, 0xbf, 0xc2, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x48, 0x54, "
		"0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x32, "
		"0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, "
		"0x3a, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, "
		"0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x0d, 0x0a, (2048){ 0x0d, 0x0a}"
	  }, { 0, 4 }, 1 },
};

static const int ring_size(struct ring *r)
{
	return sizeof(r->buf);
}
static const int ring_used(struct ring *r)
{
	return (r->head - r->tail) & (ring_size(r) - 1);
}
static const int ring_free(struct ring *r)
{
	return (ring_size(r) - 1) - ring_used(r);
}
static const int ring_get_one(struct ring *r)
{
	int n = r->buf[r->tail] & 255;

	if (r->tail == r->head)
		return -1;

	r->tail++;
	if (r->tail == ring_size(r))
		r->tail = 0;

	return n;
}

static int hex(char c)
{
	if (c >= '0' && c <= '9')
		return c -'0';
	if (c >= 'a' && c <= 'f')
		return c - 'a' + 10;
	if (c >='A' && c <= 'F')
		return c - 'A' + 10;

	return -1;
}

static int
fuzxy_tok(const char **src, unsigned char **buf, int *len)
{
	unsigned char *start;
	unsigned int count, rlen;

	while (**src) {

		if (**src == ' ' || **src == ','  || **src == '\n') {
			(*src)++;
			continue;
		}

		if ((*src)[0] == '}') {
			(*src)++;
			return 0;
		}

		if ((*src)[0] == '0' && (*src)[1] == 'x') {
			if (!len) {
				lwsl_err("out of space\n");
				return -1;
			}

			((*buf)++)[0] = (hex((*src)[2]) << 4) | hex((*src)[3]);
			*src += 4;
			(*len)--;
		}

		if (*src[0] == '(') {
			start = *buf;
			(*src)++;
			count = atoi(*src) - 1;
			lwsl_err("count %d\n", count);
			while (**src && **src != ')')
				(*src)++;
			if (!(*src)[0]) {
				lwsl_err("unexpected end in (\n");
				return -1;
			}
			(*src)++;
			while (**src == ' ')
				(*src)++;
			if (**src != '{') {
				lwsl_err("missing {\n");

				return -1;
			}
			(*src)++;
			if (fuzxy_tok(src, buf, len))
				return -1;
			rlen = *buf - start;
			while (count--) {
				if (*len < rlen) {
					lwsl_err("out of space\n");
					return -1;
				}
				memcpy(*buf, start, rlen);
				*buf += rlen;
				*len -= rlen;
			}
		}
	}

	return 0;
}

static int
fuzxy_create_pattern(const char *src, unsigned char *buf, int len)
{
	unsigned char *old = buf;
	int n;

	while (*src && (*src == '{' || *src == ' '))
		src++;

	if (!*src)
		return -1;

	n = fuzxy_tok(&src, &buf, &len);
	if (n)
		return -1;

	return buf - old;
}

void sighandler(int sig)
{
	force_exit = 1;
}

static struct option options[] = {
	{ "help",	no_argument,		NULL, 'h' },
	{ "debug",	required_argument,	NULL, 'd' },
	{ "port",	required_argument,	NULL, 'p' },
	{ "ssl",	no_argument,		NULL, 's' },
	{ "allow-non-ssl",	no_argument,	NULL, 'a' },
	{ "interface",  required_argument,	NULL, 'i' },
	{ "closetest",  no_argument,		NULL, 'c' },
	{ "libev",  no_argument,		NULL, 'e' },
#ifndef LWS_NO_DAEMONIZE
	{ "daemonize", 	no_argument,		NULL, 'D' },
#endif
	{ "resource_path", required_argument,	NULL, 'r' },
	{ NULL, 0, 0, 0 }
};

static struct pollfd pfd[128];
static struct state state[128];
static int pfds = 0;

static void close_and_remove_fd(int index)
{
	int n;

	lwsl_notice("%s: closing index %d\n", __func__, index);
	close(pfd[index].fd);
	pfd[index].fd = -1;

	n = state[index].twin;
	if (n) {
		assert(state[n].twin == index);
	}
	state[index].type = FZY_S_DEAD;

	if (index == pfds - 1) {
		if (state[index].twin)
			state[state[index].twin].twin = 0;
		state[index].twin = 0;
		goto bail;
	}

	/* swap the end guy into the deleted guy and trim back one */

	if (state[pfds - 1].twin) {
		state[state[pfds - 1].twin].twin = index;
		if (n && n == pfds - 1)
			n = index;
	}

	/* swap the last guy into dead guy's place and trim by one */
	pfd[index] = pfd[pfds - 1];
	state[index] = state[pfds - 1];

	if (n) {
		pfds--;
		state[n].twin = 0;
		close_and_remove_fd(n);
		return;
	}

bail:
	pfds--;
}

static void construct_state(int n, enum types s, int flags)
{
	memset(&state[n], 0, sizeof state[n]);
	state[n].type = s;
	pfd[n].events = flags | POLLHUP;
}

static int
fuzxy_listen(const char *interface_name, int port, int *sockfd)
{
	struct sockaddr_in serv_addr4;
	socklen_t len = sizeof(struct sockaddr);
	struct sockaddr_in sin;
	int n, opt = 1;

	*sockfd = socket(AF_INET, SOCK_STREAM, 0);

	if (*sockfd == -1) {
		lwsl_err("ERROR opening socket\n");
		goto bail1;
	}

	if (setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR,
		       (const void *)&opt, sizeof(opt)) < 0) {
		lwsl_err("unable to set listen socket options\n");
		goto bail2;
	}

	bzero((char *) &serv_addr4, sizeof(serv_addr4));
	serv_addr4.sin_addr.s_addr = INADDR_ANY;
	serv_addr4.sin_family = AF_INET;

	if (interface_name[0] &&
	    lws_interface_to_sa(0, interface_name, (struct sockaddr_in *)
			        (struct sockaddr *)&serv_addr4,
				sizeof(serv_addr4)) < 0) {
		lwsl_err("Unable to find interface %s\n", interface_name);
		goto bail2;
	}

	serv_addr4.sin_port = htons(port);

	n = bind(*sockfd, (struct sockaddr *)&serv_addr4,
				sizeof(serv_addr4));
	if (n < 0) {
		lwsl_err("ERROR on binding to port %d (%d %d)\n",
			 port, n, errno);
		goto bail2;
	}

	if (getsockname(*sockfd, (struct sockaddr *)&sin, &len) == -1)
		lwsl_warn("getsockname: %s\n", strerror(errno));
	else
		port = ntohs(sin.sin_port);

	listen(*sockfd, SOMAXCONN);

	return 0;
bail2:
	close(*sockfd);
bail1:
	return -1;
}


static int fuzz(int n, char *out, int len)
{
	struct state *s = &state[n];
	const struct test *t = &tests[which];
	int m = 0;
	int c;

	while (m < len) {
		switch (s->fp) {
		case FZY_FP_SEARCH:
			if (t->s[0] == NULL) {
				s->fuzc = 0;
				s->is_pending = 0;
				s->fp = FZY_FP_SEARCH2;
				goto search2;
			}
			c = ring_get_one(&state[s->twin].in);
			if (c < 0)
				return m;
			if (c == tests[which].s[0][s->fuzc++]) {
				if (s->fuzc == t->len[0]) {
					s->fuzc = 0;
					s->fp = FZY_FP_SEARCH2;
				}
			} else
				s->fuzc = 0;
			out[m++] = c;
			break;

		case FZY_FP_SEARCH2:
search2:
			if (tests[which].s[1] == NULL) {
				s->fuzc = 0;
				s->is_pending = 0;
				s->fp = FZY_FP_INJECT_PREPARE;
				goto inject;
			}
			c = ring_get_one(&state[s->twin].in);
			if (c < 0)
				return m;
			if (c == tests[which].s[1][s->fuzc++]) {
				if (s->fuzc == tests[which].len[1]) {
					lwsl_notice("+++++++fuzzer hit...\n");
					s->fuzc = 0;
					s->fp = FZY_FP_INJECT_PREPARE;
					s->is_pending = !t->swallow;
					s->pending = c;
					goto inject;
				}
			} else
				s->fuzc = 0;
			if (!t->swallow)
				out[m++] = c;
			break;

		case FZY_FP_INJECT_PREPARE:
inject:
			s->inject_len = fuzxy_create_pattern(t->s[2],
				s->buf, sizeof(s->buf));
			if (s->inject_len == (unsigned int) -1)
				return -1;
			s->fp = FZY_FP_INJECT;
			/* fallthru */

		case FZY_FP_INJECT:
			out[m++] = s->buf[s->fuzc++];
			if (s->fuzc == s->inject_len)
				s->fp = FZY_FP_PENDING;
			break;

		case FZY_FP_PENDING:
			if (s->is_pending)
				out[m++] = s->pending;
			s->fp = FZY_FP_SEARCH;
			s->fuzc = 0;
			break;
		}
	}

	return m;
}

static int
handle_accept(int n)
{
	struct addrinfo ai, *res, *result;
	struct sockaddr_in serv_addr4;
	struct state *s = &state[n];
	void *p = NULL;
	int m, sockfd;

	while (1) {
		m = ring_get_one(&s->in);
		if (m < 0)
			return 0;

		switch (s->pp) {
		case FZY_PP_CONNECT:
			if (m != "CONNECT "[s->ppc++]) {
				lwsl_notice("failed CONNECT match\n");
				return 1;
			}
			if (s->ppc == 8) {
				s->pp = FZY_PP_ADDRESS;
				s->ppc = 0;
			}
			break;
		case FZY_PP_ADDRESS:
			if (m == ':') {
				s->address[s->ppc++] = '\0';
				s->pp = FZY_PP_PORT;
				s->ppc = 0;
				break;
			}
			if (m == ' ') {
				s->address[s->ppc++] = '\0';
				s->pp = FZY_PP_CRLFS;
				s->ppc = 0;
				break;
			}


			s->address[s->ppc++] = m;
			if (s->ppc == sizeof(s->address)) {
				lwsl_notice("Failed on address length\n");
				return 1;
			}
			break;
		case FZY_PP_PORT:
			if (m == ' ') {
				s->pp = FZY_PP_CRLFS;
				s->ppc = 0;
				break;
			}
			if (m >= '0' && m <= '9') {
				s->port *= 10;
				s->port += m - '0';
				break;
			}
			return 1;

		case FZY_PP_CRLFS:
			if (m != "\x0d\x0a\x0d\x0a"[s->ppc++])
				s->ppc = 0;
			if (s->ppc != 4)
				break;
			s->type = FZY_S_PROXIED;

			memset (&ai, 0, sizeof ai);
			ai.ai_family = PF_UNSPEC;
			ai.ai_socktype = SOCK_STREAM;
			ai.ai_flags = AI_CANONNAME;

			if (getaddrinfo(s->address, NULL, &ai, &result)) {
				lwsl_notice("failed to lookup %s\n",
					    s->address);
				return 1;
			}

			res = result;
			while (!p && res) {
				switch (res->ai_family) {
				case AF_INET:
					p = &((struct sockaddr_in *)res->
					      ai_addr)->sin_addr;
					break;
				}

				res = res->ai_next;
			}

			if (!p) {
				lwsl_notice("Failed to get address result %s\n",
					    s->address);
				freeaddrinfo(result);
				return 1;
			}

			serv_addr4.sin_family = AF_INET;
			serv_addr4.sin_addr = *((struct in_addr *)p);
			serv_addr4.sin_port = htons(s->port);
			bzero(&serv_addr4.sin_zero, 8);
			freeaddrinfo(result);

			lwsl_err("Conn %d req '%s' port %d\n", n,
				 s->address, s->port);
			/* we need to open the associated onward connection */
			sockfd = socket(AF_INET, SOCK_STREAM, 0);
			if (sockfd < 0) {
				lwsl_err("Could not get socket\n");
				return -1;
			}

			if (connect(sockfd, (struct sockaddr *)&serv_addr4,
				    sizeof(struct sockaddr)) == -1 ||
			    errno == EISCONN) {
				close(sockfd);
				lwsl_err("proxied onward connection failed\n");
				return 1;
			}
			s->twin = pfds;
			construct_state(pfds, FZY_S_ONWARD,
					POLLOUT | POLLIN | POLLERR);
			state[pfds].twin = n;
			lwsl_notice("binding conns %d and %d\n", n, pfds);
			state[pfds].outbound = s->outbound;
			state[pfds].ppc = 0;
			pfd[pfds++].fd = sockfd;

			lwsl_notice("onward connection in progress\n");
			if (ring_used(&s->in))
				pfd[s->twin].events |= POLLOUT;
			if (write(pfd[n].fd,
				  "HTTP/1.0 200 \x0d\x0a\x0d\x0a", 17) < 17)
				return 1;
		}
	}

	return 0;
}

static void sigpipe_handler(int x)
{
}

int
main(int argc, char **argv)
{
	char interface_name[128] = "", interface_name_local[128] = "lo";
	int port_local = 8880, accept_fd;
	struct sockaddr_in cli_addr;
	int debug_level = 7;
	socklen_t clilen;
	struct state *s;
	char out[4096];
	int opts = 0;
	int n = 0, m;

#ifndef _WIN32
	int syslog_options = LOG_PID | LOG_PERROR;
#endif
#ifndef LWS_NO_DAEMONIZE
 	int daemonize = 0;
#endif
	signal(SIGPIPE, sigpipe_handler);

	while (n >= 0) {
		n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL);
		if (n < 0)
			continue;
		switch (n) {
		case 'e':
			opts |= LWS_SERVER_OPTION_LIBEV;
			break;
#ifndef LWS_NO_DAEMONIZE
		case 'D':
			daemonize = 1;
			#ifndef _WIN32
			syslog_options &= ~LOG_PERROR;
			#endif
			break;
#endif
		case 'd':
			debug_level = atoi(optarg);
			break;
		case 'p':
			port_local = atoi(optarg);
			break;
		case 'i':
			strncpy(interface_name, optarg, sizeof interface_name);
			interface_name[(sizeof interface_name) - 1] = '\0';
			break;
		case 'h':
			fprintf(stderr, "Usage: libwebsockets-test-fuzxy "
					"[--port=<p>] [--ssl] "
					"[-d <log bitfield>] "
					"[--resource_path <path>]\n");
			exit(1);
		}
	}

#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
	/*
	 * normally lock path would be /var/lock/lwsts or similar, to
	 * simplify getting started without having to take care about
	 * permissions or running as root, set to /tmp/.lwsts-lock
	 */
	if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
		fprintf(stderr, "Failed to daemonize\n");
		return 1;
	}
#endif

	signal(SIGINT, sighandler);

#ifndef _WIN32
	/* we will only try to log things according to our debug_level */
	setlogmask(LOG_UPTO (LOG_DEBUG));
	openlog("fuzxy", syslog_options, LOG_DAEMON);
#endif

	/* tell the library what debug level to emit and to send it to syslog */
	lws_set_log_level(debug_level, lwsl_emit_syslog);

	lwsl_notice("libwebsockets fuzzing proxy - license LGPL2.1+SLE\n");
	lwsl_notice("(C) Copyright 2016 Andy Green <andy@warmcat.com>\n");

	/* listen on local side */

	if (fuzxy_listen(interface_name, port_local, &pfd[pfds].fd)) {
		lwsl_err("Failed to listen on local side\n");
		goto bail1;
	}
	construct_state(pfds, FZY_S_LISTENING, POLLIN | POLLERR);
	pfds++;

	lwsl_notice("Local side listening on %s:%u\n",
		    interface_name_local, port_local);

	while (!force_exit) {

		m = poll(pfd, pfds, 50);
		if (m < 0)
			continue;
		for (n = 0; n < pfds; n++) {
			s = &state[n];
			if (s->type == FZY_S_LISTENING &&
			    (pfd[n].revents & POLLIN)) {
				/* first do the accept entry */

				clilen = sizeof(cli_addr);
				accept_fd = accept(pfd[0].fd,
					 (struct sockaddr *)&cli_addr, &clilen);
				if (accept_fd < 0) {
					if (errno == EAGAIN ||
					    errno == EWOULDBLOCK)
						continue;

					lwsl_warn("ERROR on accept: %s\n",
						  strerror(errno));
					continue;
				}
				construct_state(pfds, FZY_S_ACCEPTED,
						POLLIN | POLLERR);
				state[pfds].outbound = n == 0;
				state[pfds].pp = FZY_PP_CONNECT;
				state[pfds].ppc = 0;
				pfd[pfds++].fd = accept_fd;
				lwsl_notice("new connect accepted\n");
				continue;
			}
			if (pfd[n].revents & POLLIN) {
				assert(ring_free(&s->in));
				m = (ring_size(&s->in) - 1) -
				    s->in.head;
				if (s->in.head == ring_size(&s->in) - 1 &&
				    s->in.tail)
					m = 1;
				m = read(pfd[n].fd, s->in.buf + s->in.head, m);
//				lwsl_notice("read %d\n", m);
				if (m <= 0) {
					lwsl_err("Error on read\n");
					goto drop;
				}
				s->in.head += m;
				if (s->in.head == ring_size(&s->in))
					s->in.head = 0;

				switch (s->type) {
				case FZY_S_ACCEPTED: /* parse proxy CONNECT */
					if (handle_accept(n))
						goto drop;
					break;
				case FZY_S_PROXIED:
				case FZY_S_ONWARD:
					if (ring_used(&s->in))
						pfd[s->twin].events |= POLLOUT;
					break;
				default:
					assert(0);
					break;
				}
				if (s->in.head == s->in.tail) {
					s->in.head = s->in.tail = 0;
					pfd[n].events |= POLLIN;
				}
				if (!ring_free(&s->in))
					pfd[n].events &= ~POLLIN;
			}
			if (pfd[n].revents & POLLOUT) {
				switch (s->type) {
				case FZY_S_PROXIED:
				case FZY_S_ONWARD:
					/*
					 * draw down enough of the partner's
					 * in ring to either exhaust it
 					 * or fill an output buffer
 					 */
					m = fuzz(n, out, sizeof(out));
					if (m < 0) {
						lwsl_err("Error on fuzz\n");
						goto drop;
					}
					lwsl_notice("got block %d\n", m);
					if (m) {
						m = write(pfd[n].fd, out, m);
						if (m <= 0) {
							lwsl_err("Error on write\n");
							goto drop;
						} else
							pfd[s->twin].events &= ~POLLIN;
					} else {
						pfd[n].events &= ~POLLOUT;

						if (ring_free(&state[s->twin].in))
							pfd[s->twin].events |= POLLIN;
					}

					break;
				default:
					break;
				}
			}

			continue;
drop:
			close_and_remove_fd(n);
			n--; /* redo this slot */
		}
	}

bail1:
	lwsl_notice("%s exited cleanly\n", argv[0]);

#ifndef _WIN32
	closelog();
#endif

	return 0;
}
