Quick Blog Links

Coding:
  HTML Char Escape   [Python]
  RADIUS/SSH OTP   [Python / C]
  SSH-Pass Automation   [Python]
  VPN-Tunneled Traffic   [Python]
  DHCP/ARP Client-AP WiFi-Management   [C / Python]
Configs:
  OpenSSH Setup   SSH VPN Tunnel
  FreeRADIUS Setup   Radius CA Capture
  Cisco/OpenWRT Setup   ebtables/iptables Firewall
Written:
  Seneca College – Secure LAN Communication   [Thesis]
  Seneca College – Teaching Hacking   [Paper]
  ARM Assembly – Basic Introduction   [Post]
Random:
  What Motivates Us   /   Career ~ Resume   /   Seneca Project Links   /   Office Space
Quick Blog Links

I think the final version of relayd functionality written in C (arp & dhcp)

So it took me a while to update & re-write the dhcp relayd functionality that I made in python previously. This new C file can relay & rebroadcast both arp and dhcp packets via raw sockets. For DHCP relaying, it has to be able to insert the bridges IP address into the request so that the server replies back to us and then we can forward it on (so we can run only 1 dhcp server total on the network). The last interface specified in the list is designated as the dhcp server interface to send the requests coming in.

Also tried to reduce the number of system calls made by reading both the arp table proc file and routing table file instead (only 1 sys call to replace host route entries on the bridge router).

It’s been a while since I used select with multiple sockets but this compile uses less memory while running versus the python version, although the py version is easier to read and maintain (depends on your needs).

https://github.com/stoops/arprb/blob/master/dhcprb.c?ts=4

I’ll run this for a while and see how it performs!

Jon C

I think the final version of relayd functionality written in C (arp & dhcp)

Last Part Of The WiFi-AP Bridge Setup (DHCP Relay/Forwarder) in Py

I was having troubles getting dnsmasq to be a simple DHCP relay/forwarder/proxy and I didn’t want to add this into the ARP relay C code to keep that as simple as possible so I wrote this little Python script that will basically bridge 2 interfaces together (one that has DHCP clients on it and one that is connected to the DHCP server [1 main server running for the whole network]).

I stored this in the ARP-relay GIT repo I created prior since it is related to the same bridged setup:
https://github.com/stoops/arprb/blob/master/dhcprb.py?ts=4

An example usage & output from running it so far:

root@OpenWRT:~# python dhcprb.py br-wan wlan0
('00:be:ee:ca:fe:00', '192.168.17.51', '<-->', '00:be:ee:ca:fe:ff', '192.168.16.51')
...
request-> ('0.0.0.0', 68) [304] {wlan0}
<-reply ('192.168.16.1', 67) [300] {br-wan:192.168.16.131}
...
request-> ('0.0.0.0', 68) [300] {wlan0}
<-reply ('192.168.16.1', 67) [300] {br-wan:192.168.16.153}
...

Last Part Of The WiFi-AP Bridge Setup (DHCP Relay/Forwarder) in Py

802.11n -> 802.11ac AP-Client Kick-Off Script (Py)

So I have been running multiple APs with the same SSID on separate channels and frequencies and noticed that the clients are really good at switching from a weak 802.11ac signal strength to the stronger but lower speed 802.11n AP station. This is good, however, they don’t seem to be as aggressive in switching back to 802.11ac once they get closer again (unless they turn off or shutdown or restart their network stack since the 802.11n just gets stronger the closer you get). I found an OpenWRT compatible shell script which kicks clients off a given radio depending on their signal strength to the router. I adjusted it to disconnect a client if they start to get too close to the N router as they are likely going to get a good signal strength from the AC AP instead. You can set the AP deauth time (ex 19 seconds), the time between kicking the same client on/off again (ex 31 mins), and it checks for the signal-to-noise ratio to get above a certain amount (ex 45 SNR) before activating on a client!

python apc.py n 20 40
import os,sys,time

radios = []
mode = sys.argv[1]
secs = (int(sys.argv[2]) * 1000)
macs = {}
band = (int(sys.argv[3]) * 60)

if (mode == "ac"):
	radios = ["wlan0"]

