Pourquoi ICMP est un privilège, et ce que SSHive fait à la place
Le ping n'est pas une chose unique. L'outil classique envoie une requête ICMP Echo (type 8) et attend une réponse Echo (type 0), les apparie par identifiant et numéro de séquence, puis soustrait les horodatages. ICMP n'a ni ports ni sockets ordinaires ; pour en émettre un, il faut soit un socket brut, soit, sur Darwin, un socket ICMP datagramme (SOCK_DGRAM avec IPPROTO_ICMP). Historiquement, cela voulait dire root. Sur macOS moderne, le bit setuid a disparu de /sbin/ping : Darwin autorise n'importe quel processus à ouvrir un socket ICMP datagramme non privilégié (SOCK_DGRAM avec IPPROTO_ICMP), et c'est ce qu'utilise le binaire système. /usr/sbin/traceroute, lui, reste installé setuid root.
C'est précisément le privilège que le bac à sable d'Apple n'accorde pas. Une application Mac App Store sandboxée obtient com.apple.security.network.client, qui autorise les connexions TCP et UDP sortantes. Elle n'autorise ni socket ICMP brut ni socket datagramme, et ne permet pas de lancer un binaire setuid. iOS est encore plus strict : Network.framework, seule couche réseau autorisée aux applications de l'App Store, expose TCP, UDP, QUIC et TLS. Il n'y a aucun transport ICMP dans l'API. Une application iOS qui annonce un « ping » embarque donc soit le vieil exemple SimplePing d'Apple sur des sockets BSD bruts, soit fait ce que fait SSHive.
SSHive sépare nettement l'implémentation selon la version. Le DMG macOS en téléchargement direct n'est pas sandboxé : il lance le binaire système ping avec un tableau d'arguments fixe — ping, -c, 10, nom d'hôte — via un spawn de processus sans interprétation par le shell, et redirige stdout et stderr vers l'interface par IPC. Vous obtenez du vrai ICMP parce que le binaire du système détient déjà le privilège, et le passage des arguments sous forme de tableau plutôt que de chaîne de commande rend toute injection shell via le nom d'hôte impossible. Une réserve à connaître : le binaire est appelé par son nom, il doit donc figurer dans le PATH.
Partout ailleurs, SSHive mesure une joignabilité TCP. Sur Windows et sur la version Mac App Store, il ouvre un socket Node vers le port 80, dix sondes, une par seconde, avec un délai d'attente de trois secondes, et retient comme aller-retour l'écart d'horloge entre la demande de connexion et le moment où le socket devient exploitable. Sur iPhone et iPad, la même idée tourne sur Network.framework : une NWConnection TCP vers le port 80, le chronomètre arrêté dès que la connexion atteint son état prêt, une tâche d'annulation déclenchée à trois secondes, et des résultats émis un par un dans un AsyncStream pour que le graphique et les cartes se remplissent en direct. iOS effectue vingt sondes plutôt que dix, ce qui donne au graphique assez de points pour faire apparaître une tendance.
Les compromis sont réels et méritent d'être énoncés clairement plutôt que dissimulés. Le port est figé à 80 sur tous les chemins TCP ; vous ne pouvez pas le choisir. La latence inclut le coût de la poignée de main TCP, donc les valeurs dépassent un véritable aller-retour ICMP vers le même hôte. Un hôte qui répond en ICMP mais filtre le port 80 s'affichera à 100 % de perte. Aucune option de TTL, de taille de paquet, d'intervalle ou de nombre de sondes n'existe sur aucune plateforme, et aucun mode IPv6 spécifique n'est exposé. Sur iPhone et iPad, l'adresse affichée à côté de chaque sonde est la chaîne que vous avez saisie, pas l'IP résolue. En échange, vous disposez d'un signal de joignabilité et de latence cohérent et honnête sur chacun de vos appareils Apple — y compris les deux où le système d'exploitation n'autorisera jamais mieux.