racket/udp: fixed for udp-bind!' and udp-connect!'

Fix ephemeral-port support in `udp-bind!', and
change `udp-bind!' and `udp-connect!' to try address
resolutions in order to find one that works (which is
typically needed to auto-select an IPv4 or IPv6 variant
of an address).
This commit is contained in:
Matthew Flatt 2013-04-10 08:56:12 -06:00
parent 7c0f35e138
commit 842da32e48

View File

@ -3175,7 +3175,7 @@ static Scheme_Object *udp_bind_or_connect(const char *name, int argc, Scheme_Obj
Scheme_UDP *udp; Scheme_UDP *udp;
char *address = NULL; char *address = NULL;
unsigned short port = 0; unsigned short port = 0;
GC_CAN_IGNORE struct mz_addrinfo *udp_bind_addr = NULL; GC_CAN_IGNORE struct mz_addrinfo *udp_bind_addr = NULL, *addr;
udp = (Scheme_UDP *)argv[0]; udp = (Scheme_UDP *)argv[0];
@ -3219,8 +3219,8 @@ static Scheme_Object *udp_bind_or_connect(const char *name, int argc, Scheme_Obj
return NULL; return NULL;
} }
/* DISCONNECT */
if (SCHEME_FALSEP(argv[1]) && SCHEME_FALSEP(argv[2])) { if (SCHEME_FALSEP(argv[1]) && SCHEME_FALSEP(argv[2])) {
/* DISCONNECT */
int errid = 0; int errid = 0;
if (udp->connected) { if (udp->connected) {
int ok; int ok;
@ -3253,8 +3253,8 @@ static Scheme_Object *udp_bind_or_connect(const char *name, int argc, Scheme_Obj
return scheme_void; return scheme_void;
} }
{
/* RESOLVE ADDRESS */ /* RESOLVE ADDRESS */
if (address || port) {
int err; int err;
udp_bind_addr = scheme_get_host_address(address, port, &err, -1, do_bind, 0); udp_bind_addr = scheme_get_host_address(address, port, &err, -1, do_bind, 0);
if (!udp_bind_addr) { if (!udp_bind_addr) {
@ -3269,15 +3269,23 @@ static Scheme_Object *udp_bind_or_connect(const char *name, int argc, Scheme_Obj
} }
} }
/* CONNECT CASE */
if (!do_bind) { if (!do_bind) {
int ok = !connect(udp->s, udp_bind_addr->ai_addr, udp_bind_addr->ai_addrlen); /* CONNECT CASE */
mz_freeaddrinfo(udp_bind_addr); int ok, errid = -1;
/* connect using first address that works: */
for (addr = udp_bind_addr; addr; addr = addr->ai_next) {
ok = !connect(udp->s, addr->ai_addr, addr->ai_addrlen);
if (ok) { if (ok) {
udp->connected = 1; udp->connected = 1;
mz_freeaddrinfo(udp_bind_addr);
return scheme_void; return scheme_void;
} else
errid = SOCK_ERRNO();
} }
else {
mz_freeaddrinfo(udp_bind_addr);
scheme_raise_exn(MZEXN_FAIL_NETWORK, scheme_raise_exn(MZEXN_FAIL_NETWORK,
"%s: can't connect\n" "%s: can't connect\n"
" address: %s\n" " address: %s\n"
@ -3286,13 +3294,12 @@ static Scheme_Object *udp_bind_or_connect(const char *name, int argc, Scheme_Obj
name, name,
address ? address : "#f", address ? address : "#f",
port, port,
SOCK_ERRNO()); errid);
return NULL; return NULL;
} } else {
}
/* BIND CASE */ /* BIND CASE */
else { int ok, errid = -1;
int ok;
if ((argc > 3) && SCHEME_TRUEP(argv[3])) { if ((argc > 3) && SCHEME_TRUEP(argv[3])) {
int one = 1; int one = 1;
@ -3306,24 +3313,19 @@ static Scheme_Object *udp_bind_or_connect(const char *name, int argc, Scheme_Obj
} }
} }
if (udp_bind_addr == NULL ) { /* bind using first address that works: */
GC_CAN_IGNORE mz_unspec_address ua; for (addr = udp_bind_addr; addr; addr = addr->ai_next) {
memset(&ua, 0, sizeof(mz_unspec_address)); ok = !bind(udp->s, addr->ai_addr, addr->ai_addrlen);
ua.sin_family = AF_UNSPEC;
ua.sin_port = 0;
memset(&(ua.sin_addr), 0, sizeof(ua.sin_addr));
memset(&(ua.sin_zero), 0, sizeof(ua.sin_zero));
ok = !bind(udp->s, (struct sockaddr *)&ua, sizeof(ua));
}
else {
ok = !bind(udp->s, udp_bind_addr->ai_addr, udp_bind_addr->ai_addrlen);
mz_freeaddrinfo(udp_bind_addr);
}
if (ok) { if (ok) {
udp->bound = 1; udp->bound = 1;
mz_freeaddrinfo(udp_bind_addr);
return scheme_void; return scheme_void;
} else
errid = SOCK_ERRNO();
} }
else {
mz_freeaddrinfo(udp_bind_addr);
scheme_raise_exn(MZEXN_FAIL_NETWORK, scheme_raise_exn(MZEXN_FAIL_NETWORK,
"%s: can't bind\n" "%s: can't bind\n"
" address: %s\n" " address: %s\n"
@ -3332,11 +3334,10 @@ static Scheme_Object *udp_bind_or_connect(const char *name, int argc, Scheme_Obj
name, name,
address ? address : "#f", address ? address : "#f",
port, port,
SOCK_ERRNO()); errid);
return NULL; return NULL;
} }
} }
}
#else #else
return scheme_void; return scheme_void;
#endif #endif