A small whois server and a nice holiday video

So here’s the most basic whois server in Python (connects to a PostgreSQL DB for the cached data):

Also, a funny holiday reminder that the NSA is spying on all of us!

ACLU NSA Vid [funny]

whois_limit

limiter.py

#!/usr/bin/python

import ast
import getpass
import hashlib
import os
import random
import re
import socket
import string
import sys
import time

def rstr(size):
	outp = ""
	for x in range(0, size):
		outp += random.choice(string.digits + string.ascii_uppercase + string.ascii_lowercase + "~!@#$%^&*-_=+?")
	return outp

fobj = open(sys.argv[1], "r")
conf = ast.literal_eval(fobj.read().strip())
fobj.close()

servport = conf["limiter"]["port"]
password = conf["limiter"]["pass"]
numtimes = conf["limiter"]["conn"]
timeouts = conf["limiter"]["time"]

prepad = rstr(8); pospad = rstr(8); cntrlist = []
addrlist = {}

sockobjc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockobjc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sockobjc.bind(("", servport))
sockobjc.listen(1)

while (1):
	(sockconn, sockaddr) = sockobjc.accept()
	print("conn open:", sockaddr)
	try:
		sockconn.settimeout(1)
		datalist = sockconn.recv(1024).strip().split(" ")
		try:
			sendflag = 0
			scounter = datalist[0].strip()
			inetaddr = datalist[1].strip()
			authhash = datalist.pop()
			sharedsec = (prepad + " " + password + " " + pospad)
			temphash = hashlib.sha256(sharedsec + " " + " ".join(datalist) + " " + sharedsec).hexdigest()
			if (temphash != authhash):
				sockconn.send("0" + " " + prepad + " " + pospad)
				sendflag = 1
			else:
				if (len(cntrlist) >= 65536):
					prepad = rstr(8); pospad = rstr(8); cntrlist = []
				if (scounter in cntrlist):
					sockconn.send("1" + " " + scounter)
					sendflag = 1
				else:
					cntrlist.append(scounter)
					if (not inetaddr in addrlist.keys()):
						addrlist[inetaddr] = [0, 0]
					prestime = time.time()
					if ((prestime - addrlist[inetaddr][0]) >= timeouts):
						addrlist[inetaddr][0] = prestime
						addrlist[inetaddr][1] = 0
					print("conn info:", sockaddr, scounter, datalist, addrlist[inetaddr])
					if (addrlist[inetaddr][1] < numtimes):
						addrlist[inetaddr][1] += 1
						sockconn.send("2" + " " + "ok")
						sendflag = 1
			if (sendflag != 1):
				sockconn.send("2" + " " + "no")
		except:
			pass
	except:
		pass
	try:
		print("conn closed:", sockaddr)
		sockconn.shutdown(socket.SHUT_RDWR)
		sockconn.close()
	except:
		pass

whoiss.py

#!/usr/bin/python

import ast
import getpass
import hashlib
import os
import psycopg2
import random
import re
import socket
import string
import sys
import time

def sstr(inpt):
	outp = ""
	yesl = (string.digits + string.ascii_uppercase + string.ascii_lowercase + "-_.")
	for chrl in inpt:
		if (chrl in yesl):
			outp += chrl
	return outp

def rnum(size):
	random.seed(time.time())
	outp = ""
	for x in range(0, size):
		outp += random.choice(string.digits)
	return outp

def lims(connaddr):
	global limhost, limport
	global limpass, limpre, limpos
	x = 0
	limresp = ["-1"]
	try:
		while ((x < 8) and (limresp[0] != "2")):
			limnumb = rnum(8)
			limobjc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			limobjc.connect((limhost, limport))
			limdata = (limnumb + " " + connaddr)
			limsec = (limpre + " " + limpass + " " + limpos)
			limhash = hashlib.sha256(limsec + " " + limdata + " " + limsec).hexdigest()
			limobjc.sendall(limdata + " " + limhash)
			limresp = limobjc.recv(1024).strip().split(" ")
			limobjc.close()
			if (len(limresp) < 1):
				limresp = ["-1"]
			if (limresp[0] == "0"):
				limpre = limresp[1]
				limpos = limresp[2]
			elif (limresp[0] == "1"):
				pass
			print("recv limit:", limdata, limresp)
			x += 1
	except:
		pass
	return limresp

'''{
"db":{"host":"127.0.0.1", "user":"whois", "pass":"Siohw1!", "name":"dbname", "table":"whois_cache", "col":"text"},
"limiter":{"host":"127.0.0.1", "port":3434, "pass":"Limiter1!", "conn":1, "time":10},
"whois":{"port":4343},
}'''
fobj = open(sys.argv[1], "r")
conf = ast.literal_eval(fobj.read().strip())
fobj.close()

