<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>App Engine on bramp.net</title>
    <link>https://blog.bramp.net/</link>
    <description>Recent content in App Engine on bramp.net</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-GB</language>
    <lastBuildDate>Mon, 20 Feb 2017 12:50:31 -0800</lastBuildDate>
    <atom:link href="https://blog.bramp.net/tags/app-engine/" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Building a better “What&#39;s My IP?” site</title>
      <link>https://blog.bramp.net/post/2017/02/20/building-a-better-whats-my-ip-site/</link>
      <pubDate>Mon, 20 Feb 2017 12:50:31 -0800</pubDate>
      
      <guid>https://blog.bramp.net/post/2017/02/20/building-a-better-whats-my-ip-site/</guid>
      <description><p>Occasionally I’m curious to know what network my device is using, if it has a IPv6 address, and who owns the address space. For example, when in a coffee shop I’m curious to know their ISP, or when roaming internationally I’m always curious to understand which mobile operator’s IP address gets assigned to device.</p>
<p>Most &ldquo;<a href="https://www.google.com/search?q=What%E2%80%99s+my+IP+address">What’s my IP address</a>&rdquo; sites, will either only show you one of your IPv4, or IPv6. It won’t do a DNS lookup, and they rarely do a WHOIS lookup.  Doing all these things, shouldn’t be too hard, so I figured in a weekend I could hack together a site to do this.</p>
<p>This blog post explains the creation of <a href="http://ip.bramp.net">ip.bramp.net</a>.</p>
<div class="text-center">
  <img src="screenshot.png" alt="Screenshot of ip.bramp.net in action"></img>
</div>
<h2 id="how-to-get-both-ipv4-and-ipv6-address">How to get both IPv4 and IPv6 address?</h2>
<p>When navigating to a website your browser makes a connection, normally over one of IPv4, or IPv6. Which one is based on the DNS records available for the website’s domain, and the preference of your OS and browser. Thus your web server sees a single incoming connection, with a single remote address. This address is typically stored in a variable named REMOTE_ADDR. Most &ldquo;What’s my IP&rdquo; sites then display this variable back to the user as their IP address. However, as I’d like to see both IPv4 and IPv6 addresses, I need to somehow force the browser to make two requests, one over each.</p>
<p>There is no API to tell a browser to use IPv6 over IPv4, however, I use a trick with two domain names. Namely, I have ip4.bramp.net, and ip6.bramp.net. Both resolve to the same web server, but the former only has <a href="https://tools.ietf.org/html/rfc1035">DNS A records</a>, and the latter only <a href="https://tools.ietf.org/html/rfc3596">DNS AAAA records</a>. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ dig ip4.bramp.net
</span></span><span class="line"><span class="cl">ip4.bramp.net.		300	IN	A	216.239.32.21
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ dig AAAA ip6.bramp.net
</span></span><span class="line"><span class="cl">ip6.bramp.net.		291	IN	AAAA	2001:4860:4802:32::15
</span></span></code></pre></div><p>This forces the connection to be over either IPv4, or IPv6. If a browser doesn’t support IPv6, then the connection is never made, and an error is returned. Interesting side note, some browsers uses a technique called <a href="https://en.wikipedia.org/wiki/Happy_Eyeballs">Happy Eyeballs</a>, which tries to connect over both concurrently, but abandons the slower or worse behaving of the two connections.</p>
<h2 id="how-do-you-make-two-requests-from-one-page">How do you make two requests from one page?</h2>
<p>To force the site to make requests to both of these domains, I issue two <a href="https://en.wikipedia.org/wiki/Ajax_(programming)">AJAX</a> queries. The typical flow looks like:</p>
<div class="text-center">
  <object data="diagram.svg" type="image/svg+xml" height="364" width="583" alt="diagram of AJAX calls">
    <img src="diagram.png" />
  </object>
