feat: dbip geolocation (#4)

* ip-location-api and db-ip

* geolocation with dbip
This commit is contained in:
lollerfirst
2025-08-26 15:51:18 +02:00
committed by GitHub
parent c506f77942
commit 0a5496909c
3 changed files with 123 additions and 31 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
- name: Install required dependencies - name: Install required dependencies
run: | run: |
sudo apt-get update && sudo apt-get install -y jq curl dnsutils sudo apt-get update && sudo apt-get install -y jq dnsutils gzip
wget https://github.com/fiatjaf/nak/releases/download/v0.15.3/nak-v0.15.3-linux-amd64 wget https://github.com/fiatjaf/nak/releases/download/v0.15.3/nak-v0.15.3-linux-amd64
sudo mv nak-v0.15.3-linux-amd64 /usr/local/bin/nak sudo mv nak-v0.15.3-linux-amd64 /usr/local/bin/nak
sudo chmod +x /usr/local/bin/nak sudo chmod +x /usr/local/bin/nak
+8 -2
View File
@@ -1,2 +1,8 @@
# georelays # GeoRelays
a collection of nostr relays and their estimated locations A collection of nostr relays and their estimated locations
---
## Attribution
`nostr_relays.csv` and `relay_discovery_results.json` include DB-IP data created by db-ip, available at https://www.db-ip.com.
+114 -28
View File
@@ -1,47 +1,133 @@
#!/bin/bash #!/bin/bash
# Check if the output file is provided set -uo pipefail
if [ "$#" -ne 1 ]; then if [ "$#" -ne 1 ]; then
echo "Usage: $0 output.csv" echo "Usage: $0 output.csv"
exit 1 exit 1
fi fi
output_file="$1" output_file="$1"
wget -O dbip-city-ipv4-num.csv.gz https://raw.githubusercontent.com/sapics/ip-location-db/refs/heads/main/dbip-city/dbip-city-ipv4-num.csv.gz
gzip -fd dbip-city-ipv4-num.csv.gz
CSV_FILE="dbip-city-ipv4-num.csv"
declare -a start_arr
declare -a end_arr
declare -a gps_arr
# Convert dotted IPv4 to numeric (unsigned 32-bit)
# Usage: ip_to_num "1.2.3.4"
ip_to_num() {
local ip="$1"
IFS='.' read -r a b c d <<< "$ip"
for part in "$a" "$b" "$c" "$d"; do
if ! [[ "$part" =~ ^[0-9]+$ ]] || (( part < 0 || part > 255 )); then
return 1
fi
done
printf '%u' "$(( (a * 16777216) + (b * 65536) + (c * 256) + d ))"
}
# Load CSV and populate arrays
load_csv() {
local file="$1"
if [[ ! -f "$file" ]]; then
echo "CSV not found: $file" >&2
return 2
fi
local line
local IFS_old="$IFS"
while IFS= read -r line || [[ -n "$line" ]]; do
[[ -z "$line" || "${line:0:1}" == "#" ]] && continue
IFS=',' read -r -a f <<< "$line"
IFS="$IFS_old"
[[ ${#f[@]} -lt 2 ]] && continue
local s="${f[0]}" e="${f[1]}"
local lat="" lon=""
[[ ${#f[@]} -ge 8 ]] && lat="${f[7]}"
[[ ${#f[@]} -ge 9 ]] && lon="${f[8]}"
s="${s//[[:space:]]/}" e="${e//[[:space:]]/}"
lat="${lat//[[:space:]]/}" lon="${lon//[[:space:]]/}"
start_arr+=("$s")
end_arr+=("$e")
if [[ -n "$lat" && -n "$lon" ]]; then
gps_arr+=("${lat},${lon}")
else
gps_arr+=("")
fi
done < "$file"
}
# Binary search. Now accepts dotted IP, converts it, then searches.
# Usage: lookup_ip "1.2.3.4"
# Prints "lat,lon" if found, else returns 1.
lookup_ip() {
local ip="$1"
local num
num="$(ip_to_num "$ip")" || return 2
local lo=0
local hi=$(( ${#start_arr[@]} - 1 ))
if (( hi < 0 )); then return 1; fi
if (( num < start_arr[0] || num > end_arr[hi] )); then return 1; fi
while (( lo <= hi )); do
local mid=$(( (lo + hi) / 2 ))
local s="${start_arr[mid]}"
local e="${end_arr[mid]}"
if (( num < s )); then
hi=$(( mid - 1 ))
elif (( num > e )); then
lo=$(( mid + 1 ))
else
local gps="${gps_arr[mid]}"
if [[ -n "$gps" ]]; then
printf '%s\n' "$gps"
return 0
else
return 1
fi
fi
done
return 1
}
echo "Loading database into memory..."
load_csv $CSV_FILE
echo "Relay URL,Latitude,Longitude" > "$output_file" echo "Relay URL,Latitude,Longitude" > "$output_file"
while IFS= read -r url; do while IFS= read -r url; do
a_records=$(dig +short "$url" A) a_records=$(dig +short "$url" A 2>/dev/null)
if [[ -n "$a_records" ]]; then if [[ -n "$a_records" ]]; then
for ip in $a_records; do for ip in $a_records; do
echo "Attempting geo-location lookup for $url -> $ip" echo "Attempting geo-location lookup for $url -> $ip"
location_data=$(lookup_ip "$ip" 2>/dev/null || true)
location_data=""
retries=3 if [[ -z "$location_data" ]]; then
attempt=0 echo "geolocation failed for $ip"
continue
fi
latitude=$(printf '%s' "$location_data" | cut -d',' -f1)
longitude=$(printf '%s' "$location_data" | cut -d',' -f2)
while [[ -z "$location_data" && $attempt -lt $retries ]]; do if [[ -n "$latitude" && -n "$longitude" ]]; then
location_data=$(curl -s "https://ipinfo.io/$ip/json") echo "$url: latitude=$latitude, longitude=$longitude"
#echo "location data: $(echo $location_data | jq)" echo "$url,$latitude,$longitude" >> "$output_file"
attempt=$((attempt + 1)) break
sleep 1 else
done echo "can't extract latidude or longitude for $ip"
continue
latitude=$(echo "$location_data" | jq -r '.loc' | cut -d',' -f1) fi
longitude=$(echo "$location_data" | jq -r '.loc' | cut -d',' -f2) done
if [[ -n "$latitude" && -n "$longitude" && $latitude != "null" && $longitude != "null" ]]; then
echo "$url: latitude=$latitude, longitude=$longitude"
echo "$url,$latitude,$longitude" >> "$output_file"
break
else
echo "geolocation failed for $url"
continue
fi
done
else else
echo "geolocation failed for $url" echo "geolocation failed for $url"
fi fi