socket sans bind : getsockname possible ?

Le
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
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 3
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Nicolas George
Le #26137472
Olivier Miakinen , dans le message 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.
Xavier Roche
Le #26137462
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)

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.
Xavier Roche
Le #26137452
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 ?
Olivier Miakinen
Le #26137512
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)



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
Olivier Miakinen
Le #26137532
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
Xavier Roche
Le #26137622
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.
Nicolas George
Le #26137612
Xavier Roche , dans le message é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.
Xavier Roche
Le #26137702
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.
Paul Gaborit
Le #26137872
À (at) Fri, 16 May 2014 11:31:32 +0200,
Olivier Miakinen
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 -
Olivier Miakinen
Le #26137932
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
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;
}



Publicité
Poster une réponse
Anonyme