{"id":2350,"date":"2018-01-28T18:49:35","date_gmt":"2018-01-28T23:49:35","guid":{"rendered":"http:\/\/osric.com\/chris\/accidental-developer\/?p=2350"},"modified":"2018-01-28T18:49:35","modified_gmt":"2018-01-28T23:49:35","slug":"using-nc-netcat-to-make-an-http-request","status":"publish","type":"post","link":"https:\/\/osric.com\/chris\/accidental-developer\/2018\/01\/using-nc-netcat-to-make-an-http-request\/","title":{"rendered":"Using nc (netcat) to make an HTTP request"},"content":{"rendered":"<p>I must have had some reason for wanting to do this, although I can&#8217;t think of why right now. <a href=\"https:\/\/curl.haxx.se\/\">curl<\/a> is an excellent tool for ad hoc HTTP requests.<\/p>\n<p>On a server running Apache 2.4.6, first I tried:<\/p>\n<p><code># nc 127.0.0.1 80<br \/>\nGET \/ HTTP\/1.1<\/code><\/p>\n<p>Which returned a <code>HTTP\/1.1 400 Bad Request<\/code> error.<\/p>\n<p>Next I tried:<\/p>\n<p><code># printf \"GET \/index.html HTTP\/1.1\\r\\n\\r\\n\" | nc 127.0.0.1 80<\/code><\/p>\n<p>Which also returned a <code>HTTP\/1.1 400 Bad Request<\/code> error.<\/p>\n<p>I decided to take a look at what curl was sending, since that was working:<\/p>\n<p><code># curl -v http:\/\/127.0.0.1<br \/>\n* About to connect() to 127.0.0.1 port 80 (#0)<br \/>\n*   Trying 127.0.0.1...<br \/>\n* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)<br \/>\n&gt; GET \/ HTTP\/1.1<br \/>\n&gt; User-Agent: curl\/7.29.0<br \/>\n&gt; Host: 127.0.0.1<br \/>\n&gt; Accept: *\/*<br \/>\n...<\/code><\/p>\n<p>I put the same headers (with a modified <code>User-Agent<\/code>) into my <code>printf<\/code> statement:<\/p>\n<p><code># printf \"GET \/index.html HTTP\/1.1\\r\\nUser-Agent: nc\/0.0.1\\r\\nHost: 127.0.0.1\\r\\nAccept: *\/*\\r\\n\\r\\n\" | nc 127.0.0.1 80<br \/>\nHTTP\/1.1 200 OK<br \/>\nDate: Sun, 28 Jan 2018 23:11:04 GMT<br \/>\nServer: Apache\/2.4.6 (CentOS) PHP\/5.4.16<br \/>\nLast-Modified: Sun, 28 Jan 2018 20:10:37 GMT<br \/>\nETag: \"78-563dbb912bfe0\"<br \/>\nAccept-Ranges: bytes<br \/>\nContent-Length: 120<br \/>\nContent-Type: text\/html; charset=UTF-8<\/p>\n<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html&gt;<br \/>\n&lt;head&gt;<br \/>\n&lt;title&gt;well that worked&lt;\/title&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body&gt;<br \/>\n&lt;h1&gt;apache is running&lt;\/h1&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt;<\/code><\/p>\n<p>That worked!<\/p>\n<p>I eliminated the <code>User-Agent<\/code> the <code>Accept<\/code> headers and it still worked, so the missing <code>Host<\/code> header was the cause of my problems. I swear I&#8217;ve done this before without a <code>Host<\/code> header though.<\/p>\n<p>I looked up the <a href=\"https:\/\/tools.ietf.org\/html\/rfc2616#section-5.2\">HTTP specification<\/a>, and as described in section 5.2 of the RFC:<\/p>\n<blockquote><p>\n1. If Request-URI is an absoluteURI, the host is part of the Request-URI. Any Host header field value in the request MUST be ignored.<\/p>\n<p>2. If the Request-URI is not an absoluteURI, and the request includes a Host header field, the host is determined by the Host header field value.<\/p>\n<p>3. If the host as determined by rule 1 or 2 is not a valid host on the server, the response MUST be a 400 (Bad Request) error message.<\/p>\n<p>Recipients of an HTTP\/1.0 request that lacks a Host header field MAY attempt to use heuristics (e.g., examination of the URI path for something unique to a particular host) in order to determine what exact resource is being requested.<\/p><\/blockquote>\n<p>I could not get it to work with an <code>absoluteURI<\/code>, even using the example in the RFC. However I did find that I could ignore the <code>Host<\/code> header if I specified HTTP\/1.0:<\/p>\n<p><code># printf \"GET \/ HTTP\/1.0\\r\\n\\r\\n\" | nc 127.0.0.1 80<\/code><\/p>\n<p>I also found that Apache didn&#8217;t care what the Host header was when using HTTP\/1.1, just so long as something was there:<\/p>\n<p><code># printf \"GET \/ HTTP\/1.1\\r\\nHost: z\\r\\n\\r\\n\" | nc 127.0.0.1 80<\/code><\/p>\n<p>That&#8217;s a little odd. I did not specify a <code>ServerName<\/code> in my Apache config, but even after I specified <code>ServerName 127.0.0.1:80<\/code> in <code>\/etc\/httpd\/conf\/httpd.conf<\/code> and restarted Apache, it still required the <code>Host<\/code> header and it still didn&#8217;t care what the content of the <code>Host<\/code> header was (so long as it was not empty).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>My attempts to send an HTTP GET request via netcat (nc) were failing, with 400 Bad Request errors returned by the server. I discovered that I could get a successful 200 OK response if I specified HTTP\/1.0 instead of HTTP\/1.1, or if I included the Host header in my HTTP request using HTTP\/1.1.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[232],"tags":[73,486,485],"class_list":["post-2350","post","type-post","status-publish","format-standard","hentry","category-tips-tricks","tag-apache","tag-nc","tag-netcat"],"_links":{"self":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/2350","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/comments?post=2350"}],"version-history":[{"count":2,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/2350\/revisions"}],"predecessor-version":[{"id":2359,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/2350\/revisions\/2359"}],"wp:attachment":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/media?parent=2350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/categories?post=2350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/tags?post=2350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}