servport = conf["whois"]["port"]
childlist = []

limhost = conf["limiter"]["host"]
limport = conf["limiter"]["port"]
limpass = conf["limiter"]["pass"]
limpre = ""; limpos = ""

sqlshost = conf["db"]["host"]
sqlsuser = conf["db"]["user"]
sqlspass = conf["db"]["pass"]
sqlsdb = conf["db"]["name"]
sqlstbl = conf["db"]["table"]
sqlscol = conf["db"]["col"]

sockobjc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockobjc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sockobjc.bind(("", servport))
sockobjc.listen(1)

while (1):
	for x in range(0, len(childlist)):
		try:
			os.waitpid(childlist[x], os.WNOHANG)
		except:
			childlist[x] = -1
	while (-1 in childlist):
		childlist.remove(-1)
	lims("127.0.0.1")
	(sockconn, sockaddr) = sockobjc.accept()
	print("conn open:", sockaddr)
	try:
		childpid = os.fork()
	except:
		childpid = -1
	if (childpid == 0):
		resplim = lims(sockaddr[0])
		try:
			sqldom = sockconn.recv(1024).strip()
			sqldom = sstr(sqldom.upper())
			connresp = ""
			print("conn data:", sockaddr, sqldom)
			if ((resplim[0] == "2") and (resplim[1] == "ok")):
				try:
					conn = psycopg2.connect("host='%s' dbname='%s' user='%s' password='%s'" % (sqlshost, sqlsdb, sqlsuser, sqlspass))
					curs = conn.cursor()
					curs.execute("SELECT %s FROM %s WHERE name = '%s';" % (sqlscol, sqlstbl, sqldom))
					rows = curs.fetchall()
					conn.close()
					leng = 0
					for rowo in rows:
						connresp = ("%s\n\n\n" % (rowo[0].strip()))
						leng += 1
						break
					if (leng < 1):
						connresp = ("NOT FOUND\n")
				except:
					connresp = ("SERVER ERROR (1)\n")
			else:
				connresp = ("LIMIT REACHED\n")
			sockconn.send(connresp)
		except:
			pass
		try:
			print("conn closed:", sockaddr)
			sockconn.shutdown(socket.SHUT_RDWR)
			sockconn.close()
		except:
			pass
		sys.exit(0)
	else:
		try:
			sockconn.close()
		except:
			pass
		childlist.append(childpid)

Limiter Notes:

  • The shared secret is padded with 2 random strings:
    • shared-secret = (pre-pad + common-password + post-pad)
    • These 2 strings are re-generated after 2^16 unique nonces are used
  • The message data is prefixed with a unique nonce string:
    • message-data = (uniq-nonce + message-body)
  • The limiter server uses sha256 hashing to authenticate data:
    • auth-message = sha256(shared-secret + message-data + shared-secret)
    • Can switch to a basic HMAC call if greater security is needed
  • The limiter server is a single process which communicates with any connecting clients
    • Can switch to a forked-child setup if faster connections are needed

Whois Notes:

  • The whois server uses forked-child processes to communicate with multiple clients at the same time
    • Each client process will make its own connection to the database server

15 thoughts on “A small whois server and a nice holiday video

  1. I have been browsing online more than 4 hours today, yet I never
    found any interesting article like yours. It’s pretty worth enough for me.
    In my opinion, if all web owners and bloggers made good content as you did, the web
    will be a lot more useful than ever before.

  2. Appreciating the dedication you put into your website and in depth information you offer.
    It’s nice to come across a blog every once in a while that
    isn’t the same outdated rehashed material. Great read!
    I’ve bookmarked your site and I’m adding your RSS feeds to my Google account.

  3. I’m impressed, I must say. Seldom do I encounter a blog that’s both educative and entertaining, and without a doubt, you
    have hit the nail on the head. The problem is an issue that not enough men and women are speaking intelligently about.
    Now i’m very happy I came across this during my hunt for something regarding this.

  4. Oh my goodness! Impressive article dude! Many thanks, However
    I am encountering difficulties with your RSS. I don’t
    understand why I cannot join it. Is there anybody else having identical RSS problems?

    Anyone that knows the solution will you kindly respond?
    Thanks!!

  5. Greetings from Carolina! I’m bored to tears at work so I decided to check out your site on my iphone
    during lunch break. I enjoy the knowledge you provide here and can’t wait to take a
    look when I get home. I’m surprised at how fast your blog filled on my cell phone ..
    I’m not even using WIFI, just 3G .. Anyways, amazing site!

  6. It is really a great and useful piece of information.
    I am happy that you simply shared this useful
    information with us. Please stay us informed like this.
    Thanks for sharing.

Leave a comment