/* * fuzzing proxy - network-level fuzzing injection proxy * * Copyright (C) 2016 Andy Green * * 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 #include #include #include #include #include #include #include #include #include "../lib/libwebsockets.h" #ifdef _WIN32 #include #include "gettimeofday.h" #else #include #include #include #include #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=

] [--ssl] " "[-d ] " "[--resource_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 \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; }