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 */ {
if (address || port) { /* RESOLVE ADDRESS */
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,30 +3269,37 @@ 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 */
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) {
udp->connected = 1;
mz_freeaddrinfo(udp_bind_addr);
return scheme_void;
} else
errid = SOCK_ERRNO();
}
mz_freeaddrinfo(udp_bind_addr); mz_freeaddrinfo(udp_bind_addr);
if (ok) {
udp->connected = 1; scheme_raise_exn(MZEXN_FAIL_NETWORK,
return scheme_void; "%s: can't connect\n"
} " address: %s\n"
else { " port number: %d\n"
scheme_raise_exn(MZEXN_FAIL_NETWORK, " system error: %E",
"%s: can't connect\n" name,
" address: %s\n" address ? address : "#f",
" port number: %d\n" port,
" system error: %E", errid);
name,
address ? address : "#f", return NULL;
port, } else {
SOCK_ERRNO()); /* BIND CASE */
return NULL; int ok, errid = -1;
}
}
/* BIND CASE */
else {
int ok;
if ((argc > 3) && SCHEME_TRUEP(argv[3])) { if ((argc > 3) && SCHEME_TRUEP(argv[3])) {
int one = 1; int one = 1;
@ -3306,35 +3313,29 @@ 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; if (ok) {
ua.sin_port = 0; udp->bound = 1;
memset(&(ua.sin_addr), 0, sizeof(ua.sin_addr)); mz_freeaddrinfo(udp_bind_addr);
memset(&(ua.sin_zero), 0, sizeof(ua.sin_zero)); return scheme_void;
ok = !bind(udp->s, (struct sockaddr *)&ua, sizeof(ua)); } else
} errid = SOCK_ERRNO();
else {
ok = !bind(udp->s, udp_bind_addr->ai_addr, udp_bind_addr->ai_addrlen);
mz_freeaddrinfo(udp_bind_addr);
}
if (ok) {
udp->bound = 1;
return scheme_void;
}
else {
scheme_raise_exn(MZEXN_FAIL_NETWORK,
"%s: can't bind\n"
" address: %s\n"
" port number: %d\n"
" system error: %E",
name,
address ? address : "#f",
port,
SOCK_ERRNO());
return NULL;
} }
mz_freeaddrinfo(udp_bind_addr);
scheme_raise_exn(MZEXN_FAIL_NETWORK,
"%s: can't bind\n"
" address: %s\n"
" port number: %d\n"
" system error: %E",
name,
address ? address : "#f",
port,
errid);
return NULL;
} }
} }
#else #else