Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

socket sans bind : getsockname possible ?

21 réponses
Avatar
Olivier Miakinen
[diapublication dans trois groupes, dont ceux sur Unix et Windows,
suivi vers fr.comp.reseaux.ip seul]

Bonjour,

Dans un programme en C utilisant les sockets, une fonction
reçoit un socket initialisé par la fonction socket(), mais
où le bind() n'a pas encore été fait. La fonction voudrait
déterminer quel type d'adresse on peut attacher à ce socket
(entre AF_INET et AF_INET6 à priori) : peut-elle utiliser
la fonction getsockname() pour ce faire, ou bien est-ce que
l'on risque une erreur sur certains OS du fait que le bind()
n'est pas fait ?

P.-S. : même si la question ne se pose pas vraiment, par
curiosité j'aimerais savoir aussi ce qu'il en est des
protocoles autres que IPv4 et IPv6, par exemple AF_UNIX

Cordialement,
--
Olivier Miakinen

10 réponses

1 2 3
Avatar
Nicolas George
Olivier Miakinen , dans le message <ll4lu9$2qpo$,
a écrit :
Dans un programme en C utilisant les sockets, une fonction
reçoit un socket initialisé par la fonction socket(), mais
où le bind() n'a pas encore été fait.



Le type de la socket est connu par l'appelant, puisqu'il en a eu besoin pour
la créer. Il suffit de le fournir en argument à la fonction.
Avatar
Xavier Roche
On 05/16/2014 11:31 AM, Olivier Miakinen wrote:
Dans un programme en C utilisant les sockets, une fonction
reçoit un socket initialisé par la fonction socket(), mais
où le bind() n'a pas encore été fait. La fonction voudrait
déterminer quel type d'adresse on peut attacher à ce socket
(entre AF_INET et AF_INET6 à priori) : peut-elle utiliser
la fonction getsockname()



Je ne vois pas bien comment. De plus (*1) il est bien spécifié que le
comportement est indéterminé sans lien prélable...

(*1)
<http://pubs.opengroup.org/onlinepubs/009695399/functions/getsockname.html>

P.-S. : même si la question ne se pose pas vraiment, par
curiosité j'aimerais savoir aussi ce qu'il en est des
protocoles autres que IPv4 et IPv6, par exemple AF_UNIX



La seule option réaliste, par exemple pour IPv6, est de tenter de binder
l'adresse locale (::1) sur un port anonyme (0 - assigné par le système),
le tout avec REUSEADDR (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
&one..)) et de voir si cela échoue ou non, et de fermer le port
immédiatement (en cas de succès)

Mais cela ne répond pas à l'autre souci: la plupart des distributions
Linux, et c'est le cas sous Windows probablement, embarquent au moins un
localhost en V4 (ou V6), mais rien ne dit que c'est routable.

Il faut donc aller plus loin: énumérer les interfaces réseau et regarder
si une passerelle par défaut est définie ou non, ou bien pinguer une
adresse "connue", etc..

Pour du AF_UNIX, je n'ai pas trop d'idées par contre.
Avatar
Xavier Roche
On 05/16/2014 11:46 AM, Nicolas George wrote:
Le type de la socket est connu par l'appelant, puisqu'il en a eu besoin pour
la créer. Il suffit de le fournir en argument à la fonction.



Si c'était la question posée, pour le type, getsockopt(fd, SOL_SOCKET,
SO_TYPE, ..) permet de le récupérer.

Mais je ne vois pas de SO_FAMILY ou de SO_DOMAIN - ais-je la berlue ?
Avatar
Olivier Miakinen
Le 16/05/2014 11:52, Xavier Roche m'a répondu :

Dans un programme en C utilisant les sockets, une fonction
reçoit un socket initialisé par la fonction socket(), mais
où le bind() n'a pas encore été fait. La fonction voudrait
déterminer quel type d'adresse on peut attacher à ce socket
(entre AF_INET et AF_INET6 à priori) : peut-elle utiliser
la fonction getsockname()



Je ne vois pas bien comment. De plus (*1) il est bien spécifié que le
comportement est indéterminé sans lien prélable...

(*1)
<http://pubs.opengroup.org/onlinepubs/009695399/functions/getsockname.html>



Merci pour ce lien, je n'avais trouvé que des 'man' moins complets
(conformité POSIX 2001) :
http://linux.die.net/man/2/getsockname
http://pwet.fr/man/linux/appels_systemes/getsockname
http://unixhelp.ed.ac.uk/CGI/man-cgi?getsockname+2
http://www.man-linux-magique.net/man2/getsockname.html
etc.

P.-S. : même si la question ne se pose pas vraiment, par
curiosité j'aimerais savoir aussi ce qu'il en est des
protocoles autres que IPv4 et IPv6, par exemple AF_UNIX





Si cela ne fonctionne déjà pas pour AF_INET et AF_INET6, ma
curiosité tombe du même coup pour les autres protocoles.

La seule option réaliste, par exemple pour IPv6, est de tenter de binder
l'adresse locale (::1) sur un port anonyme (0 - assigné par le système),
le tout avec REUSEADDR (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
&one..)) et de voir si cela échoue ou non, et de fermer le port
immédiatement (en cas de succès)

Mais cela ne répond pas à l'autre souci: la plupart des distributions
Linux, et c'est le cas sous Windows probablement, embarquent au moins un
localhost en V4 (ou V6), mais rien ne dit que c'est routable.