if (mode == "n"):
	radios = ["wlan1"]

while True:
	sec = int(time.time())

	for intf in radios:
		os.system("iwinfo '%s' assoclist | grep 'SNR' | tr '(/)' ' ' | tr -s ' ' > /tmp/apc.log" % (intf))
		f = open("/tmp/apc.log", "r")
		lines = f.readlines()
		f.close()

		for line in lines:
			info = line.strip().split(" ")
			(mac, sig, snr) = (info[0], int(info[1]), abs(int(info[6])))
			print("> %d %d [%s][%s]" % (snr, sig, intf, mac))

			if (secs >= 1000):
				delc = 0 ; kick = "true"
				opts = ("'addr':'%s', 'reason':5, 'deauth':%s, 'ban_time':%s" % (mac, kick, secs))

				if ((mode == "ac") and (sig <= -83)):
					delc = 1

				#if ((mode == "n") and (sig >= -51)):
				#	delc = 1

				if ((mode == "n") and (snr >= 45)):
					delc = 1

				if (delc == 1):
					if (not mac in macs.keys()):
						macs[mac] = 0
					if (sec >= (macs[mac] + band)):
						macs[mac] = sec
						print("* %d %d [%s][%s][%d][%d]" % (macs[mac], sec, mac, intf, sig, snr))
						os.system('ubus call "hostapd.%s" del_client "{%s}"' % (intf, opts))

	for mac in macs.keys():
		if (sec >= (macs[mac] + band)):
			print("x", mac, macs[mac])
			del macs[mac]

	print("")
	time.sleep(9)

802.11n -> 802.11ac AP-Client Kick-Off Script (Py)

Creating a simple relayd replacement in C (compiled for an ARMv7 WRT router)

I’ve been running into different issues with relayd that I tried to manually fix in the framework, for example: not locking host route entries onto a specific interface, better detection for when a host changes interfaces, better arp table & ip routing management, etc.. However, I’ve still been experiencing some flakiness in it’s operation so I made a simple alternative that I can run which duplicates its most essential features needed for layer 2 bridging:

  • Layer 2 server socket to listen for ARP request packets
  • Layer 2 client sockets to send ARP packets with the routers MAC address inserted into the ARP replies for each host and for each bridged interface
  • Layer 3 UDP socket-based pinging for unknown host detection & discovery (auto IP-to-MAC ARP table entry resolution for each bridged interface)
  • Layer 3 IP routing-table host-entry updates related to which bridged interface the client is currently on

The code is just under 300 lines but it seems to be working so far and will keep testing it out to see if it works better than what I was experiencing with relayd!

https://github.com/stoops/arprb/blob/master/arprb.c?ts=4

--
arp req [42] [1544][256] [192.168.17.175][192.168.16.1]
arp reply [wlan0] [00:be:ee:ca:fe:ff][192.168.17.175] <-> [192.168.16.1]
--
arp req [42] [1544][256] [192.168.16.1][192.168.17.175]
arp reply [br-wan] [00:be:ee:ca:fe:00][192.168.16.1] <-> [192.168.17.175]
--

... snippet ...

void send_arp(struct intf relay, char *who_adr, uchar *dst_mac, char *dst_adr) {
	printf("arp reply [%s] [%s][%s] <-> [%s]\n", relay.ifn, relay.smac, who_adr, dst_adr);

	int sock = relay.sock;
	unsigned char *src_mac = relay.mac;
	struct sockaddr ssa = relay.ssa;

	struct in_addr src_adr;
	struct arp_packet pkt;

	pkt.frame_type = htons(FRAME_HW_TYPE);
	pkt.hw_type    = htons(ETHER_HW_TYPE);
	pkt.proto_type = htons(IP_PROTO_TYPE);
	pkt.hw_size    = HW_ADDR_LEN;
	pkt.proto_size = IP_ADDR_LEN;
	pkt.op         = htons(OP_ARP_REPLY);

	bcopy(src_mac, pkt.sorc_hw_addr, HW_ADDR_LEN);
	bcopy(dst_mac, pkt.targ_hw_addr, HW_ADDR_LEN);

	bcopy(src_mac, pkt.sndr_hw_addr, HW_ADDR_LEN); /* target mac */
	src_adr.s_addr = inet_addr(who_adr);
	bcopy(&src_adr, pkt.sndr_ip_addr, IP_ADDR_LEN); /* target ip */

	bcopy(dst_mac, pkt.rcpt_hw_addr, HW_ADDR_LEN); /* requester mac */
	src_adr.s_addr = inet_addr(dst_adr);
	bcopy(&src_adr, pkt.rcpt_ip_addr, IP_ADDR_LEN); /* requester ip */

	sendto(sock, &pkt, sizeof(pkt), 0, &ssa, sizeof(ssa));
}

