mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
6.6 KiB
225 lines
6.6 KiB
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "uv.h"
|
|
#include "tree.h"
|
|
#include "uv-common.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
struct uv_ares_task_s {
|
|
UV_HANDLE_FIELDS
|
|
ares_socket_t sock;
|
|
uv_poll_t poll_watcher;
|
|
RB_ENTRY(uv_ares_task_s) node;
|
|
};
|
|
|
|
|
|
static int cmp_ares_tasks(const uv_ares_task_t* a, const uv_ares_task_t* b) {
|
|
if (a->sock < b->sock) return -1;
|
|
if (a->sock > b->sock) return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
RB_GENERATE_STATIC(uv__ares_tasks, uv_ares_task_s, node, cmp_ares_tasks)
|
|
|
|
|
|
/* Add ares handle to list. */
|
|
static void uv_add_ares_handle(uv_loop_t* loop, uv_ares_task_t* handle) {
|
|
assert(loop == handle->loop);
|
|
RB_INSERT(uv__ares_tasks, &loop->ares_handles, handle);
|
|
}
|
|
|
|
|
|
/* Find matching ares handle in list. */
|
|
static uv_ares_task_t* uv_find_ares_handle(uv_loop_t* loop, ares_socket_t sock) {
|
|
uv_ares_task_t handle;
|
|
handle.sock = sock;
|
|
return RB_FIND(uv__ares_tasks, &loop->ares_handles, &handle);
|
|
}
|
|
|
|
|
|
/* Remove ares handle from list. */
|
|
static void uv_remove_ares_handle(uv_ares_task_t* handle) {
|
|
RB_REMOVE(uv__ares_tasks, &handle->loop->ares_handles, handle);
|
|
}
|
|
|
|
|
|
/* Returns 1 if the ares_handles list is empty, 0 otherwise. */
|
|
static int uv_ares_handles_empty(uv_loop_t* loop) {
|
|
return RB_EMPTY(&loop->ares_handles);
|
|
}
|
|
|
|
|
|
/* This is called once per second by loop->timer. It is used to constantly */
|
|
/* call back into c-ares for possibly processing timeouts. */
|
|
static void uv__ares_timeout(uv_timer_t* handle, int status) {
|
|
assert(!uv_ares_handles_empty(handle->loop));
|
|
ares_process_fd(handle->loop->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
|
|
}
|
|
|
|
|
|
static void uv__ares_poll_cb(uv_poll_t* watcher, int status, int events) {
|
|
uv_loop_t* loop = watcher->loop;
|
|
uv_ares_task_t* task = container_of(watcher, uv_ares_task_t, poll_watcher);
|
|
|
|
/* Reset the idle timer */
|
|
uv_timer_again(&loop->ares_timer);
|
|
|
|
if (status < 0) {
|
|
/* An error happened. Just pretend that the socket is both readable and */
|
|
/* writable. */
|
|
ares_process_fd(loop->channel, task->sock, task->sock);
|
|
return;
|
|
}
|
|
|
|
/* Process DNS responses */
|
|
ares_process_fd(loop->channel,
|
|
events & UV_READABLE ? task->sock : ARES_SOCKET_BAD,
|
|
events & UV_WRITABLE ? task->sock : ARES_SOCKET_BAD);
|
|
}
|
|
|
|
|
|
static void uv__ares_poll_close_cb(uv_handle_t* watcher) {
|
|
uv_ares_task_t* task = container_of(watcher, uv_ares_task_t, poll_watcher);
|
|
free(task);
|
|
}
|
|
|
|
|
|
/* Allocates and returns a new uv_ares_task_t */
|
|
static uv_ares_task_t* uv__ares_task_create(uv_loop_t* loop, ares_socket_t sock) {
|
|
uv_ares_task_t* task = (uv_ares_task_t*) malloc(sizeof *task);
|
|
|
|
if (task == NULL) {
|
|
/* Out of memory. */
|
|
return NULL;
|
|
}
|
|
|
|
task->loop = loop;
|
|
task->sock = sock;
|
|
|
|
if (uv_poll_init_socket(loop, &task->poll_watcher, sock) < 0) {
|
|
/* This should never happen. */
|
|
free(task);
|
|
return NULL;
|
|
}
|
|
|
|
return task;
|
|
}
|
|
|
|
|
|
/* Callback from ares when socket operation is started */
|
|
static void uv__ares_sockstate_cb(void* data, ares_socket_t sock,
|
|
int read, int write) {
|
|
uv_loop_t* loop = (uv_loop_t*) data;
|
|
uv_ares_task_t* task;
|
|
|
|
task = uv_find_ares_handle(loop, sock);
|
|
|
|
if (read || write) {
|
|
if (!task) {
|
|
/* New socket */
|
|
|
|
/* If this is the first socket then start the timer. */
|
|
if (!uv_is_active((uv_handle_t*) &loop->ares_timer)) {
|
|
assert(uv_ares_handles_empty(loop));
|
|
uv_timer_start(&loop->ares_timer, uv__ares_timeout, 1000, 1000);
|
|
}
|
|
|
|
task = uv__ares_task_create(loop, sock);
|
|
if (task == NULL) {
|
|
/* This should never happen unless we're out of memory or something */
|
|
/* is seriously wrong. The socket won't be polled, but the the query */
|
|
/* will eventually time out. */
|
|
return;
|
|
}
|
|
|
|
uv_add_ares_handle(loop, task);
|
|
}
|
|
|
|
/* This should never fail. If it fails anyway, the query will eventually */
|
|
/* time out. */
|
|
uv_poll_start(&task->poll_watcher,
|
|
(read ? UV_READABLE : 0) | (write ? UV_WRITABLE : 0),
|
|
uv__ares_poll_cb);
|
|
|
|
} else {
|
|
/* read == 0 and write == 0 this is c-ares's way of notifying us that */
|
|
/* the socket is now closed. We must free the data associated with */
|
|
/* socket. */
|
|
assert(task &&
|
|
"When an ares socket is closed we should have a handle for it");
|
|
|
|
uv_remove_ares_handle(task);
|
|
uv_close((uv_handle_t*) &task->poll_watcher, uv__ares_poll_close_cb);
|
|
|
|
if (uv_ares_handles_empty(loop)) {
|
|
uv_timer_stop(&loop->ares_timer);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* C-ares integration initialize and terminate */
|
|
int uv_ares_init_options(uv_loop_t* loop, ares_channel *channelptr,
|
|
struct ares_options *options, int optmask) {
|
|
int rc;
|
|
|
|
/* only allow single init at a time */
|
|
if (loop->channel != NULL) {
|
|
uv__set_artificial_error(loop, UV_EALREADY);
|
|
return -1;
|
|
}
|
|
|
|
/* set our callback as an option */
|
|
options->sock_state_cb = uv__ares_sockstate_cb;
|
|
options->sock_state_cb_data = loop;
|
|
optmask |= ARES_OPT_SOCK_STATE_CB;
|
|
|
|
/* We do the call to ares_init_option for caller. */
|
|
rc = ares_init_options(channelptr, options, optmask);
|
|
|
|
/* if success, save channel */
|
|
if (rc == ARES_SUCCESS) {
|
|
loop->channel = *channelptr;
|
|
}
|
|
|
|
/* Initialize the timeout timer. The timer won't be started until the */
|
|
/* first socket is opened. */
|
|
uv_timer_init(loop, &loop->ares_timer);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) {
|
|
/* Only allow destroy if did init. */
|
|
if (loop->channel) {
|
|
uv_timer_stop(&loop->ares_timer);
|
|
ares_destroy(channel);
|
|
loop->channel = NULL;
|
|
}
|
|
}
|
|
|