Il faut donc aller plus loin: énumérer les interfaces réseau et regarder
si une passerelle par défaut est définie ou non, ou bien pinguer une
adresse "connue", etc..



En effet. Merci pour toutes ces précisions.

Pour du AF_UNIX, je n'ai pas trop d'idées par contre.



C'est vraiment sans importance en l'occurrence.

Cordialement,
--
Olivier Miakinen
Avatar
Olivier Miakinen
Le 16/05/2014 11:59, Xavier Roche répondait à Nicolas George :

Le type de la socket est connu par l'appelant, puisqu'il en a eu besoin pour
la créer. Il suffit de le fournir en argument à la fonction.





Le type de la socket est connu par l'appelant de la fonction socket,
mais il se trouve que ce type n'est pas connu par l'appelant de la
fonction objet de ma question. Sinon, je suis bien d'accord avec toi,
il suffirait de le passer en argument.

Si c'était la question posée, pour le type, getsockopt(fd, SOL_SOCKET,
SO_TYPE, ..) permet de le récupérer.



Oui.

Mais je ne vois pas de SO_FAMILY ou de SO_DOMAIN - ai-je la berlue ?



Un tel getsockopt existe dans Windows seulement : SO_PROTOCOL_INFO.
Tu n'as pas la berlue, je ne trouve rien de tel pour les autres OS.

Cordialement,
--
Olivier Miakinen
Avatar
Xavier Roche
On 05/16/2014 12:38 PM, Olivier Miakinen wrote:
Un tel getsockopt existe dans Windows seulement : SO_PROTOCOL_INFO.
Tu n'as pas la berlue, je ne trouve rien de tel pour les autres OS.



Ah, si, j'avais la berlue, en tout les cas sous Linux:

SO_DOMAIN (since Linux 2.6.32)
Retrieves the socket domain as an integer, returning a value
such as AF_INET6. See socket(2) for details. This socket
option is read-only.
Avatar
Nicolas George
Xavier Roche , dans le message <ll4n40$kc6$, a
écrit :
La seule option réaliste, par exemple pour IPv6, est de tenter de binder



Non, ce n'est pas une option réaliste, c'est un hack pas fiable.

La solution réaliste, c'est de faire en sorte que l'information nécessaire
soit disponible sans avoir à aller la repêcher à des endroits inaccessibles.
Si ce n'est pas le cas, il faut réorganiser le programme pour que ça le
soit.
Avatar
Xavier Roche
On 05/16/2014 12:58 PM, Nicolas George wrote:
Non, ce n'est pas une option réaliste, c'est un hack pas fiable.



En quoi est-ce un hack, et en quoi est-il pas fiable ?

L'idée du bind() anonyme était non pas pour repêcher le domaine de la
socket, mais savoir si la connectivité de base Ipv6 était disponible,
par exemple. Pour cet usage seul, cette solution me semble parfaitement
fiable.
Avatar
Paul Gaborit
À (at) Fri, 16 May 2014 11:31:32 +0200,
Olivier Miakinen <om+ écrivait (wrote):

Dans un programme en C utilisant les sockets, une fonction
reçoit un socket initialisé par la fonction socket(), mais
où le bind() n'a pas encore été fait. La fonction voudrait
déterminer quel type d'adresse on peut attacher à ce socket
(entre AF_INET et AF_INET6 à priori) : peut-elle utiliser
la fonction getsockname() pour ce faire, ou bien est-ce que
l'on risque une erreur sur certains OS du fait que le bind()
n'est pas fait ?



Vu que getsockname() est une fonction POSIX, le comportement devrait
être le même sur tous les OS (qui respectent POSIX).

--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Avatar
Olivier Miakinen
Le 16/05/2014 12:55, Xavier Roche a écrit :

Ah, si, j'avais la berlue, en tout les cas sous Linux:

SO_DOMAIN (since Linux 2.6.32)
Retrieves the socket domain as an integer, returning a value
such as AF_INET6. See socket(2) for details. This socket
option is read-only.



Tiens ? Oui.

On trouve par exemple sur la toile un code qui vérifie que le
socket est un AF_INET ou un AF_INET6 avant de lui appliquer
d'autres getsockopt :

https://git.fedorahosted.org/cgit/conga.git/plain/ricci/common/Socket.cpp
<cit.>
bool
Socket::nondelaying()
{
if (!valid())
throw String("socket not valid");

int ret;
socklen_t ret_len;

/* sanity check that we use right domain (AF_INET*) and type (SOCK_STREAM) */
ret_len = sizeof(ret);
if (getsockopt(_sock, SOL_SOCKET, SO_DOMAIN, &ret, &ret_len) == -1)
throw String("getsockopt(SOL_SOCKET, SO_DOMAIN): " + String(strerror(errno)));
if (ret != AF_INET && ret != AF_INET6)
throw String("nondelaying(): called for socket domain other than AF_INET*");
ret_len = sizeof(ret);
if (getsockopt(_sock, SOL_SOCKET, SO_TYPE, &ret, &ret_len) == -1)
throw String("getsockopt(SOL_SOCKET, SO_TYPE): " + String(strerror(errno)));
if (ret != SOCK_STREAM)
throw String("nondelaying(): called for socket type other than SOCK_STREAM");

ret_len = sizeof(ret);
if (getsockopt(_sock, SOL_TCP, TCP_NODELAY, &ret, &ret_len) == -1)
throw String("getsockopt(SOL_TCP, TCP_NODELAY): " + String(strerror(errno)));
return ret;
}



</cit.>
1 2 3