Creating a simple relayd replacement in C (compiled for an ARMv7 WRT router)

Following along with the OpenWRT subreddit users

There was a post on a subreddit that I follow (OpenWRT) that was asking for peoples router/network setups and how they are using OpenWRT to accomplish that so I decided to try and make a network diagram of the setup I worked on here at my parents home while I’ve been working remotely (needing stable Internet/WiFi!). There are 2 levels that are covered and the Internet comes into the lower level which then gets sent up via a dedicated 802.11ac radio (separate backchannel). That is then rebroadcasted to the rest of the home with another dedicated, separate 802.11ac AP via the middle relay bridge in between. The relay bridge runs the customized app below that I compiled and it all shares one flat network throughout the home (one /20)! There is also 802.11n sent out for better reach and distance just in case as well 🙂

Edit: You can also add guest networks on each of the radios but with different SSIDs and wlan interfaces which will let them act like wifi-vlans that you can section off with dnsmasq & iptables…

Edit-edit (Updated diagram/layout):

I made this network graph with this site: https://www.lucidchart.com/documents#/dashboard

Historical documentation purposes,
Previous setup with semi-ok results:

Following along with the OpenWRT subreddit users

Turning a small stream cipher (ARC4) into a hash function (ARCH)!

$ python hash.py b b
('fd5f1b4640e0f5cdda0368b67601b9077cbe4a4931a94b52fdf46bf1be0c2bf5', '3a4627878dca5c4daeb243a661b56ebbaa03c268f659b6001cd84911051c103b', 'bb', 2)

$ python hash.py b c
('1181834e637bf3519010de1902889807341cd57a1d2e68a281524d91f2f86cf9', '7deadb485c3d329026d04cefadb4b43a9af19536aaf450052a2edce404f6065f', 'bc', 2)

$ python hash.py c b
('2f4802585ed79a61d25c92a3d7af8fa2a16e2e269e9c6fbd21891e6a7bae15ec', '6a25a3b18a93d966d7c878f9263f053d653618782a9f7918db05b06e0fa27787', 'cb', 2)

$ python hash.py c c
('6683682f501fdaa1c4a95e1ec6b7fb5ff49206af34e1729a17ded27fda355ea4', '7f12dc4dd32e3c9b31d03382b9c9af1efc0ea3ba620ba8e27fa9b9d8ebc1a88b', 'cc', 2)
import os,sys

def swap(s, a, b):
	t = s[a] ; s[a] = s[b] ; s[b] = t
	return s

def init(mesg, leng):
	j = 0 ; skey = []
	for i in range(0, 256):
		skey.append(i)
	for i in range(0, 256+leng):
		k = (i % 256) ; m = (i % leng)
		l = (((i + 1 + ord(mesg[m])) * leng) % 256)
		j = ((j + skey[k] + skey[l]) % 256)
		swap(skey, k, j)
	return skey

def arch(skey):
	i = 0 ; j = 0
	l = 0 ; rnds = 4 ; outp = ""
	for z in range(0, 256*rnds):
		i = ((i + 1) % 256)
		j = ((j + skey[i]) % 256)
		swap(skey, i, j)
	for z in range(0, 32):
		i = ((i + 1) % 256)
		j = ((j + skey[i]) % 256)
		swap(skey, i, j)
		k = ((skey[i] + skey[j] + skey[l]) % 256)
		outp += chr(skey[k])
		l = (l ^ ord(outp[z]))
	return outp

