⚠️ This post is archived from my phlog in Gopherspace. Please read my post on the Gopher Protocol to get started!
I host different Gopher Protocol services which use different ports on one box. So I kind of need a reverse proxy of sorts. I wanted to make sure I could access all of them using port 70.
The idea:
- Don’t touch my existing Gopher Protocol service
- Have a new Gopher service running on port 70
-
On the new Gopher service, associate certain internal ports with certain selectors
-
for example if a selector beginning with
/phorum
is requested on port 70, I want to strip the/phorum
bit, then request localhost 7070 and serve that - For all internal links, prepend the selector and change the port to 70
In other words, I have various Gopher Protocol services running on different ports, but with xinetd I serve them all on port 70, just under different selectors. This is basically a Gopher Protocol routing/reverse proxy!
It’s running live!
I use this configuration on this server.
gopher://gopher.someodd.zip:70/1/someodd/
: basically puts my localhost 7071
gopher service to be served on port 70, under the /someodd
selector.
gopher://gopher.someodd.zip:70/1/phorum/
: serves phorum (running on localhost
7070) on port 70, under the /someodd
selector.
This configuration also offers a service index at gopher://gopher.someodd.zip/
.
Configure the server
Install xnetd
:
sudo apt-get install -y xinetd
Add this file /etc/xinetd.d/gopher
:
service gopher
{
type = UNLISTED
port = 70
socket_type = stream
wait = no
user = root
server = /usr/local/bin/gopher_router.sh
log_on_success += USERID
log_on_failure += USERID
log_type = FILE /var/log/xinetd_gopher.log
disable = no
}
Create this script /usr/local/bin/gopher_router.sh
:
#!/bin/bash
# Read the selector from stdin
read selector
# Function to process and replace paths and ports in the response
process_response() {
local prefix="$1"
local port="$2"
local target_host="gopher.someodd.zip"
local target_port="70"
# Strip the prefix from the selector and ensure the leading slash is retained
local stripped_selector="${selector#$prefix}"
if [[ "$stripped_selector" != /* ]]; then
stripped_selector="/$stripped_selector"
fi
# Send the selector to the appropriate service and process the response
echo "$stripped_selector" | nc localhost $port | \
sed -e "/\t${target_host}\t${port}/s|\t/|\t${prefix}/|g" \
-e "s|\(${target_host}\)\t${port}|\1\t${target_port}|g"
}
# Handle the selector
case "$selector" in
"/phorum"*)
process_response "/phorum" 7070
;;
*)
process_response "/" 7071
;;
esac
Restart:
sudo systemctl restart xinetd
For testing:
echo "/parent2/little-notes" | nc localhost 70
Also ufw
(firewall entry):
sudo ufw allow 70/tcp comment 'gopher (router)'
Bonus
Serve default menu
You could even serve a default menu:
# Handle the selector
case "$selector" in
"/phorum"*)
process_response "/phorum" 7070
;;
"/someodd"*)
process_response "/someodd" 7071
;;
*)
# Return a Gopher menu with links to /phorum and /someodd
echo "1Phorum link /phorum gopher.someodd.zip 70"
echo "1someodd link /someodd gopher.someodd.zip 70"
echo ""
;;
esac
Handle rewriting for Tor
in /etc/xinetd.d/gopher
:
service gopher
{
type = UNLISTED
port = 70
socket_type = stream
wait = no
user = root
server = /bin/bash
server_args = /usr/local/bin/gopher_router.sh
log_on_success += USERID
log_on_failure += USERID
log_type = FILE /var/log/xinetd_gopher.log
disable = no
}
now i updated my routing script:
#!/bin/bash
# Read the selector from stdin
read selector
# Function to check if the connection is coming from Tor
is_tor_connection() {
# Check if the remote IP address matches known Tor patterns
if [[ "$REMOTE_HOST" == "::ffff:127.0.0.1" ]]; then
# Assuming Tor traffic comes from localhost for hidden services
return 0
fi
return 1
}
# Function to process and replace paths and ports in the response
process_response() {
local prefix="$1"
local port="$2"
local target_host="gopher.someodd.zip"
local target_port="70"
# Check if Tor was used and adjust the target host
if is_tor_connection; then
target_host="xj2o2wylbqkprajldswuyxm6dffca4eepegelblgvux3uuqmtb2l56id.onion"
fi
# Strip the prefix from the selector and ensure the leading slash is retained
local stripped_selector="${selector#$prefix}"
if [[ "$stripped_selector" != /* ]]; then
stripped_selector="/$stripped_selector"
fi
# Send the selector to the appropriate service and process the response
echo "$stripped_selector" | nc localhost $port | \
sed -e "s|\tgopher.someodd.zip\t${port}|\t${target_host}\t${target_port}|g" \
-e "s|\tgopher.someodd.zip/|\t${target_host}/|g"
}
# Handle the selector
case "$selector" in
"/phorum"*)
process_response "/phorum" 7070
;;
*)
process_response "/" 7071
;;
esac
yet another update
sorry i need to clean up this article, but i realized for phorum until i release a prefix-adding feature for slectors in phorum i need to rewrite the links in phorum.
#!/bin/bash
# Read the selector from stdin
read selector
# Function to check if the connection is coming from Tor
is_tor_connection() {
# Check if the remote IP address matches known Tor patterns
if [[ "$REMOTE_HOST" == "::ffff:127.0.0.1" ]]; then
# Assuming Tor traffic comes from localhost for hidden services
return 0
fi
return 1
}
# Function to process and replace paths and ports in the response
process_response() {
local prefix="$1"
local port="$2"
local rewrite_selectors_to_use_prefix="$3"
local target_host="gopher.someodd.zip"
local target_port="70"
# Check if Tor was used and adjust the target host
if is_tor_connection; then
target_host="xj2o2wylbqkprajldswuyxm6dffca4eepegelblgvux3uuqmtb2l56id.onion"
fi
# Strip the prefix from the selector and ensure the leading slash is retained
local stripped_selector="${selector#$prefix}"
if [[ "$stripped_selector" != /* ]]; then
stripped_selector="/$stripped_selector"
fi
# Send the selector to the appropriate service and process the response
if [[ "$rewrite_selectors_to_use_prefix" == "true" ]]; then
# When rewriting is enabled, add the prefix to all selectors in the response
echo "$stripped_selector" | nc localhost $port | \
sed -e "s|\tgopher.someodd.zip\t${port}|\t${target_host}\t${target_port}|g" \
-e "s|\tgopher.someodd.zip/|\t${target_host}/|g" \
-e "s|\t/|\t${prefix}/|g"
else
# Original behavior without selector rewriting
echo "$stripped_selector" | nc localhost $port | \
sed -e "s|\tgopher.someodd.zip\t${port}|\t${target_host}\t${target_port}|g" \
-e "s|\tgopher.someodd.zip/|\t${target_host}/|g"
fi
}
# Handle the selector
case "$selector" in
"/phorum"*)
process_response "/phorum" 7070 "true"
;;
*)
process_response "/" 7071 "false"
;;
esac
Original content in gopherspace: gopher://gopher.someodd.zip:70/0/phlog/gopher-routing.gopher.txt