</div>
<!--
```
user->ip.bramp.net: GET /
ip.bramp.net->user: <html...>
user->ip4.bramp.net: GET ip4.bramp.net/json
ip4.bramp.net->user: {address: 1.2.3.4}
user->ip6.bramp.net: GET ip6.bramp.net/json
ip6.bramp.net->user: {address: 2001:db8::1}
```
-->
<p>These AJAX queries return a simple JSON object, containing information about the requesting user. In my application an example response may look like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;RemoteAddrFamily&#34;</span><span class="p">:</span> <span class="s2">&#34;IPv6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;RemoteAddr&#34;</span><span class="p">:</span>       <span class="s2">&#34;2601:646:c200:b466:b446:ff32:b227:a53c&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This response can then be used to update the page, to display the appropriate address.</p>
<p>An experienced reader may be aware of some security issues with making AJAX request to a different domain. In particular, there are subtle ways in which a malicious site could abuse your AJAX endpoints. This is easily fixed by using cross-origin resource sharing (<a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a>) headers, in particular ip4.bramp.net, and ip6.bramp.net return the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ curl -v ip4.bramp.net/json
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt; HTTP/1.1 <span class="m">200</span> OK
</span></span><span class="line"><span class="cl">&lt; Content-Type: application/json
</span></span><span class="line"><span class="cl">&lt; Access-Control-Allow-Origin: http://ip.bramp.net
</span></span></code></pre></div><p>The last header, explicitly allows requests from ip.bramp.net only, and thus forbids requests from other sites. Without this header, the AJAX request would be issued, but then rejected by the browser.</p>
<h2 id="what-about-the-reverse-dns-and-whois">What about the reverse DNS and WHOIS?</h2>
<p>I noted I wanted to display both the reverse DNS, and WHOIS records. This is something the browser doesn’t support, but the server side could. Thus as part of processing the /json AJAX request, the application makes various additional requests to remote DNS and WHOIS servers.</p>
<p>To reverse lookup a IP address, you need to issue a <a href="https://tools.ietf.org/html/rfc1035">PTR DNS request</a>. This is a special DNS request which requires the IP address to be formatted as a in-addr.arpa or ip6.arpa name. For example, the IP address 1.2.3.4, would become 4.3.2.1.in-addr.arpa. Then when a request is sent for that in-addr.arpa. name, the reverse DNS is returned.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ dig PTR 4.3.2.1.in-addr.arpa.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">4.3.2.1.in-addr.arpa.	74312	IN	PTR	c-1-2-3-4.example.com.
</span></span></code></pre></div><p>The in-addr.arpa transformation, and lookup is commonly provided by <a href="https://linux.die.net/man/3/getaddrinfo">getaddrinfo(3)</a> function, which makes this easy to do.</p>
<p>The WHOIS lookup is a little bit more complex. Each domain is represented by a different WHOIS server, that can be determined by the ccTLD or TLD of the domain. However, with IP addresses, you must identify the Regional Internet Registry (<a href="https://en.wikipedia.org/wiki/Regional_Internet_registry">RIR</a>) that owns the IP space. Sadly there is not a trivial mapping, so instead I issue a WHOIS query to the Internet Assigned Numbers Authority (<a href="https://en.wikipedia.org/wiki/Internet_Assigned_Numbers_Authority">IANA</a>), who replies with the RIR which owns the IP address. From there, I can query the correct registry directly, typically one of <a href="https://en.wikipedia.org/wiki/AFRINIC">AFRINIC</a>, <a href="https://en.wikipedia.org/wiki/American_Registry_for_Internet_Numbers">ARIN</a>, <a href="https://en.wikipedia.org/wiki/Asia-Pacific_Network_Information_Centre">APNIC</a>, <a href="https://en.wikipedia.org/wiki/Latin_America_and_Caribbean_Network_Information_Centre">LACNIC</a>, or <a href="https://en.wikipedia.org/wiki/R%C3%A9seaux_IP_Europ%C3%A9ens_Network_Coordination_Centre">RIPE NCC</a>. Thus a typical WHOIS request looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ whois -h whois.iana.org 1.2.3.4
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">% IANA WHOIS server
</span></span><span class="line"><span class="cl">% <span class="k">for</span> more information on IANA, visit http://www.iana.org
</span></span><span class="line"><span class="cl">% This query returned <span class="m">1</span> object
</span></span><span class="line"><span class="cl">refer:        whois.apnic.net
</span></span><span class="line"><span class="cl">inetnum:      1.0.0.0 - 1.255.255.255
</span></span><span class="line"><span class="cl">organisation: APNIC
</span></span><span class="line"><span class="cl">status:       ALLOCATED
</span></span><span class="line"><span class="cl">whois:        whois.apnic.net
</span></span><span class="line"><span class="cl">changed:      2010-01
</span></span><span class="line"><span class="cl">source:       IANA
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ whois -h whois.apnic.net 1.2.3.4
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">% <span class="o">[</span>whois.apnic.net<span class="o">]</span>
</span></span><span class="line"><span class="cl">% Whois data copyright terms    http://www.apnic.net/db/dbcopyright.html
</span></span><span class="line"><span class="cl">% Information related to <span class="s1">&#39;1.2.3.0 - 1.2.3.255&#39;</span>
</span></span><span class="line"><span class="cl">inetnum:        1.2.3.0 - 1.2.3.255
</span></span><span class="line"><span class="cl">netname:        Example-prefix
</span></span><span class="line"><span class="cl">descr:          APNIC Example Project
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">% This query was served by the APNIC Whois Service version 1.69.1-APNICv1r0
</span></span></code></pre></div><p>Both the reverse DNS and WHOIS response is returned as part of the AJAX JSON response.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;RemoteAddrFamily&#34;</span><span class="p">:</span>  <span class="s2">&#34;IPv4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;RemoteAddr&#34;</span><span class="p">:</span>        <span class="s2">&#34;1.2.3.4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;RemoteAddrReverse&#34;</span><span class="p">:</span> <span class="s2">&#34;c-1-2-3-4.example.com.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;RemoteAddrWhois&#34;</span><span class="p">:</span>   <span class="s2">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    % IANA WHOIS server
</span></span></span><span class="line"><span class="cl"><span class="s2">    % for more information on IANA, visit http://www.iana.org
</span></span></span><span class="line"><span class="cl"><span class="s2">    % This query returned 1 object
</span></span></span><span class="line"><span class="cl"><span class="s2">    refer:        whois.apnic.net
</span></span></span><span class="line"><span class="cl"><span class="s2">    inetnum:      1.0.0.0 - 1.255.255.255
</span></span></span><span class="line"><span class="cl"><span class="s2">    organisation: APNIC
</span></span></span><span class="line"><span class="cl"><span class="s2">    status:       ALLOCATED
</span></span></span><span class="line"><span class="cl"><span class="s2">    ...
</span></span></span><span class="line"><span class="cl"><span class="s2">  &#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="what-about-proxies">What about proxies?</h2>
<p>Many users are behind proxies, which connects to the webserver on the user’s behalf. Thus the REMOTE_ADDR is the address of the proxy, not the actual user.  Some proxies have a workaround for this, by placing the user’s real IP address in the <a href="https://en.wikipedia.org/wiki/X-Forwarded-For">X-Forwarded-For</a> (XFF), or newer <a href="https://tools.ietf.org/html/rfc7239">Forwarded</a> HTTP header. However, these headers are easily set by the user, so can not be trusted. Thus for the moment I ignore these headers, and will instead display the proxy’s IP address. It is conceivable to create a whitelist of proxies, that I would trust the XFF header, but for now I didn’t want that headache. Especially since the server side issues requests to external hosts, if I trusted the XFF header, an abusive user could use my site as a proxy, or even use my site as a relay to <a href="https://en.wikipedia.org/wiki/Denial-of-service_attack">denial of service</a> these remote servers.</p>
<h2 id="tying-this-all-together">Tying this all together</h2>
<p>Server side I use <a href="https://cloud.google.com/appengine/">App Engine</a>, and <a href="https://golang.org/">Go</a>. Why? Because I wanted to play with App Engine, and I’m a fan of Go right now. On the client side I use <a href="http://getbootstrap.com">Bootstrap</a> to make the page look nice, and <a href="https://angularjs.org">AngularJS</a>. AngularJS because I’m familiar with it, and because it is really easy to issue an AJAX requests and transform the result into a web page.</p>
<p>I like App Engine, because of the <a href="https://en.wikipedia.org/wiki/Platform_as_a_service">PaaS</a> model. It keeps my costs down, and I don’t need to setup a virtual machine, or create docker images. Instead I just write a single binary and upload it. However, App Engine does place some restrictions on what I can do, in particular limiting outbound connections to ones made via its own library. Thus I had to jump through a few hoops to make the reverse DNS and WHOIS requests. Instead of using <a href="https://linux.die.net/man/3/getaddrinfo">getaddrinfo(3)</a>, I had to issue DNS requests myself using App Engine’s socket library and my own UDP packets on port 53. Luckily the Go DNS library, <a href="https://github.com/miekg/dns">miekg/dns</a>, makes this relatively easy.  Similarly I had to implement the WHOIS lookup by hand, but again aided by a library, this time <a href="https://github.com/domainr/whois">domainr/whois</a>.</p>
<p>In conclusion, though the use of multiple domain names, some AJAX queries, and server side support. I was able to make &ldquo;(a better) What&rsquo;s My IP Address?&rdquo; site in under a weekend.</p>
<p>Check out the <a href="https://github.com/bramp/myip">full source on github</a>, or view the site at <a href="http://ip.bramp.net/">ip.bramp.net</a></p>
</description>
    </item>
    
  </channel>
</rss>