def hmac(mesg, mlen, skey, klen):
	inner_pad = 0x36 ; outer_pad = 0x5C
	block_size = 64 ; ikey = "" ; okey = ""
	tkey = skey ; tlen = klen
	if (klen > block_size):
		zkey = init(skey, klen)
		tkey = arch(zkey)
		tlen = len(tkey)
	for x in range(0, block_size):
		c = 0
		if (x < tlen):
			c = ord(tkey[x])
		ikey += chr(inner_pad ^ c)
		okey += chr(outer_pad ^ c)
	zkey = init(ikey+mesg, block_size+mlen)
	ihsh = arch(zkey)
	ilen = len(ihsh)
	zkey = init(okey+ihsh, block_size+ilen)
	ohsh = arch(zkey)
	return ohsh

def stoh(inpt):
	o = ""
	for c in inpt:
		s = hex(ord(c))[2:]
		if len(s) != 2:
			s = "0" + s
		o += s
	return o

m = sys.argv[1] ; l = len(m)
k = sys.argv[2] ; n = len(k)
z = init(m+k, l+n)
print(stoh(hmac(m, l, k, n)), stoh(arch(z)), m+k, l+n)

Turning a small stream cipher (ARC4) into a hash function (ARCH)!

A simpler improvement to relayd (checking the arp table before expiring)

So instead of running a process heavy static arp broadcaster client/server service, I realized that I could just make relayd a bit smarter in order to properly detect disconnected hosts faster (simply by double checking the arp table). I was also able to compile this software in an Ubuntu VM running in a VirtualBox on OSX for mips & armv7 (archer c7 tplink & wrt1900acs wifi extended setup):

https://github.com/stoops/relayd/compare/master…stoops:moddel

./relayd -I wlan0 -I wlan1 -t 2 -p -1 -B -D -P

With a couple of arp table clean up helper scripts the setup is much simpler with this and less involved in managing the arp table entries!

mode:  ap  |  ssid:  Peppi Place      bmac: 00:be:ee:ca:fe:01  key: psk2+ccmp  |  freq: 2.4GHz  chan:   1  mode:  HT40  |  timeouts: 300 300
mode:  ap  |  ssid:  Peppi Place      bmac: 00:be:ee:ca:fe:11  key: psk2+ccmp  |  freq: 2.4GHz  chan:  11  mode:  HT40  |  timeouts: 300 300
mode:  ap  |  ssid:  Peppi Place      bmac: 00:be:ee:ca:fe:61  key: psk2+ccmp  |  freq: 5.0GHz  chan: 161  mode: VHT80  |  timeouts: 300 300
mode:  ap  |  ssid:  Peppi Place AC   bmac: 00:be:ee:ca:fe:63  key: psk2+ccmp  |  freq: 5.0GHz  chan: 161  mode: VHT80  |  timeouts: 300 300

A simpler improvement to relayd (checking the arp table before expiring)

GIT Rebase with HEAD Conflicts (Auto Accept & Merge the Changes)

I have a bad habit of using git amends (vs commit & squash)
git commit -a --amend --no-edit
but this can cause some future issues when force pulling in the new changes (for example to another branch possibly). I learnt a new way when rebasing to auto accept all of the head changes listed as a conflict:

git checkout --conflict=merge . ; git checkout --ours . ; git add .

GIT Rebase with HEAD Conflicts (Auto Accept & Merge the Changes)

Turning the sdbm hash method into an hmac version

Some small hash loop tweaks and I increased the number of hash rounds from 3 to 4 also:

import os,sys

def tt(ll):
	return (ll & 0xffffffff)

def sdbm(inpt, leng):
	hshs = 0
	for x in range(0, leng):
		hshs = tt(ord(inpt[x]) + tt(hshs << 6) + tt(hshs << 16) - hshs)
	return hshs

