#!/usr/bin/env bash
# network-metadata-logger/netlog.sh
# Logs FIFA 23 network connection metadata: host, port, protocol, timing.
# Does NOT capture payload, does NOT decrypt TLS.
#
# Usage: netlog.sh [--interval 2] [--duration 120]
#   Polls /proc/net/tcp* and resolves FIFA23 PIDs every N seconds.
#   Output: CSV to stdout and netlog-TIMESTAMP.csv in this directory.

set -euo pipefail

INTERVAL="${INTERVAL:-2}"
DURATION="${DURATION:-300}"
OUT_DIR="$(cd "$(dirname "$0")" && pwd)/logs"
mkdir -p "$OUT_DIR"
OUT_FILE="$OUT_DIR/netlog-$(date +%Y%m%d-%H%M%S).csv"

log() { echo "$*" | tee -a "$OUT_FILE"; }

find_fifa_pids() {
  pgrep -x FIFA23.exe 2>/dev/null || \
  pgrep -f FIFA23.exe 2>/dev/null || \
  true
}

# Decode a hex IP:port pair from /proc/net/tcp
# e.g. "0F02000A:01BB" → "10.0.2.15:443"
decode_addr() {
  local hex_ip="${1%%:*}"
  local hex_port="${1##*:}"
  local port=$((16#$hex_port))
  # IPv4 in /proc/net/tcp is little-endian
  local b1=$((16#${hex_ip:6:2}))
  local b2=$((16#${hex_ip:4:2}))
  local b3=$((16#${hex_ip:2:2}))
  local b4=$((16#${hex_ip:0:2}))
  echo "${b1}.${b2}.${b3}.${b4}:${port}"
}

# TCP state codes
state_name() {
  case "$1" in
    01) echo ESTABLISHED ;;  02) echo SYN_SENT ;;
    03) echo SYN_RECV ;;     04) echo FIN_WAIT1 ;;
    05) echo FIN_WAIT2 ;;    06) echo TIME_WAIT ;;
    07) echo CLOSE ;;        08) echo CLOSE_WAIT ;;
    09) echo LAST_ACK ;;     0A) echo LISTEN ;;
    0B) echo CLOSING ;;      *) echo "STATE_$1" ;;
  esac
}

# Resolve remote IP → hostname (best-effort, non-blocking)
resolve_host() {
  local ip="${1%%:*}"
  # Skip local/loopback
  case "$ip" in
    127.*|10.*|192.168.*|172.1[6-9].*|172.2[0-9].*|172.3[0-1].*) echo "$ip"; return ;;
  esac
  getent hosts "$ip" 2>/dev/null | awk '{print $2}' | head -1 || echo "$ip"
}

# Categorise destination by port/host heuristic
categorise() {
  local host="$1" port="$2"
  case "$port" in
    443|8443) echo "HTTPS/TLS" ;;
    80)       echo "HTTP" ;;
    10853)    echo "EA-LSX-auth" ;;
    3216|3478|3479|3480) echo "EA-UDP-STUN-or-relay" ;;
    *)
      case "$host" in
        *ea.com*|*easports.com*|*footapi.com*|*ugc.*) echo "EA-service" ;;
        *akamai*|*amazonaws*|*cloudfront*)            echo "CDN" ;;
        *)                                             echo "other" ;;
      esac
      ;;
  esac
}

# Build inode→pid map from /proc
build_inode_pid_map() {
  declare -gA INODE_PID
  INODE_PID=()
  for pid_dir in /proc/[0-9]*/fd; do
    local pid="${pid_dir%%/fd}"
    pid="${pid##/proc/}"
    for link in "$pid_dir"/[0-9]* 2>/dev/null; do
      local target
      target=$(readlink "$link" 2>/dev/null) || continue
      if [[ "$target" == "socket:["* ]]; then
        local inode="${target//[^0-9]/}"
        INODE_PID["$inode"]="$pid"
      fi
    done
  done 2>/dev/null || true
}

log "timestamp,pid,proc,local_addr,remote_host,remote_addr,port,state,category"

declare -A SEEN_CONNS
END_TIME=$(( $(date +%s) + DURATION ))

echo "Logging FIFA 23 network metadata to $OUT_FILE (${DURATION}s, Ctrl-C to stop)..."

while [ "$(date +%s)" -lt "$END_TIME" ]; do
  TS=$(date +%H:%M:%S)
  build_inode_pid_map

  # Read /proc/net/tcp (IPv4 ESTABLISHED and SYN_SENT)
  while read -r _ local_hex remote_hex state _ _ _ _ _ inode _rest; do
    [ "$state" = "01" ] || [ "$state" = "02" ] || continue
    local_addr=$(decode_addr "$local_hex")
    remote_addr=$(decode_addr "$remote_hex")
    remote_ip="${remote_addr%%:*}"
    remote_port="${remote_addr##*:}"
    pid="${INODE_PID[$inode]:-}"
    [ -z "$pid" ] && continue

    # Filter to FIFA-related PIDs (or log all if no FIFA found)
    proc=$(cat "/proc/$pid/comm" 2>/dev/null) || continue

    state_str=$(state_name "$state")
    key="${pid}:${local_addr}:${remote_addr}"
    if [ -z "${SEEN_CONNS[$key]:-}" ] || [ "$state_str" != "${SEEN_CONNS[$key]}" ]; then
      SEEN_CONNS["$key"]="$state_str"
      host=$(resolve_host "$remote_ip")
      cat=$(categorise "$host" "$remote_port")
      log "$TS,$pid,$proc,$local_addr,$host,$remote_addr,$remote_port,$state_str,$cat"
    fi
  done < /proc/net/tcp 2>/dev/null || true

  sleep "$INTERVAL"
done

echo "Done. Output: $OUT_FILE"
