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!
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 + "[email protected]#$%^&*-_=+?") 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