Skip to content

Commit

Permalink
BLANK! vs. error for read dns:// when network works, but not found
Browse files Browse the repository at this point in the history
This changes the READ DNS:// behavior to give errors when a truly
exceptional network problem happens, but to give blanks when a lookup
connects to the name server but just fails.

It also gets rid of the asynchronous DNS lookup code on Windows.  The
relevant API calls were not carried forward with IPv6, and are thus
deprecated.  Microsoft "urges" that the more standard/POSIX network API
be used with a thread if this capability is desired.
  • Loading branch information
hostilefork committed May 17, 2017
1 parent 9d790a4 commit c712df7
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 125 deletions.
8 changes: 4 additions & 4 deletions src/core/d-break.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ REBOOL Do_Breakpoint_Throws(
}

// Call the host's breakpoint hook.
// The DECLARE_LOCAL is here and not outside the loop
// The DECLARE_LOCAL is here and not outside the loop
// due to wanting to avoid "longjmp clobbering" warnings
// (seen in optimized builds on Android).
//
Expand Down Expand Up @@ -265,7 +265,7 @@ REBOOL Do_Breakpoint_Throws(
return_default:

if (do_default) {
// The DECLARE_LOCAL is here and not outside the loop
// The DECLARE_LOCAL is here and not outside the loop
// due to wanting to avoid "longjmp clobbering" warnings
// (seen in optimized builds on Android).
//
Expand All @@ -284,7 +284,7 @@ REBOOL Do_Breakpoint_Throws(
}
}
else {
// The DECLARE_LOCAL is here and not outside the loop
// The DECLARE_LOCAL is here and not outside the loop
// due to wanting to avoid "longjmp clobbering" warnings
// (seen in optimized builds on Android).
//
Expand All @@ -304,7 +304,7 @@ REBOOL Do_Breakpoint_Throws(
// natives do not currently respond to definitional returns...though
// they can do so just as well as FUNCTION! can.
//
// The DECLARE_LOCAL is here and not outside the loop
// The DECLARE_LOCAL is here and not outside the loop
// due to wanting to avoid "longjmp clobbering" warnings
// (seen in optimized builds on Android).
//
Expand Down
10 changes: 5 additions & 5 deletions src/core/n-data.c
Original file line number Diff line number Diff line change
Expand Up @@ -556,10 +556,6 @@ REBNATIVE(get)
REBVAL *dest;
REBSPC *specifier;

// Move the argument into the single cell in the frame if it's not a
// block, so the same enumeration-up-to-an-END marker can work on it
// as for handling a block of items.
//
REBARR *results;

if (IS_BLOCK(ARG(source))) {
Expand All @@ -579,6 +575,10 @@ REBNATIVE(get)
dest = SINK(ARR_HEAD(results));
}
else {
// Move the argument into the single cell in the frame if it's not a
// block, so the same enumeration-up-to-an-END marker can work on it
// as for handling a block of items.
//
Move_Value(D_CELL, ARG(source));
source = D_CELL;
specifier = SPECIFIED;
Expand Down Expand Up @@ -660,7 +660,7 @@ REBNATIVE(get)
//
// to-value: native [
//
// {Turns unset to NONE, with ANY-VALUE! passing through. (See: OPT)}
// {Turns voids into blanks, with ANY-VALUE! passing through. (See: OPT)}
//
// return: [any-value!]
// value [<opt> any-value!]
Expand Down
48 changes: 27 additions & 21 deletions src/core/p-dns.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,10 @@ static REB_R DNS_Actor(REBFRM *frame_, REBCTX *port, REBSYM action)
if (result < 0)
fail (Error_On_Port(RE_READ_ERROR, port, sock->error));

// Wait for it...
if (sync && result == DR_PEND) {
for (len = 0; GET_FLAG(sock->flags, RRF_PENDING) && len < 10; len++) {
assert(FALSE); // asynchronous R3-Alpha DNS code removed
len = 0;
for (; GET_FLAG(sock->flags, RRF_PENDING) && len < 10; ++len) {
OS_WAIT(2000, 0);
}
len = 1;
Expand All @@ -126,27 +127,32 @@ static REB_R DNS_Actor(REBFRM *frame_, REBCTX *port, REBSYM action)
fail (Error_On_Port(RE_NOT_OPEN, port, -12));

len = Get_Num_From_Arg(arg); // Position
pick:
if (len == 1) {
if (
!DEVREQ_NET(sock)->host_info
|| !GET_FLAG(sock->flags, RRF_DONE
)) {
return R_VOID;
}
if (sock->error) {
OS_DO_DEVICE(sock, RDC_CLOSE);
fail (Error_On_Port(RE_READ_ERROR, port, sock->error));
}
if (GET_FLAG(sock->modes, RST_REVERSE)) {
Init_String(D_OUT, Copy_Bytes(sock->common.data, LEN_BYTES(sock->common.data)));
} else {
Set_Tuple(D_OUT, cast(REBYTE*, &DEVREQ_NET(sock)->remote_ip), 4);
}
pick:
if (len != 1)
fail (Error_Out_Of_Range(arg));

assert(GET_FLAG(sock->flags, RRF_DONE)); // R3-Alpha async DNS removed

if (sock->error) {
OS_DO_DEVICE(sock, RDC_CLOSE);
fail (Error_On_Port(RE_READ_ERROR, port, sock->error));
}
else
fail (Error_Out_Of_Range(arg));

if (DEVREQ_NET(sock)->host_info == NULL) {
Init_Blank(D_OUT); // HOST_NOT_FOUND or NO_ADDRESS blank vs. error
return R_OUT; // READ action currently required to use R_OUTs
}

if (GET_FLAG(sock->modes, RST_REVERSE)) {
Init_String(
D_OUT,
Copy_Bytes(sock->common.data, LEN_BYTES(sock->common.data))
);
}
else {
Set_Tuple(D_OUT, cast(REBYTE*, &DEVREQ_NET(sock)->remote_ip), 4);
}
OS_DO_DEVICE(sock, RDC_CLOSE);
break;

case SYM_OPEN: {
Expand Down
73 changes: 36 additions & 37 deletions src/os/dev-dns.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ extern i32 Request_Size_Net(REBREQ *); // Share same request struct

extern void Signal_Device(REBREQ *req, REBINT type);

#ifdef HAS_ASYNC_DNS
// Async DNS requires a window handle to signal completion (WSAASync)
extern HWND Event_Handle;
#endif

//
// Open_DNS: C
Expand All @@ -70,12 +66,7 @@ DEVICE_CMD Close_DNS(REBREQ *req)
{
// Terminate a pending request:
struct devreq_net *sock = DEVREQ_NET(req);
#ifdef HAS_ASYNC_DNS
if (GET_FLAG(req->flags, RRF_PENDING)) {
CLR_FLAG(req->flags, RRF_PENDING);
if (req->requestee.handle) WSACancelAsyncRequest(req->requestee.handle);
}
#endif

if (sock->host_info) OS_FREE(sock->host_info);
sock->host_info = 0;
req->requestee.handle = 0;
Expand All @@ -88,57 +79,65 @@ DEVICE_CMD Close_DNS(REBREQ *req)
// Read_DNS: C
//
// Initiate the GetHost request and return immediately.
// Note the temporary results buffer (must be freed later).
// Note the temporary results buffer (must be freed later by the caller).
//
// !!! R3-Alpha used WSAAsyncGetHostByName and WSAAsyncGetHostByName to do
// non-blocking DNS lookup on Windows. These functions are deprecated, since
// they do not have IPv6 equivalents...so applications that want asynchronous
// lookup are expected to use their own threads and call getnameinfo().
//
// !!! R3-Alpha was written to use the old non-reentrant form in POSIX, but
// glibc2 implements _r versions.
//
DEVICE_CMD Read_DNS(REBREQ *req)
{
char *host;
#ifdef HAS_ASYNC_DNS
HANDLE handle;
#else
HOSTENT *he;
#endif

struct devreq_net *sock = DEVREQ_NET(req);
host = OS_ALLOC_N(char, MAXGETHOSTSTRUCT); // be sure to free it

#ifdef HAS_ASYNC_DNS
if (!GET_FLAG(req->modes, RST_REVERSE)) // hostname lookup
handle = WSAAsyncGetHostByName(Event_Handle, WM_DNS, s_cast(req->common.data), host, MAXGETHOSTSTRUCT);
else
handle = WSAAsyncGetHostByAddr(Event_Handle, WM_DNS, s_cast(&sock->remote_ip), 4, AF_INET, host, MAXGETHOSTSTRUCT);

if (handle != 0) {
sock->host_info = host;
req->requestee.handle = handle;
return DR_PEND; // keep it on pending list
}
#else
// Use old-style blocking DNS (mainly for testing purposes):
char *host = OS_ALLOC_N(char, MAXGETHOSTSTRUCT);

HOSTENT *he;
if (GET_FLAG(req->modes, RST_REVERSE)) {
// 93.184.216.34 => example.com
he = gethostbyaddr(
cast(char*, &sock->remote_ip), 4, AF_INET
);
if (he) {
if (he != NULL) {
sock->host_info = host; //???
req->common.data = b_cast(he->h_name);
SET_FLAG(req->flags, RRF_DONE);
return DR_DONE;
}
}
else {
// example.com => 93.184.216.34
he = gethostbyname(s_cast(req->common.data));
if (he) {
if (he != NULL) {
sock->host_info = host; // ?? who deallocs?
memcpy(&sock->remote_ip, *he->h_addr_list, 4); //he->h_length);
SET_FLAG(req->flags, RRF_DONE);
return DR_DONE;
}
}
#endif

OS_FREE(host);
sock->host_info = 0;
sock->host_info = NULL;

switch (h_errno) {
case HOST_NOT_FOUND: // The specified host is unknown
case NO_ADDRESS: // (or NO_DATA) name is valid but has no IP
//
// The READ should return a blank in these cases, vs. raise an
// error, for convenience in handling.
//
SET_FLAG(req->flags, RRF_DONE);
return DR_DONE;

case NO_RECOVERY: // A nonrecoverable name server error occurred
case TRY_AGAIN: // Temporary error on authoritative name server
break;

default:
assert(FALSE);
}

req->error = GET_ERROR;
//Signal_Device(req, EVT_ERROR);
Expand Down
49 changes: 6 additions & 43 deletions src/os/dev-net.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,6 @@ DEVICE_CMD Close_Socket(REBREQ *req)

// If DNS pending, abort it:
if (sock->host_info) { // indicates DNS phase active
#ifdef HAS_ASYNC_DNS
if (req->requestee.handle) WSACancelAsyncRequest(req->requestee.handle);
#endif
OS_FREE(sock->host_info);
req->requestee.socket = req->length; // Restore TCP socket (see Lookup)
}
Expand All @@ -247,54 +244,20 @@ DEVICE_CMD Close_Socket(REBREQ *req)
//
DEVICE_CMD Lookup_Socket(REBREQ *req)
{
#ifdef TO_WINDOWS
HANDLE handle;
#endif
HOSTENT *host;

struct devreq_net *sock = DEVREQ_NET(req);
sock->host_info = NULL; // no allocated data

#ifdef HAS_ASYNC_DNS
// Check if we are polling for completion:
if ((host = cast(HOSTENT*, sock->host_info))) {
// The windows main event handler will change this when it gets WM_DNS event:
if (!GET_FLAG(req->flags, RRF_DONE)) return DR_PEND; // still waiting
CLR_FLAG(req->flags, RRF_DONE);
if (!req->error) { // Success!
host = cast(HOSTENT*, sock->host_info);
memcpy(&sock->remote_ip, *host->h_addr_list, 4); //he->h_length);
Signal_Device(req, EVT_LOOKUP);
}
else
Signal_Device(req, EVT_ERROR);
OS_FREE(host); // free what we allocated earlier
req->requestee.socket = req->length; // Restore TCP socket saved below
sock->host_info = 0;
return DR_DONE;
}

// Else, make the lookup request:
host = cast(HOSTENT*, OS_ALLOC_N(char, MAXGETHOSTSTRUCT));
handle = WSAAsyncGetHostByName(Event_Handle, WM_DNS, s_cast(req->common.data), cast(char*, host), MAXGETHOSTSTRUCT);
if (handle != 0) {
sock->host_info = host;
req->length = req->requestee.socket; // save TCP socket temporarily
req->requestee.handle = handle;
return DR_PEND; // keep it on pending list
}
OS_FREE(host);
#else
// Use old-style blocking DNS (mainly for testing purposes):
host = gethostbyname(s_cast(req->common.data));
sock->host_info = 0; // no allocated data
// !!! R3-Alpha would use asynchronous DNS API on Windows, but that API
// was not supported by IPv6, and developers are encouraged to use normal
// socket APIs with their own threads.

if (host) {
HOSTENT *host = gethostbyname(s_cast(req->common.data));
if (host != NULL) {
memcpy(&sock->remote_ip, *host->h_addr_list, 4); //he->h_length);
CLR_FLAG(req->flags, RRF_DONE);
Signal_Device(req, EVT_LOOKUP);
return DR_DONE;
}
#endif

req->error = GET_ERROR;
//Signal_Device(req, EVT_ERROR);
Expand Down
2 changes: 1 addition & 1 deletion src/os/host-main.c
Original file line number Diff line number Diff line change
Expand Up @@ -988,7 +988,7 @@ int main(int argc, char **argv_ansi)
//

while (NOT(finished)) {
// The DECLARE_LOCAL is here and not outside the loop
// The DECLARE_LOCAL is here and not outside the loop
// due to wanting to avoid "longjmp clobbering" warnings
// (seen in optimized builds on Android).
//
Expand Down
Loading

0 comments on commit c712df7

Please sign in to comment.