def sdbm_hash(inpt, leng):
	mixs = [1, 6, 16, 13, 33, 27, 67, 55, 123]
	hshs = [0, 0, 0, 0, 0, 0, 0, 0, 0]
	more = 0
	mlen = len(hshs) ; rnds = (4 * mlen)
	for x in range(0, mlen+leng):
		hshs[0] = tt(mixs[x%mlen] + ord(inpt[x%leng]) + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0])
		more = (more ^ (hshs[0] >> 16))
		i = ((x % (mlen - 1)) + 1)
		less = ((hshs[i] & 0xffff0000) ^ ((hshs[i] & 0xffff) << 16))
		hshs[i] = (less ^ more)
	for z in range(0, rnds):
		hshs[0] = tt(z + more + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0])
		more = (more ^ (hshs[mlen-1] >> 16))
		for y in range(mlen-1, 0, -1):
			hshs[y] = tt((hshs[y] << 16) | (hshs[y-1] >> 16))
			hshs[y-1] = (hshs[y-1] & 0xffff)
	o = ""
	for h in hshs[1:]:
		for x in range(3, -1, -1):
			o += chr((h >> (x * 8)) & 0xff)
	return o

def sdbm_hmac(mesg, mlen, skey, klen):
	inner_pad = 0x36 ; outer_pad = 0x5C
	block_size = 64 ; ikey = "" ; okey = ""
	tkey = skey ; tlen = klen
	if (klen > block_size):
		tkey = sdbm_hash(skey, klen)
		tlen = len(tkey)
	for x in range(0, block_size):
		c = 0
		if (x < tlen):
			c = ord(tkey[x])
		ikey += chr(inner_pad ^ c)
		okey += chr(outer_pad ^ c)
	ihsh = sdbm_hash(ikey+mesg, block_size+mlen)
	ilen = len(ihsh)
	ohsh = sdbm_hash(okey+ihsh, block_size+ilen)
	return ohsh

def stoh(s):
	return "".join([hex(ord(c))[2:].rjust(2, '0') for c in s])

m = sys.argv[1] ; l = len(m)
k = sys.argv[2] ; n = len(k)
#print(stoh(sdbm_hash(m, l)), sdbm(m, l), m, l)
print(stoh(sdbm_hmac(m, l, k, n)), sdbm(m, l), m, l, sdbm(k, n), k, n)

$ for x in b c ; do for y in b c ; do python hash.py "$x" "$y" ; echo ; done ; done
('d805c892902cf6ea0c98d1946110a61334a9a1158b430fb7d1c5ba7afb29147d', 98, 'b', 1, 98, 'b', 1)

('6eb38871cc3679cb533f1daeee4ed87ab4f154f953e5ea96d508bda6bb054abd', 98, 'b', 1, 99, 'c', 1)

('0d034f1f7627492fd57bd92a63a8a4f2d977f02c2fdf29b4e49f4b3b21a9e738', 99, 'c', 1, 98, 'b', 1)

('09b2678c7ce46cd7cd112b4a2b6fd05d50c858fe0460d29264c4ea01595968db', 99, 'c', 1, 99, 'c', 1)


$ ./hash "this is a test" "b"
[6b18600ee98449472dd377755dd67cd436c9c143cfc893c9a2181e0567d16cf1] [1655286693] [this is a test] [14] [98] [b] [1]

$ ./hash "this is a test" "c"
[bd3bc1a983e35311e5ea60db5ae8cd28a821386937dfd480eccff95fab8356b9] [1655286693] [this is a test] [14] [99] [c] [1]

$ ./hash "this is a tesu" "b"
[8cddc8b9f5a1a1adf41c78b035bf59b444431e772b9c39824e2cf67e2837d9b0] [1655286694] [this is a tesu] [14] [98] [b] [1]

$ ./hash "this is a tesu" "c"
[2554d7d9ecb47d36b7f62d5e4432a7983490aca1229a91331c16b81567c4edff] [1655286694] [this is a tesu] [14] [99] [c] [1]
#include <stdio.h>
#include <string.h>

unsigned int sdbm(char *inpt, int leng) {
	unsigned int hshs = 0;
	for (int x = 0; x < leng; ++x) {
		hshs = (inpt[x] + (hshs << 6) + (hshs << 16) - hshs);
	}
	return hshs;
}

