#!/bin/sh
# ═══════════════════════════════════════════════════════════════════════════════════════
#  Le MMS sort par le LIEN MOBILE, meme WiFi allume — et pour N'IMPORTE QUEL operateur.
# ═══════════════════════════════════════════════════════════════════════════════════════
#
# ═══ LA CHAINE COMPLETE, mesuree le 31/07 ═══ (elle n'a pas change, seule sa SOURCE change)
# 1. Le MMSC n'est joignable que depuis le reseau MOBILE. Sinon il repond
#    « error-permanent-service-denied » : il voit une IP qui n'est pas celle de l'abonne.
# 2. Le nom du MMSC ne resout souvent QUE via le DNS de l'operateur. Interroge depuis la
#    box, il ne rend qu'une IPv4 — et si la data est IPv6-only, cette IPv4 ne mene nulle
#    part.
# 3. Or le DNS mobile lui-meme etait route par le WiFi :
#       ip route get 2001:860:d002:1201::129:202 -> dev wlan0
#    Les requetes partaient chez le FAI et n'atteignaient jamais le resolveur de
#    l'operateur. Sans lui, aucune resolution, donc aucun MMS.
#
# ☠️☠️ CE QUI CHANGE LE 24/08, ET POURQUOI : CE FICHIER NOMMAIT BOUYGUES.
#    Il portait deux constantes en dur — « MMSC_V6=2001:860:de00::/40 » et
#    « MMS_DOMAIN=bouyguestelecom.fr » — donc il ne routait RIEN chez un autre operateur,
#    en silence. Les deux sont desormais DERIVEES de « CarrierMMSC », que mmsd-tng ecrit
#    lui-meme dans sa configuration a partir de la base des fournisseurs et du modem.
#    ➜ Une seule source de verite : ce que mmsd-tng va reellement appeler.
#
# ⚖️ CE QU'ON PERD, ET C'EST ASSUME. Le /40 couvrait tout changement d'adresse du MMSC
#    sans re-resolution (nos notes donnaient 2001:860:de00:8::34, la mesure du 31/07 rend
#    2001:860:de01:5::2 : l'adresse CHANGE). On ne peut pas deviner un prefixe generique,
#    donc on route les adresses RESOLUES, en /128, et on les repose a chaque evenement
#    reseau. Si l'adresse bouge entre deux evenements, la route devient obsolete — mais
#    la panne est ALORS BRUYANTE (« service-denied », le MMSC voit l'IP du WiFi) et non
#    silencieuse. On echange un pari sur un prefixe contre un echec qui se voit.
#
# ⚠ PAS DE ROUTE IPv4 VERS LE MMSC SANS ADRESSE SOURCE. Essayee puis retiree le 31/07 :
#   elle sortait avec src 172.16.42.1 (le reseau USB !), ce qui garantissait un refus.
#   On ne pose une route v4 que si le bearer porte lui-meme une adresse v4, et on lui
#   donne explicitement ce « src ».
IFACE="$1"
ACTION="$2"

BOITE=${REDRTOS_MMS_BOITE:-/run/redrtos-mms}
LIEN="$BOITE/lien"
BESOIN_CLAT="$BOITE/besoin-clat"
ROUTES_MMSC="$BOITE/routes-mmsc"
DNSMASQ_CONF=${REDRTOS_MMS_DNSMASQ_CONF:-/etc/NetworkManager/dnsmasq.d/redrtos-mms.conf}
ANCIEN_CONF=${REDRTOS_MMS_ANCIEN_CONF:-/etc/NetworkManager/dnsmasq.d/joyeuse-mms.conf}
CONFIGS=${REDRTOS_MMS_CONFIGS:-"/home/*/.mms/*/mms /root/.mms/*/mms"}

publier_lien() {
    printf '%s\n%s\n' "$DATA_IF" "$APN_ACTIF" > "$LIEN" 2>/dev/null || return 1
    chmod 644 "$LIEN" 2>/dev/null || true
}

mkdir -p "$BOITE" 2>/dev/null

case "$ACTION" in
    up|dhcp6-change|dhcp4-change) ;;
    down)
        : > "$LIEN" 2>/dev/null
		: > "$ROUTES_MMSC" 2>/dev/null
        chmod 644 "$LIEN" 2>/dev/null || true
        rm -f "$BESOIN_CLAT"
		systemctl stop redrtos-mms-clat.service 2>/dev/null || true
        exit 0
        ;;
    *) exit 0 ;;
esac

