blob: 9455cbb3c629e1c74a21f0a915bc46c6603bafbc [file] [log] [blame]
/*
* 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 <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
#if defined(__sun)
#include <strings.h> /* bzero */
#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 int ring_size(struct ring *r)
{
return sizeof(r->buf);
}
static int ring_used(struct ring *r)
{
return (r->head - r->tail) & (ring_size(r) - 1);
}
static int ring_free(struct ring *r)
{
return (ring_size(r) - 1) - ring_used(r);
}
static 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 < (int)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 == (int)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;
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
/* LOG_PERROR is not POSIX standard, and may not be portable */
#ifdef __sun
int syslog_options = LOG_PID;
#else
int syslog_options = LOG_PID | LOG_PERROR;
#endif
#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;
#if !defined(_WIN32) && !defined(__sun)
syslog_options &= ~LOG_PERROR;
#endif
break;
#endif
case 'd':
debug_level = atoi(optarg);
break;
case 'p':
port_local = atoi(optarg);
break;
case 'i':
lws_strncpy(interface_name, optarg, sizeof interface_name);
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++;
(void)interface_name_local;
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;
}