From 6b1cb001e42fe7b580c3ad5af7f2acb1e3d30913 Mon Sep 17 00:00:00 2001
From: =?utf8?q?Am=C3=A9lia=20Coutard-Sander?= <git@ameliathe1st.gay>
Date: Mon, 2 Dec 2024 22:07:38 +0100
Subject: [PATCH] Rewrite using gnunet-vpn

---
 README.txt        | 45 ++++++++++++++++++++++++---------------------
 daemon.sh         | 26 --------------------------
 git-remote-gnunet | 40 +++++++++++++++++++---------------------
 handle-request.sh | 29 +++++++++++++++++++++++++++++
 run-daemons.sh    | 14 --------------
 5 files changed, 72 insertions(+), 82 deletions(-)
 delete mode 100755 daemon.sh
 create mode 100755 handle-request.sh
 delete mode 100755 run-daemons.sh

diff --git a/README.txt b/README.txt
index c9eadbe..59e784f 100644
--- a/README.txt
+++ b/README.txt
@@ -1,18 +1,27 @@
-This set of bash scripts allows one to clone/fetch/pull git repositories using gnunet, specifically the GNS and cadet.
-In the interest of demonstrating that it works, this repo is available at
-	`gnunet://git.serv.amelia.gnunet.gns.alt/git-over-gnunet`.
-	Try cloning it ! (And do tell me if it fails, or if it works.)
+This set of bash scripts allows one to clone/fetch/pull git repositories using gnunet, specifically the GNS and the GNUnet VPN.
+In the interest of demonstrating that it works, all the repos on my server are available at
+	`gnunet://git.serv.amelia.gnunet.gns.alt/<repo>.git` (see gitweb for the specific URIs).
+	Try cloning them ! (And do tell me if it fails, or if it works.)
 
 Usage:
 Client:
 	You must add the repo folder to the GIT_EXEC_PATH (for example by prepending
 		`GIT_EXEC_PATH=$GIT_EXEC_PATH:<path-to-git-over-gnunet-repo>` before every git command that uses remotes).
-	The URL to give to git is gnunet://<location of the repo>/<name of the repo>.
+	The URL to give to git is `gnunet://<gns location of the repo>/<name of the repo>`.
 Server:
-	- To run the server on a repo, simply copy the two .sh files to its root's parent, and run
-		`./run-daemons.sh <repo-name>`.
-	- You should also add a gns TXT record containing the peerid serving your repos to your zone.
-	- This will open the repo for pulling on the url `gnunet://<gns-record-name>/<repo-name>`.
+	- To serve repos, you have to setup a gnunet-vpn exit service.
+	  ```
+	  [exit]
+	  IMMEDIATE_START = YES
+	  EXIT_IFNAME = %
+	  [<gns-name>.gnunet.]
+	  TCP_REDIRECTS = 9418:169.254.86.1:9418
+	  ```
+	- You should also add a gns VPN record containing the required information to connect to the exit service.
+	  `gnunet-namestore -a -t VPN -e 1d -n <gns-name> -V '1 <peer-id> <gns-name>' -z <ego> -p`
+	- Next, you need to run the daemon: it needs to run `handle-request.sh` in the parent folder of all the repos every time someone connects to it.
+	  For example, with socat: `socat tcp4-listen:9418,bind=169.254.86.1,fork  exec:'/path/to/handle-request.sh /path/to/repos'`
+	- This will open the repos for pulling on the url `gnunet://<gns-record-name>/<repo-name>`.
 
 Features:
 	- Clone repo !
@@ -20,23 +29,17 @@ Features:
 	- Pull repo !
 
 TODO:
-	- GNUnet VPN exit service
-	- Figure out why cadet breaks when pushing, probably linked to the usage of the CLI client, GNUnet VPN rewrite might fix.
-		- gnunet-cadet exits too early, definitely an issue with the input method. GNUnet VPN will probably fix the issue.
-		In the meantime, a cat+sleep is a reasonnable enough "fix".
-	- In which case, password system to push to repos (just add the password to the port ?)
+	- Pushing, and a gnunet-identity verification system ?
 	- Pulling from anyone using a commit hash ?
 		- DHT to figure out who has what commits ?
 		- Question of trust for pulling from peers ?
-	- Multiple repos.
 
 How it works:
 Client:
 	- Uses a git remote helper to give git the ability to interpet the gnunet:// scheme.
-	- Gets the TXT record associated with the GNS name given in gnunet://<gns-name>/<repo-name>.
-	- This TXT record is a <peer-id>.
-	- To run a specific git command on repo <repo> and get its inputs and outputs, connect via cadet
-		to the peer id <peer-id>, via the port git.<repo-name>-<command>.
-	- This is used by the remote helper to run git-upload-pack remotely.
+	- Gets the VPN record associated with the GNS name given in gnunet://<gns-name>/<repo-name>.
+	- Connects to the exit service, give it the name of the repo and the command required (e.g. `git-upload-pack`).
 Server:
-	- The server just loops on a cadet server with inputs and outputs connected to the specified git-command.
+	- Reads the path and the command.
+	- Checks that the command is git-upload-pack and the path isn't malicious.
+	- Executes the command and connects it to the client.
diff --git a/daemon.sh b/daemon.sh
deleted file mode 100755
index 0720812..0000000
--- a/daemon.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-
-mktempfifo() {
-	fifo_name="$(mktemp -u)"
-	mkfifo "$fifo_name" || mktempfifo
-}
-
-if test -z $1 || test -z $2 || ! test -z $3; then
-	echo >&2 "Usage: $0 <service> <name>"
-	exit 1
-fi
-
-service="$1"
-name="$2"
-
-echo >&2 "Running $service on port \`git.$name.git-$service'."
-
-mktempfifo
-while < "$fifo_name" gnunet-cadet -o "git.$name.git-$service" | git "$service" "$(pwd)/$name" | (cat; sleep 5) | cat > "$fifo_name"; test 130 != "$?"; do
-	rm "$fifo_name"
-	mktempfifo
-done
-
-rm $fifo_name
-
-echo >&2 "Terminating $service on port \`git.$name.git-$service'."
diff --git a/git-remote-gnunet b/git-remote-gnunet
index c92e29d..59d1bcd 100755
--- a/git-remote-gnunet
+++ b/git-remote-gnunet
@@ -1,20 +1,20 @@
 #!/usr/bin/env bash
 
+shopt -s lastpipe
+
 if [[ "$2" = gnunet://* ]]; then
-	addr=${2:9}
+	echo "${2:9}"
 else
-	addr=$2
-fi
-gns="$(echo "$addr" | cut -d/ -f1)"
-repo="$(echo "$addr" | cut -d/ -f2)"
-if test -z "$gns" || test -z "$repo" || ! test -z "$(echo "$addr" | cut -d/ -f3-)"; then
+	echo "$2"
+fi | awk '
+match($0, /(([^@]*)@)?([^/]*)(\/(.*))?/, a) { printf "%s\n%s\n%s\n", a[2], a[3], a[5] }
+' | { read -r username && read -r gnsname && read -r path && ! grep '.'; }
+if test "$?" != "0"; then
 	echo >&2 "Invalid address: \`$2'."
-	echo >&2 "Format: \`gnunet://gns-name/repo-name'."
-	echo >&2 "     or \`gnunet::gns-name/repo-name'."
+	echo >&2 "URI format: \`[gnunet://][user@]gns.name/path/to/repo'."
 	exit 1
 fi
 
-
 while read -r cmd arg; do
 	case "$cmd" in
 	capabilities)
@@ -30,18 +30,16 @@ while read -r cmd arg; do
 	esac
 done
 
-if [[ "$arg" = "git-receive-pack" ]]; then
-	echo >&2 "Pushing unsupported for now."
+echo >&2 "Looking up $gnsname."
+vpn="$(gnunet-gns -u "$gnsname" -t VPN -r)"
+if test "$(echo "$vpn" | cut -d' ' -f1)" != "1"; then
+	echo >&2 "VPN record must specify TCP, not UDP."
 	exit 1
 fi
+peer="$(echo "$vpn" | cut -d' ' -f2)"
+service="$(echo "$vpn" | cut -d' ' -f3)"
+echo >&2 "Trying service \`$service' on peer \`$peer'."
+ip="$(gnunet-vpn -s "$service" -p "$peer" -t)"
+echo >&2 "Using tunnel ip \`$ip'."
 
-echo ''
-echo >&2 "Looking up $gns."
-peer="$(gnunet-gns -u "$gns" -t TXT -r)"
-if test -z "$peer"; then
-	echo >&2 "Address lookup failed: \`$gns'."
-	exit 1
-fi
-port="git.$repo.$arg"
-echo >&2 "Attempting to connect to peer $peer, on port \`$port'."
-(stdbuf -i0 -o0 -e0 cat; sleep 0.01) | gnunet-cadet "$peer" "$port"
+{ printf '%s\n' "$path" "$arg"; cat; } | socat stdio tcp4:"$ip":9418
diff --git a/handle-request.sh b/handle-request.sh
new file mode 100755
index 0000000..32785ed
--- /dev/null
+++ b/handle-request.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+if test -z $1 || ! test -z $2; then
+	echo >&2 "Usage: $0 /base/path"
+	exit 1
+fi
+base_path="$1/"
+
+read -r path || { echo "No path specified." | tee /dev/stderr; exit 1; }
+read -r comm || { echo "No command specified." | tee /dev/stderr; exit 1; }
+
+if echo "$path" | grep -q '/'; then
+	echo "Path \`$path' contains a '/'." | tee /dev/stderr
+	exit 1
+fi
+if test "$path" = '..'; then
+	echo "Path cannot be \`..'." | tee /dev/stderr
+	exit 1
+fi
+
+if test "$comm" != "git-upload-pack"; then
+	echo "Command \`$comm' has to be git-upload-pack." | tee /dev/stderr
+	exit 1
+fi
+
+git -C "$base_path$path" status >/dev/null 2>/dev/null || { echo "Not a git repository." | tee /dev/stderr; exit 1; }
+
+echo ''
+exec "$comm" "$base_path$path"
diff --git a/run-daemons.sh b/run-daemons.sh
deleted file mode 100755
index db1302d..0000000
--- a/run-daemons.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-if test -z $1 || ! test -z $2; then
-	echo >&2 "Usage: $0 <repo-name>"
-	exit 1
-fi
-
-name="$1"
-
-echo >&2 "Running pullable repo $name."
-
-./daemon.sh upload-pack "$name"
-
-echo >&2 "Terminating pullable repo $name."
-- 
2.46.0