[ "${CONNECTION_TYPE:-}" = "gsm" ] || [ "${DEVICE_IFACE:-}" = "qrtr0" ] || exit 0

# ── L'INTERFACE QUI PORTE VRAIMENT L'ADRESSE ────────────────────────────────────────────
#
# ☠️ NetworkManager nomme la connexion « qrtr0 », mais l'adresse du bearer vit sur une
#    autre interface (ici qmapmux0.0). L'ancienne version la cherchait par « ^qmapmux » —
#    un nom de pilote Qualcomm, donc un pari sur le modem. NM nous donne la bonne reponse
#    dans DEVICE_IP_IFACE ; le balayage ne sert plus que de secours.
DATA_IF="${DEVICE_IP_IFACE:-}"
if [ -z "$DATA_IF" ] || ! ip -6 -br addr show dev "$DATA_IF" scope global 2>/dev/null | grep -q .; then
    DATA_IF=$(ip -br addr show scope global 2>/dev/null \
              | awk '$1 ~ /^(qmapmux|wwan|rmnet)/ { sub(/@.*/, "", $1); print $1; exit }')
fi
if [ -z "$DATA_IF" ]; then
    echo "redrtos-mms-route: aucune interface de donnees avec adresse globale" >&2
    exit 0
fi

CONNEXION=${CONNECTION_UUID:-${CONNECTION_ID:-}}
[ -n "$CONNEXION" ] || CONNEXION=$(nmcli -g GENERAL.CON-UUID device show "${DEVICE_IFACE:-qrtr0}" 2>/dev/null | sed -n '/./{p;q;}')
APN_ACTIF=$(nmcli -g GSM.APN connection show "$CONNEXION" 2>/dev/null | sed -n '/./{p;q;}')

# ── 1. LES DNS DE L'OPERATEUR PAR LE MOBILE, sinon ils sont injoignables ────────────────
DNS_LIST=$(nmcli -t -f IP6.DNS device show "${DEVICE_IFACE:-qrtr0}" 2>/dev/null | cut -d: -f2- | grep .)
for ns in $DNS_LIST; do
    ip -6 route replace "$ns/128" dev "$DATA_IF" metric 100 2>/dev/null \
        && echo "redrtos-mms-route: DNS operateur $ns via $DATA_IF"
done

# ── 2. L'HOTE DU MMSC, TEL QUE mmsd-tng LE CONNAIT ──────────────────────────────────────
#
# ⚠ Le fichier est per-user et cree par mmsd-tng ; root le lit, il ne l'ecrit jamais ici.
#   Sur un appareil a plusieurs comptes, chaque MMSC trouve est route : ils ne
#   s'excluent pas.
HOTES=$(for c in $CONFIGS; do
            [ -f "$c" ] || continue
            sed -n 's|^CarrierMMSC=\([^ ]*\)$|\1|p' "$c"
        done | sed -e 's|^[a-zA-Z][a-zA-Z0-9+.-]*://||' -e 's|/.*$||' -e 's|^.*@||' \
                   -e 's|:[0-9]*$||' | grep -v '^NULL$' | grep . | sort -u)

if [ -z "$HOTES" ]; then
	echo "redrtos-mms-route: aucun CarrierMMSC connu — mmsd-tng n'a pas encore ses reglages" >&2
    # Pas d'echec : au tout premier demarrage mmsd-tng n'a rien ecrit. L'unite .path de
    # l'arbitre rappellera ce script des que la configuration apparaitra.
	publier_lien || echo "redrtos-mms-route: impossible de publier le lien $DATA_IF" >&2
	: > "$ROUTES_MMSC" 2>/dev/null
	exit 0
fi