void sdbm_hash(unsigned char *outp, unsigned char *inpt, int leng) {
	unsigned int mixs[] = {1, 6, 16, 13, 33, 27, 67, 55, 123};
	unsigned int hshs[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	unsigned int less, more = 0;
	int i, mlen = 9, rnds = (4 * mlen);
	for (int x = 0; x < (mlen+leng); ++x) {
		hshs[0] = (mixs[x%mlen] + inpt[x%leng] + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0]);
		more = (more ^ (hshs[0] >> 16));
		i = ((x % (mlen - 1)) + 1);
		less = ((hshs[i] & 0xffff0000) ^ ((hshs[i] & 0xffff) << 16));
		hshs[i] = (less ^ more);
	}
	for (int z = 0; z < rnds; ++z) {
		hshs[0] = (z + more + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0]);
		more = (more ^ (hshs[mlen-1] >> 16));
		for (int y = mlen-1; y > 0; --y) {
			hshs[y] = ((hshs[y] << 16) | (hshs[y-1] >> 16));
			hshs[y-1] = (hshs[y-1] & 0xffff);
		}
	}
	for (int x = 1, y = 0; x < mlen; ++x) {
		for (int z = 3; z > -1; --z, ++y) { 
			outp[y] = ((hshs[x] >> (z * 8)) & 0xff);
		}
	}
}

void sdbm_hmac(unsigned char *outp, unsigned char *mesg, int mlen, unsigned char *skey, int klen) {
	int block_size = 64, hash_size = 32;
	unsigned char inner_pad = 0x36, outer_pad = 0x5C;
	unsigned char ikey[block_size], okey[block_size], ihsh[hash_size], thsh[hash_size];
	unsigned char buff[block_size+mlen+hash_size];
	unsigned char *tkey = skey; int tlen = klen;
	if (klen > block_size) {
		sdbm_hash(thsh, skey, klen);
		tkey = thsh; tlen = hash_size;
	}
	for (int x = 0; x < block_size; ++x) {
		unsigned char padc = 0;
		if (x < tlen) { padc = tkey[x]; }
		ikey[x] = (inner_pad ^ padc);
		okey[x] = (outer_pad ^ padc);
	}
	bcopy(ikey, buff, block_size);
	bcopy(mesg, buff+block_size, mlen);
	sdbm_hash(ihsh, buff, block_size+mlen);
	bcopy(okey, buff, block_size);
	bcopy(ihsh, buff+block_size, hash_size);
	sdbm_hash(outp, buff, block_size+hash_size);
}

void stoh(char *outp, unsigned char *inpt) {
	char *hexs = "0123456789abcdef";
	for (int x = 0, y = 0; x < 32; ++x) {
		outp[y] = hexs[(inpt[x] >> 4) & 0xf]; ++y;
		outp[y] = hexs[inpt[x] & 0xf]; ++y;
	}
}

int main(int argc, char *argv[]) {
	char *m = argv[1]; int l = strlen(m);
	char *k = argv[2]; int n = strlen(k);
	unsigned char h[32]; char o[65]; bzero(o, 65);
	sdbm_hmac(h, (unsigned char *)m, l, (unsigned char *)k, n); stoh(o, h);
	printf("[%s] [%u] [%s] [%d] [%u] [%s] [%d]\n", o, sdbm(m, l), m, l, sdbm(k, n), k, n);
	return 0;
}

Turning the sdbm hash method into an hmac version

Experiment – Turning the SDBM mixing algorithm into a hash function

From this page: http://www.cse.yorku.ca/~oz/hash.html

import os,sys

def tt(ll):
	return (ll & 0xffffffff)

def sdbm(inpt, leng):
	hshs = 0
	for x in range(0, leng):
		hshs = tt(ord(inpt[x]) + tt(hshs << 6) + tt(hshs << 16) - hshs)
	return hshs

