/services/dnsid.cgi
#!/usr/bin/env python
"""
DNS ID Generator
Author: Sean B. Palmer, inamidst.com
"""
import cgitb; cgitb.enable()
import sys, os, re, sha, cgi, textwrap
from base64 import b32encode
form = cgi.FieldStorage()
form.__call__ = lambda s: form[s].value
PATH_INFO = os.environ.get('PATH_INFO', '')
QUERY_STRING = os.environ.get('QUERY_STRING', '')
REQUEST_URI = os.environ.get('REQUEST_URI', '')
script = os.path.basename(REQUEST_URI.rstrip('/')).split('?')[0]
r_encode = re.compile(r'([^A-Za-z0-9-])')
r_email = re.compile(r'''(?x)^
[A-Za-z0-9!#$%&'*+/=?^`{|+~_-]+
(\.[A-Za-z0-9!#$%&'*+/=?^`{|+~_-]+)* @
([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9]\.)*
([A-Za-z]|[A-Za-z][A-Za-z0-9-]*[A-Za-z0-9])
$''')
base = 'com.inamidst.mbox.'
def validate(email):
if r_email.match(email):
return True
return False
def base32(email, base=base):
hash = sha.new(email).digest()
part = b32encode(hash[:10]).lower()
return base + part.rstrip('=\n')
def sha1sum(email, base=base):
return base + sha.new(email).hexdigest()
def encode(part):
assert part[0].isalpha() or part[0].isdigit(), part
assert part[-1].isalpha() or part[-1].isdigit(), part
if len(part) < 3:
return part
middle = r_encode.sub(lambda m: '--%02X' % ord(m.group(1)), part[1:-1])
return part[0] + middle + part[-1]
def multipart(email, base=base):
# This is the grit-teeth function
name, host = email.split('@')
name = encode(name)
hostparts = list(reversed(host.split('.')))
emailparts = frozenset(hostparts + [encode(name)])
sep, i = 'at', 0
while sep in emailparts:
if i >= 1000000:
raise ValueError("Sorry, your email address is too whacky")
sep = 'sep' + str(i)
i += 1
parts = [sep] + list(hostparts) + [sep, name]
return base + '.'.join(parts)
def serve(status, headers, body):
sys.stdout.write("Status: %s\r\n" % status)
for header in headers.iteritems():
sys.stdout.write("%s: %s\r\n" % header)
sys.stdout.write("\r\n")
sys.stdout.write(str(body))
sys.exit()
def redirect(uri, temp=False):
if not uri.startswith('http:'):
from urlparse import urljoin
domain = os.environ.get('SERVER_NAME', 'localhost')
uri = urljoin('http://' + domain + REQUEST_URI, uri)
status = (301, 307)[temp]
headers = {'Location': uri, 'Content-Type': 'text/html'}
body = '<a href="%s">go!</a>' % uri
serve(status, headers, body)
def error(msg):
body = '<p>%s</p>' % cgi.escape(msg)
serve(501, {'Content-Type': 'text/html'}, body)
def dnsid(email):
result = []
if not validate(email):
result.append("Sorry, %s is not allowed by this system." % email)
result.append("If you're sure that it's a valid email, please ")
result.append("contact me at http://inamidst.com/sbp/contact ")
result.append("and ask me to laxen the allowed values. Thanks.")
return '\n'.join(result)
result.append("The owner of %s may use any of these DNS IDs: " % email)
result.append("")
result.append(" base32: %s" % base32(email))
result.append(" sha1sum: %s" % sha1sum(email))
result.append(" multipart: %s" % multipart(email))
result.append("")
result.append("-- ")
result.append("Sean B. Palmer, inamidst.com")
result.append("")
return '\n'.join(result)
def homepage():
print "Content-Type: text/html"
print
print textwrap.dedent("""\
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>DNS ID Service</title>
<link rel="stylesheet" type="text/css" href="/trove/simple.css" />
</head>
<body>
<div class="nav">
<a href="/"><img src="/trove/logo" alt="inamidst.com" /></a>
</div>
<h1>DNS ID Service</h1>
<p>This service converts email addresses into backwards domains starting
with "com.inamidst.mbox" for the owner of the email address to use. See
below for further details, including alternate ways to create these
identifiers.</p>
<form method="get" action=\"""" + REQUEST_URI + """">
<div>Email: <input type="text" name="email" />
<input type="submit" /></div/>
</form>
<h2>DNS ID?</h2>
<p>Usually, domain names are used in email addresses and HTTP URIs in the
form "example.com", with the top level domain (.com, .net, .co.uk etc.)
coming at the end. You may also have subdomains such as
"myname.example.com", and very occasionally you see ridiculous examples
such as "this.is.a.host.at.example.com".</p>
<p>Some systems, however, use these addresses—just the domain
names, with no paths—as local identifiers. For example, the Java
package-name convention makes use of this kind of identifier. Indeed,
this service was written in response to <a
href="http://swhack.com/logs/2005-06-04#T14-34-02">a discussion</a> about
<a href="http://www.w3.org/2001/tag/issues#siteData-36">TAG issue
siteData-36</a>, one of the possible solutions for which is to use these
reversed domain names as identifiers in HTTP header values.</p>
<p>The advantage that these types of identifier have over HTTP URIs is
that they're much more compact. The disadvantage is that people are much
less likely to own domains than they are to have, say, an email address.
Domain names were used as part of the <a
href="http://www.taguri.org/">tag: URI</a> proposal too, and the authors
of that proposal got around it simply by allowing emails too. But in most
other systems, this flexibility isn't possible. That's where this
service comes in.</p>
<h2>About This Service</h2>
<p>If you have an email address but no domain or subdomain, you can
simply have a subdomain at this site that contains your email address
encoded inside. You can use any one of three different variants of
different length and obfuscation level.</p>
<p>This is powered by a CGI written in <a
href="http://www.python.org/">Python</a>. It does require that you put
your email address in, of course, which means that it'll show up in my
server logs. I won't distribute or sell these addresses or anything, but
if you still want one of these DNS IDs without divulging your email
address, you still can. Simply download <a
href="/inside/services/dnsid">the source</a>, save it as dnsid.py, and
then run it a la ./dnsid.py name@example.org to generate the DNS IDs.</p>
<p>This was mainly written as a programming exercise rather than a
service for heavy use, but anyone is welcome to use it as long as they
don't expect hosting or any warranty etc. If you have any questions, do
feel free to <a href="/sbp/contact">contact me</a>.</p>
<address>
<a href="http://inamidst.com/sbp/">Sean B. Palmer</a>
</address>
</body>
</html>
""")
def runcgi(script=script):
if QUERY_STRING:
if form.has_key('email'):
if script: script += '/'
redirect('./' + script + form('email'))
else: print error("Needed an email form key.")
elif len(PATH_INFO) > 1:
headers = {'Content-Type': 'text/plain'}
serve(200, headers, dnsid(PATH_INFO.lstrip('/')))
else: homepage()
def help(argv):
return ("Usage: %s <email>\n" % argv[0]) + __doc__.strip()
def run(argv=None):
if argv is None:
argv = sys.argv
if len(argv) == 2:
print dnsid(argv[1])
else: print help(argv)
def main():
if os.environ.has_key('SCRIPT_NAME'):
runcgi()
else: run()
if __name__=="__main__":
main()