# ── 3. DNS SCINDE : ces noms-la ne sont demandes QU'AU resolveur de l'operateur ─────────
#
# ☠️ LE FICHIER TEMPORAIRE NE DOIT JAMAIS S'APPELER « ...conf.tmp.$$ ». NetworkManager
#   lance dnsmasq avec --conf-dir SANS liste d'extensions, et dnsmasq(8) n'ignore alors
#   QUE les noms finissant par « ~ », commencant ou finissant par « # », et ceux
#   COMMENCANT PAR UN POINT. Un temporaire abandonne (disque plein, ou le SIGTERM que NM
#   envoie a un dispatcher trop lent) serait relu a chaque demarrage de dnsmasq,
#   DEFINITIVEMENT : au mieux des « server= » en double, au pire une ligne tronquee ->
#   dnsmasq refuse de demarrer -> PLUS AUCUNE RESOLUTION sur le telephone.
#     1. nom prefixe d'un point -> meme un survivant est ignore par dnsmasq ;
#     2. trap de nettoyage -> il ne survit pas (sauf SIGKILL, d'ou la parade 1).
#   ⚠ Le temporaire reste dans le MEME repertoire, expres : le « mv » est alors un
#     rename() atomique. Passer par /run en ferait une copie, donc une fenetre ou dnsmasq
#     peut lire un fichier a moitie ecrit.
if [ -n "$DNS_LIST" ]; then
    tmp=$(mktemp "${DNSMASQ_CONF%/*}/.redrtos-mms.XXXXXX" 2>/dev/null) || tmp=""
    if [ -z "$tmp" ]; then
        echo "redrtos-mms-route: mktemp a echoue, DNS scinde non ecrit" >&2
    else
        trap 'rm -f "$tmp"' EXIT
        trap 'rm -f "$tmp"; exit 1' INT TERM HUP
        if {
            echo "# Genere par 90-redrtos-mms-route. Ne pas editer a la main."
            echo "# L'hote du MMSC n'est resolu que par le DNS de l'operateur : lui seul"
            echo "# publie l'enregistrement AAAA, indispensable sur une data IPv6-only."
            for h in $HOTES; do
                for ns in $DNS_LIST; do echo "server=/$h/$ns"; done
            done
        } > "$tmp"
        then
            # mktemp cree en 0600 ; l'ancien « > » creait en 0644. On garde 0644.
            chmod 644 "$tmp" 2>/dev/null || true
            mv -f "$tmp" "$DNSMASQ_CONF" \
                && echo "redrtos-mms-route: DNS scinde pour $(echo $HOTES | tr '\n' ' ')"
        else
            echo "redrtos-mms-route: echec d'ecriture du DNS scinde, conf inchangee" >&2
        fi
        rm -f "$tmp"
        trap - EXIT INT TERM HUP
    fi
    unset tmp
    rm -f "$ANCIEN_CONF"   # le fichier de l'ancien nom, sinon ses « server= » restent
    nmcli general reload dns-full 2>/dev/null || true
fi

# ── 4. LE MMSC PAR LE MOBILE ────────────────────────────────────────────────────────────
#
# ⚠ On interroge le resolveur de l'operateur DIRECTEMENT, sans dependre du DNS scinde
#   pose a l'instant : dnsmasq peut ne pas l'avoir encore relu, et une course ici se
#   traduirait par une route manquante, donc un MMS muet.
SRC_V4=$(ip -4 -br addr show dev "$DATA_IF" scope global 2>/dev/null \
         | awk '{ sub(/\/.*/, "", $3); print $3; exit }')

for h in $HOTES; do
    ADRESSES=""
    for ns in $DNS_LIST; do
        ADRESSES="$ADRESSES $(nslookup "$h" "$ns" 2>/dev/null \
            | sed -n 's/^Address[0-9]*: *\([0-9a-fA-F.:]*\).*$/\1/p')"
    done
    [ -n "$(echo $ADRESSES)" ] || ADRESSES=$(getent ahosts "$h" 2>/dev/null | awk '{print $1}')

    for a in $(echo $ADRESSES | tr ' ' '\n' | sort -u); do
        case "$a" in
            # le resolveur lui-meme, ou une reponse vide : rien a router
            ''|127.*|::1) continue ;;
            *:*)
                ip -6 route replace "$a/128" dev "$DATA_IF" metric 100 2>/dev/null \
                    && echo "redrtos-mms-route: MMSC $h [$a] via $DATA_IF" ;;
            *)
                # ☠️ Sans « src », la route sortirait avec l'adresse du reseau USB et le
                #    MMSC refuserait l'abonne. Pas d'adresse v4 sur le bearer = pas de
                #    route v4 : c'est alors a l'arbitre de reclamer le CLAT.
                [ -n "$SRC_V4" ] || continue
                ip -4 route replace "$a/32" dev "$DATA_IF" src "$SRC_V4" metric 100 2>/dev/null \
                    && echo "redrtos-mms-route: MMSC $h [$a] via $DATA_IF (src $SRC_V4)" ;;
        esac
    done
done

printf '%s\n' "$HOTES" > "$ROUTES_MMSC" 2>/dev/null \
	|| echo "redrtos-mms-route: etat des routes non publie" >&2
publier_lien || echo "redrtos-mms-route: impossible de publier le lien $DATA_IF" >&2

exit 0