def sdbm_hash(inpt, leng):
	mixs = [1, 6, 16, 13, 33, 27, 67, 55, 123]
	hshs = [0, 0, 0, 0, 0, 0, 0, 0, 0]
	more = 0
	rnds = len(hshs)
	for z in range(0, rnds*3):
		hshs[0] = tt((hshs[0] + mixs[z%rnds]) * mixs[z%rnds])
		for x in range(0, leng):
			hshs[0] = (hshs[0] & 0xffff)
			hshs[0] = tt(ord(inpt[x]) + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0])
			more = (more ^ (hshs[rnds-1] >> 16))
			for y in range(rnds-1, 0, -1):
				hshs[y] = tt((hshs[y] << 16) | (hshs[y-1] >> 16))
				hshs[y-1] = (hshs[y-1] & 0xffff)
			hshs[0] = (hshs[0] ^ more)
	o = ""
	for h in hshs[1:]:
		for x in range(3, -1, -1):
			o += chr((h>>(x*8))&0xff)
	return o

def stoh(s):
	return "".join([hex(ord(c))[2:].rjust(2, '0') for c in s])

m = sys.argv[1] ; l = len(m)
print(stoh(sdbm_hash(m, l)), sdbm(m, l), m, l)

$ python hash.py "b" 
('197f907989061db579beba252025bfbf7f4848a47da0e5a9bc8eb73c6fdb8904', 98, 'b', 1)

$ python hash.py "c" 
('f39b31f66506067376f8f88f620e731cd4292a6aeb71da43d297fffc3f5f92c4', 99, 'c', 1)

$ python hash.py "bb" 
('d427d6ba1edaabab2ba3c6ddd70ccf3d0ceff23fcd6de3d2c0af7d0ac95dd62d', 6428800, 'bb', 2)

$ python hash.py "bc" 
('4bfdd409d3de82913ba7405894f444d8858a539abe98e312d64474abf6b68e3a', 6428801, 'bc', 2)

$ ./hash "this is a test"
[dc053c4426ce396ace93a2817b388c465bb5188336d5f8816384a1cef0a2039d] [1655286693] [this is a test] [14]

$ ./hash "this is a tesu"
[cfbb3301ae39658cf1a1874dd48d8dbf9da4aee0831f781e30066be5f67eb819] [1655286694] [this is a tesu] [14]
#include <stdio.h>
#include <string.h>

unsigned int sdbm(char *inpt, int leng) {
	unsigned int hshs = 0;
	for (int x = 0; x < leng; ++x) {
		hshs = (inpt[x] + (hshs << 6) + (hshs << 16) - hshs);
	}
	return hshs;
}

void sdbm_hash(unsigned char *outp, unsigned char *inpt, int leng) {
	unsigned int mixs[] = {1, 6, 16, 13, 33, 27, 67, 55, 123};
	unsigned int hshs[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	unsigned int more = 0;
	int rnds = 9;
	for (int z = 0; z < rnds*3; ++z) {
		hshs[0] = ((hshs[0] + mixs[z%rnds]) * mixs[z%rnds]);
		for (int x = 0; x < leng; ++x) {
			hshs[0] = (hshs[0] & 0xffff);
			hshs[0] = (inpt[x] + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0]);
			more = (more ^ (hshs[rnds-1] >> 16));
			for (int y = rnds-1; y > 0; --y) {
				hshs[y] = ((hshs[y] << 16) | (hshs[y-1] >> 16));
				hshs[y-1] = (hshs[y-1] & 0xffff);
			}
			hshs[0] = (hshs[0] ^ more);
		}
	}
	for (int x = 1, y = 0; x < rnds; ++x) {
		for (int z = 3; z > -1; --z, ++y) { 
			outp[y] = ((hshs[x] >> (z * 8)) & 0xff);
		}
	}
}

void stoh(char *outp, unsigned char *inpt) {
	char *hexs = "0123456789abcdef";
	for (int x = 0, y = 0; x < 32; ++x) {
		outp[y] = hexs[(inpt[x] >> 4) & 0xf]; ++y;
		outp[y] = hexs[inpt[x] & 0xf]; ++y;
	}
}

int main(int argc, char *argv[]) {
	char *m = argv[1]; int l = strlen(m);
	unsigned char h[32]; char o[65]; bzero(o, 65);
	sdbm_hash(h, (unsigned char *)m, l); stoh(o, h);
	printf("[%s] [%u] [%s] [%d]\n", o, sdbm(m, l), m, l);
	return 0;
}

Experiment – Turning the SDBM mixing algorithm into a hash function