• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/beast
8 //
9 
10 #ifndef BOOST_BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP
11 #define BOOST_BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP
12 
13 #include <boost/beast/core/ostream.hpp>
14 #include <boost/beast/http/detail/rfc7230.hpp>
15 #include <cstdint>
16 #include <random>
17 #include <string>
18 
19 namespace boost {
20 namespace beast {
21 namespace http {
22 
23 template<class = void>
24 std::string
escaped_string(string_view s)25 escaped_string(string_view s)
26 {
27     std::string out;
28     out.reserve(s.size());
29     for(char c : s)
30     {
31         if(c == '\r')
32             out.append("\\r");
33         else if(c == '\n')
34             out.append("\\n");
35         else if(c == '\t')
36             out.append("\\t");
37         else
38             out.append(&c, 1);
39     }
40     return out;
41 }
42 
43 // Produces random HTTP messages
44 //
45 template<class = void>
46 class message_fuzz_t
47 {
48     std::mt19937 rng_;
49 
50     static
51     std::string
to_hex(std::size_t v)52     to_hex(std::size_t v)
53     {
54         if(! v)
55             return "0";
56         std::string s;
57         while(v > 0)
58         {
59             s.insert(s.begin(),
60                 "0123456789abcdef"[v&0xf]);
61             v >>= 4;
62         }
63         return s;
64     }
65 
66 public:
67     template<class UInt = std::size_t>
68     UInt
rand(std::size_t n)69     rand(std::size_t n)
70     {
71         return static_cast<UInt>(
72             std::uniform_int_distribution<
73                 std::size_t>{0, n-1}(rng_));
74     }
75 
76     std::string
method()77     method()
78     {
79 #if 0
80         // All IANA registered methods
81         static char const* const list[39] = {
82             "ACL", "BASELINE-CONTROL", "BIND", "CHECKIN", "CHECKOUT", "CONNECT",
83             "COPY", "DELETE", "GET", "HEAD", "LABEL", "LINK", "LOCK", "MERGE",
84             "MKACTIVITY", "MKCALENDAR", "MKCOL", "MKREDIRECTREF", "MKWORKSPACE",
85             "MOVE", "OPTIONS", "ORDERPATCH", "PATCH", "POST", "PRI", "PROPFIND",
86             "PROPPATCH", "PUT", "REBIND", "REPORT", "SEARCH", "TRACE", "UNBIND",
87             "UNCHECKOUT", "UNLINK", "UNLOCK", "UPDATE", "UPDATEREDIRECTREF",
88             "VERSION-CONTROL"
89         };
90         return list[rand(39)];
91 #else
92         // methods parsed by nodejs-http-parser
93         static char const* const list[33] = {
94             "ACL", "BIND", "CHECKOUT", "CONNECT", "COPY", "DELETE", "HEAD", "GET",
95             "LINK", "LOCK", "MERGE", "MKCOL", "MKCALENDAR", "MKACTIVITY", "M-SEARCH",
96             "MOVE", "NOTIFY", "OPTIONS", "PATCH", "POST", "PROPFIND", "PROPPATCH",
97             "PURGE", "PUT", "REBIND", "REPORT", "SEARCH", "SUBSCRIBE", "TRACE",
98             "UNBIND", "UNLINK", "UNLOCK", "UNSUBSCRIBE"
99         };
100         return list[rand(33)];
101 #endif
102     }
103 
104     std::string
scheme()105     scheme()
106     {
107         static char const* const list[241] = {
108             "aaa", "aaas", "about", "acap", "acct", "acr", "adiumxtra", "afp", "afs",
109             "aim", "appdata", "apt", "attachment", "aw", "barion", "beshare", "bitcoin",
110             "blob", "bolo", "callto", "cap", "chrome", "chrome-extension", "cid",
111             "coap", "coaps", "com-eventbrite-attendee", "content", "crid", "cvs",
112             "data", "dav", "dict", "dis", "dlna-playcontainer", "dlna-playsingle",
113             "dns", "dntp", "dtn", "dvb", "ed2k", "example", "facetime", "fax", "feed",
114             "feedready", "file", "filesystem", "finger", "fish", "ftp", "geo", "gg",
115             "git", "gizmoproject", "go", "gopher", "gtalk", "h323", "ham", "hcp",
116             "http", "https", "iax", "icap", "icon", "im", "imap", "info", "iotdisco",
117             "ipn", "ipp", "ipps", "irc", "irc6", "ircs", "iris", "iris.beep",
118             "iris.lwz", "iris.xpc", "iris.xpcs", "isostore", "itms", "jabber", "jar",
119             "jms", "keyparc", "lastfm", "ldap", "ldaps", "magnet", "mailserver",
120             "mailto", "maps", "market", "message", "mid", "mms",
121             "modem", "ms-access", "ms-drive-to", "ms-enrollment", "ms-excel",
122             "ms-getoffice", "ms-help", "ms-infopath", "ms-media-stream-id", "ms-project",
123             "ms-powerpoint", "ms-publisher", "ms-search-repair",
124             "ms-secondary-screen-controller", "ms-secondary-screen-setup",
125             "ms-settings", "ms-settings-airplanemode", "ms-settings-bluetooth",
126             "ms-settings-camera", "ms-settings-cellular", "ms-settings-cloudstorage",
127             "ms-settings-emailandaccounts", "ms-settings-language", "ms-settings-location",
128             "ms-settings-lock", "ms-settings-nfctransactions", "ms-settings-notifications",
129             "ms-settings-power", "ms-settings-privacy", "ms-settings-proximity",
130             "ms-settings-screenrotation", "ms-settings-wifi", "ms-settings-workplace",
131             "ms-spd", "ms-transit-to", "ms-visio", "ms-walk-to", "ms-word", "msnim",
132             "msrp", "msrps", "mtqp", "mumble", "mupdate", "mvn", "news", "nfs", "ni",
133             "nih", "nntp", "notes", "oid", "opaquelocktoken", "pack", "palm", "paparazzi",
134             "pkcs11", "platform", "pop", "pres", "prospero", "proxy", "psyc", "query",
135             "redis", "rediss", "reload", "res", "target", "rmi", "rsync", "rtmfp",
136             "rtmp", "rtsp", "rtsps", "rtspu", "secondlife", "service", "session", "sftp",
137             "sgn", "shttp", "sieve", "sip", "sips", "skype", "smb", "sms", "smtp",
138             "snews", "snmp", "soap.beep", "soap.beeps", "soldat", "spotify", "ssh",
139             "steam", "stun", "stuns", "submit", "svn", "tag", "teamspeak", "tel",
140             "teliaeid", "telnet", "tftp", "things", "thismessage", "tip", "tn3270",
141             "tool", "turn", "turns", "tv", "udp", "unreal", "urn", "ut2004", "v-event",
142             "vemmi", "ventrilo", "videotex", "vnc", "view-source", "wais", "webcal",
143             "wpid", "ws", "wss", "wtai", "wyciwyg", "xcon", "xcon-userid", "xfire",
144             "xmlrpc.beep", "xmlrpc.beeps", "xmpp", "xri", "ymsgr", "z39.50", "z39.50r",
145             "z39.50s:"
146         };
147         return list[rand(241)];
148     }
149 
150     std::string
pchar()151     pchar()
152     {
153         if(rand(4))
154             return std::string(1,
155                 "0123456789"
156                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
157                 "abcdefghijklmnopqrstuvwxyz"
158                 ":@&=+$,"[rand(69)]);
159         std::string s = "%";
160         s += "0123456789abcdef"[rand(16)];
161         s += "0123456789abcdef"[rand(16)];
162         return s;
163     }
164 
165     char
uric()166     uric()
167     {
168         return 'a';
169     }
170 
171     char
uric_no_slash()172     uric_no_slash()
173     {
174         return 'a';
175     }
176 
177     std::string
param()178     param()
179     {
180         std::string s;
181         while(rand(2))
182             s += pchar();
183         return s;
184     }
185 
186     std::string
query()187     query()
188     {
189         std::string s;
190         while(rand(2))
191             s += uric();
192         return s;
193     }
194 
195     std::string
userinfo()196     userinfo()
197     {
198         std::string s;
199         while(rand(2))
200             s += "a";
201         return s;
202     }
203 
204     /*  authority     = server | reg_name
205 
206         reg_name      = 1*( unreserved | escaped | "$" | "," |
207                         ";" | ":" | "@" | "&" | "=" | "+" )
208 
209         server        = [ [ userinfo "@" ] hostport ]
210         userinfo      = *( unreserved | escaped |
211                          ";" | ":" | "&" | "=" | "+" | "$" | "," )
212 
213         hostport      = host [ ":" port ]
214         host          = hostname | IPv4address
215         hostname      = *( domainlabel "." ) toplabel [ "." ]
216         domainlabel   = alphanum | alphanum *( alphanum | "-" ) alphanum
217         toplabel      = alpha | alpha *( alphanum | "-" ) alphanum
218         IPv4address   = 1*digit "." 1*digit "." 1*digit "." 1*digit
219         port          = *digit
220     */
221     std::string
server()222     server()
223     {
224         std::string s;
225         if(rand(2))
226             s += userinfo() + "@";
227         return s;
228     }
229 
230     std::string
reg_name()231     reg_name()
232     {
233         std::string s;
234         s = "a";
235         while(rand(2))
236             s += "a";
237         return s;
238     }
239 
240     std::string
authority()241     authority()
242     {
243         if(rand(2))
244             return server();
245         return reg_name();
246     }
247 
248     std::string
opaque_part()249     opaque_part()
250     {
251         std::string s;
252         s += uric_no_slash();
253         while(rand(2))
254             s += uric();
255         return s;
256     }
257 
258     /*  abs_path      = "/"  path_segments
259         path_segments = segment *( "/" segment )
260         segment       = *pchar *( ";" param )
261         param         = *pchar
262         pchar         = unreserved | escaped |
263                         ":" | "@" | "&" | "=" | "+" | "$" | ","
264     */
265     std::string
abs_path()266     abs_path()
267     {
268         std::string s = "/";
269         for(;;)
270         {
271             while(rand(2))
272                 s += pchar();
273             while(rand(2))
274                 s += ";" + param();
275             if(rand(2))
276                 break;
277             s.append("/");
278         }
279         return s;
280     }
281 
282     /*  net_path      = "//" authority [ abs_path ]
283     */
284     std::string
net_path()285     net_path()
286     {
287         std::string s = "//";
288         s += authority();
289         if(rand(2))
290             s += abs_path();
291         return s;
292     }
293 
294     /*  absoluteURI   = scheme ":" ( hier_part | opaque_part )
295         scheme        = alpha *( alpha | digit | "+" | "-" | "." )
296         hier_part     = ( net_path | abs_path ) [ "?" query ]
297         abs_path      = "/"  path_segments
298         query         = *uric
299         opaque_part   = uric_no_slash *uric
300     */
301     std::string
abs_uri()302     abs_uri()
303     {
304         std::string s;
305         s = scheme() + ":";
306         if(rand(2))
307         {
308             if(rand(2))
309                 s += net_path();
310             else
311                 s += abs_path();
312             if(rand(2))
313                 s += "?" + query();
314         }
315         else
316         {
317             s += opaque_part();
318         }
319         return s;
320     }
321 
322     std::string
target()323     target()
324     {
325         //switch(rand(4))
326         switch(1)
327         {
328         case 0: return abs_uri();
329         case 1: return abs_path();
330         case 2: return authority();
331         default:
332         case 3: break;
333         }
334         return "*";
335     }
336 
337     std::string
token()338     token()
339     {
340         static char constexpr valid[78] =
341             "!#$%&\'*+-." "0123456789" "ABCDEFGHIJ" "KLMNOPQRST"
342             "UVWXYZ^_`a"  "bcdefghijk" "lmnopqrstu" "vwxyz|~";
343         std::string s;
344         s.append(1, valid[rand(77)]);
345         while(rand(4))
346             s.append(1, valid[rand(77)]);
347         return s;
348     }
349 
350 #if 0
351     std::string
352     target()
353     {
354         static char constexpr alpha[63] =
355             "0123456789" "ABCDEFGHIJ" "KLMNOPQRST"
356             "UVWXYZabcd" "efghijklmn" "opqrstuvwx" "yz";
357         std::string s;
358         s = "/";
359         while(rand(4))
360             s.append(1, alpha[rand(62)]);
361         return s;
362     }
363 #endif
364 
365     std::string
field()366     field()
367     {        static char const* const list[289] =
368         {
369             "A-IM",
370             "Accept", "Accept-Additions", "Accept-Charset", "Accept-Datetime", "Accept-Encoding",
371             "Accept-Features", "Accept-Language", "Accept-Patch", "Accept-Ranges", "Age", "Allow",
372             "ALPN", "Also-Control", "Alt-Svc", "Alt-Used", "Alternate-Recipient", "Alternates",
373             "Apply-To-Redirect-Ref", "Approved", "Archive", "Archived-At", "Article-Names",
374             "Article-Updates", "Authentication-Info", "Authentication-Results", "Authorization",
375             "Auto-Submitted", "Autoforwarded", "Autosubmitted", "Base", "Bcc", "Body", "C-Ext",
376             "C-Man", "C-Opt", "C-PEP", "C-PEP-Info", "Cache-Control",
377             "CalDAV-Timezones", "Cc", "Close", "Comments", /*"Connection",*/ "Content-Alternative",
378             "Content-Base", "Content-Description", "Content-Disposition", "Content-Duration",
379             "Content-Encoding", "Content-features", "Content-ID", "Content-Identifier",
380             "Content-Language", /*"Content-Length",*/ "Content-Location", "Content-MD5",
381             "Content-Range", "Content-Return", "Content-Script-Type", "Content-Style-Type",
382             "Content-Transfer-Encoding", "Content-Type", "Content-Version", "Control", "Conversion",
383             "Conversion-With-Loss", "Cookie", "Cookie2", "DASL", "DAV", "DL-Expansion-History", "Date",
384             "Date-Received", "Default-Style", "Deferred-Delivery", "Delivery-Date", "Delta-Base",
385             "Depth", "Derived-From", "Destination", "Differential-ID", "Digest",
386             "Discarded-X400-IPMS-Extensions", "Discarded-X400-MTS-Extensions", "Disclose-Recipients",
387             "Disposition-Notification-Options", "Disposition-Notification-To", "Distribution",
388             "DKIM-Signature", "Downgraded-Bcc", "Downgraded-Cc", "Downgraded-Disposition-Notification-To",
389             "Downgraded-Final-Recipient", "Downgraded-From", "Downgraded-In-Reply-To",
390             "Downgraded-Mail-From", "Downgraded-Message-Id", "Downgraded-Original-Recipient",
391             "Downgraded-Rcpt-To", "Downgraded-References", "Downgraded-Reply-To", "Downgraded-Resent-Bcc",
392             "Downgraded-Resent-Cc", "Downgraded-Resent-From", "Downgraded-Resent-Reply-To",
393             "Downgraded-Resent-Sender", "Downgraded-Resent-To", "Downgraded-Return-Path",
394             "Downgraded-Sender", "Downgraded-To", "Encoding", "Encrypted", "ETag", "Expect",
395             "Expires", "Expiry-Date", "Ext", "Followup-To", "Forwarded", "From",
396             "Generate-Delivery-Report", "GetProfile", "Hobareg", "Host", "HTTP2-Settings", "IM", "If",
397             "If-Match", "If-Modified-Since", "If-None-Match", "If-Range", "If-Schedule-Tag-Match",
398             "If-Unmodified-Since", "Importance", "In-Reply-To", "Incomplete-Copy", "Injection-Date",
399             "Injection-Info", "Keep-Alive", "Keywords", "Label", "Language", "Last-Modified",
400             "Latest-Delivery-Time", "Lines", "Link", "List-Archive", "List-Help", "List-ID",
401             "List-Owner", "List-Post", "List-Subscribe", "List-Unsubscribe", "Location", "Lock-Token",
402             "Man", "Max-Forwards", "Memento-Datetime", "Message-Context", "Message-ID", "Message-Type",
403             "Meter", "MIME-Version", "MMHS-Exempted-Address", "MMHS-Extended-Authorisation-Info",
404             "MMHS-Subject-Indicator-Codes", "MMHS-Handling-Instructions", "MMHS-Message-Instructions",
405             "MMHS-Codress-Message-Indicator", "MMHS-Originator-Reference", "MMHS-Primary-Precedence",
406             "MMHS-Copy-Precedence", "MMHS-Message-Type", "MMHS-Other-Recipients-Indicator-To",
407             "MMHS-Other-Recipients-Indicator-CC", "MMHS-Acp127-Message-Identifier", "MMHS-Originator-PLAD",
408             "MT-Priority", "Negotiate", "Newsgroups", "NNTP-Posting-Date", "NNTP-Posting-Host",
409             "Obsoletes", "Opt", "Ordering-Type", "Organization", "Origin",
410             "Original-Encoded-Information-Types", "Original-From", "Original-Message-ID",
411             "Original-Recipient", "Original-Sender", "Originator-Return-Address", "Original-Subject",
412             "Overwrite", "P3P", "Path", "PEP", "PICS-Label", "Pep-Info", "Position", "Posting-Version",
413             "Pragma", "Prefer", "Preference-Applied", "Prevent-NonDelivery-Report", "Priority",
414             "ProfileObject", "Protocol", "Protocol-Info", "Protocol-Query", "Protocol-Request",
415             "Proxy-Authenticate", "Proxy-Authentication-Info", "Proxy-Authorization", "Proxy-Features",
416             "Proxy-Instruction", "Public", "Public-Key-Pins", "Public-Key-Pins-Report-Only", "Range",
417             "Received", "Received-SPF", "Redirect-Ref", "References", "Referer", "Relay-Version",
418             "Reply-By", "Reply-To", "Require-Recipient-Valid-Since", "Resent-Bcc", "Resent-Cc",
419             "Resent-Date", "Resent-From", "Resent-Message-ID", "Resent-Reply-To", "Resent-Sender",
420             "Resent-To", "Retry-After", "Return-Path", "Safe", "Schedule-Reply", "Schedule-Tag",
421             "Sec-WebSocket-Accept", "Sec-WebSocket-Extensions", "Sec-WebSocket-Key",
422             "Sec-WebSocket-Protocol", "Sec-WebSocket-Version", "Security-Scheme", "See-Also", "Sender",
423             "Sensitivity", "Server", "Set-Cookie", "Set-Cookie2",
424             "SetProfile", "SLUG", "SoapAction", "Solicitation", "Status-URI", "Strict-Transport-Security",
425             "Subject", "Summary", "Supersedes", "Surrogate-Capability", "Surrogate-Control", "TCN",
426             "TE", "Timeout", "To", "Trailer", /*"Transfer-Encoding",*/ "URI", /*"Upgrade",*/ "User-Agent",
427             "Variant-Vary", "Vary", "VBR-Info", "Via", "WWW-Authenticate", "Want-Digest", "Warning",
428             "X400-Content-Identifier", "X400-Content-Return", "X400-Content-Type", "X400-MTS-Identifier",
429             "X400-Originator", "X400-Received", "X400-Recipients", "X400-Trace", "X-Frame-Options", "Xref"
430         };
431         return list[rand(289)];
432     }
433 
434     std::string
text()435     text()
436     {
437         std::string s;
438         while(rand(3))
439         {
440             for(;;)
441             {
442                 char c = rand<char>(256);
443                 if(detail::is_text(c))
444                 {
445                     s.append(1, c);
446                     break;
447                 }
448             }
449         }
450         return s;
451     }
452 
453     std::string
value()454     value()
455     {
456         std::string s;
457         while(rand(3))
458         {
459             if(rand(5))
460             {
461                 s.append(text());
462             }
463             else
464             {
465                 // LWS
466                 if(! rand(4))
467                     s.append("\r\n");
468                 s.append(1, rand(2) ? ' ' : '\t');
469                 while(rand(2))
470                     s.append(1, rand(2) ? ' ' : '\t');
471             }
472         }
473         return s;
474     }
475 
476     template<class DynamicBuffer>
477     void
fields(DynamicBuffer & db)478     fields(DynamicBuffer& db)
479     {
480         auto os = ostream(db);
481         while(rand(6))
482             os <<
483                 field() <<
484                 (rand(4) ? ": " : ":") <<
485                 value() <<
486                 "\r\n";
487     }
488 
489     template<class DynamicBuffer>
490     void
body(DynamicBuffer & db)491     body(DynamicBuffer& db)
492     {
493         if(! rand(4))
494         {
495             ostream(db) <<
496                 "Content-Length: 0\r\n\r\n";
497             return;
498         }
499         if(rand(2))
500         {
501             auto const len = rand(500);
502             ostream(db) <<
503                 "Content-Length: " << len << "\r\n\r\n";
504             auto mb = db.prepare(len);
505             for(auto it = net::buffer_sequence_begin(mb);
506                 it != net::buffer_sequence_end(mb);
507                 ++it)
508             {
509                 net::mutable_buffer b = *it;
510                 auto p = static_cast<char*>(b.data());
511                 auto n = b.size();
512                 while(n--)
513                     *p++ = static_cast<char>(32 + rand(26+26+10+6));
514             }
515             db.commit(len);
516         }
517         else
518         {
519             auto len = rand(500);
520             ostream(db) <<
521                 "Transfer-Encoding: chunked\r\n\r\n";
522             while(len > 0)
523             {
524                 auto n = (std::min)(1 + rand(300), len);
525                 len -= n;
526                 ostream(db) <<
527                     to_hex(n) << "\r\n";
528                 auto mb = db.prepare(n);
529                 for(auto it = net::buffer_sequence_begin(mb);
530                     it != net::buffer_sequence_end(mb);
531                     ++it)
532                 {
533                     net::mutable_buffer b = *it;
534                     auto p = static_cast<char*>(b.data());
535                     auto m = b.size();
536                     while(m--)
537                         *p++ = static_cast<char>(32 + rand(26+26+10+6));
538                 }
539                 db.commit(n);
540                 ostream(db) << "\r\n";
541             }
542             ostream(db) << "0\r\n\r\n";
543         }
544     }
545 
546     template<class DynamicBuffer>
547     void
request(DynamicBuffer & db)548     request(DynamicBuffer& db)
549     {
550         ostream(db) <<
551             method() << " " << target() << " HTTP/1.1\r\n";
552         fields(db);
553         body(db);
554     }
555 
556     template<class DynamicBuffer>
557     void
response(DynamicBuffer & db)558     response(DynamicBuffer& db)
559     {
560         ostream(db) <<
561             "HTTP/1." <<
562             (rand(2) ? "0" : "1") << " " <<
563             (100 + rand(401)) << " " <<
564             token() <<
565             "\r\n";
566         fields(db);
567         body(db);
568         ostream(db) << "\r\n";
569     }
570 };
571 
572 using message_fuzz = message_fuzz_t<>;
573 
574 template<class Good, class Bad>
575 void
chunkExtensionsTest(Good const & good,Bad const & bad)576 chunkExtensionsTest(
577     Good const& good, Bad const& bad)
578 {
579     good("");
580     good(";x");
581     good(";x;y");
582     good(";x=y");
583     good(";x;y=z");
584     good(" ;x");
585     good("\t;x");
586     good(" \t;x");
587     good("\t ;x");
588     good(" ; x");
589     good(" ;\tx");
590     good("\t ; \tx");
591     good(";x= y");
592     good(" ;x= y");
593     good(" ; x= y");
594     good(R"(;x="\"")");
595     good(R"(;x="\\")");
596     good(R"(;x;y=z;z="\"";p="\\";q="1\"2\\")");
597 
598     bad(" ");
599     bad(";");
600     bad("=");
601     bad(" ;");
602     bad("; ");
603     bad(" ; ");
604     bad(" ; x ");
605     bad(";x =");
606     bad(";x = ");
607     bad(";x==");
608 }
609 
610 } // http
611 } // beast
612 } // boost
613 
614 #endif
615