async_resolv.c revision 3d334807f302603b81996b41f2a365ce75f36d17
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher Async resolver
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher Martin Nagy <mnagy@redhat.com>
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher Jakub Hrozek <jhrozek@redhat.com>
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher Copyright (C) Red Hat, Inc 2009
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher This program is free software; you can redistribute it and/or modify
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher it under the terms of the GNU General Public License as published by
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher the Free Software Foundation; either version 3 of the License, or
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher (at your option) any later version.
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher This program is distributed in the hope that it will be useful,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher but WITHOUT ANY WARRANTY; without even the implied warranty of
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher GNU General Public License for more details.
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher You should have received a copy of the GNU General Public License
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher along with this program. If not, see <http://www.gnu.org/licenses/>.
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher#define ares_parse_srv_reply(abuf, alen, srv_out) \
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher _ares_parse_srv_reply(abuf, alen, srv_out)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher#define ares_parse_txt_reply(abuf, alen, txt_out) \
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher _ares_parse_txt_reply(abuf, alen, txt_out)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher#endif /* HAVE_ARES_DATA */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher#define DNS__16BIT(p) (((p)[0] << 8) | (p)[1])
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher#define DNS_HEADER_ANCOUNT(h) DNS__16BIT((h) + 6)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* Contexts are linked so we can keep track of them and re-create
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher * the ares channels in all of them at once if we need to. */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* List of file descriptors that are watched by tevent. */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* Time in milliseconds before canceling a DNS request */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* The timeout watcher periodically calls ares_process_fd() to check
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher * if our pending requests didn't timeout. */
346f41f1ede975cb2db0af570f5b454b9b306704Stephen Gallagherfd_input_available(struct tevent_context *ev, struct tevent_fd *fde,
346f41f1ede975cb2db0af570f5b454b9b306704Stephen Gallagher struct fd_watch *watch = talloc_get_type(data, struct fd_watch);
346f41f1ede975cb2db0af570f5b454b9b306704Stephen Gallagher DEBUG(1, ("Invalid ares channel - this is likely a bug\n"));
346f41f1ede975cb2db0af570f5b454b9b306704Stephen Gallagher ares_process_fd(watch->ctx->channel, watch->fd, ARES_SOCKET_BAD);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ares_process_fd(watch->ctx->channel, ARES_SOCKET_BAD, watch->fd);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallaghercheck_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct timeval current_time, void *private_data);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagheradd_timeout_timer(struct tevent_context *ev, struct resolv_ctx *ctx)
4a1e58d85409fbb7a12ac244c3dbef8c0c1b15dfMichal Zidek /* Enforce a minimum of 1 second. */
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny tv = tevent_timeval_current_ofs(tvp->tv_sec, tvp->tv_usec);
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny ctx->timeout_watcher = tevent_add_timer(ev, ctx, tv, check_fd_timeouts,
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zelenycheck_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te,
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny struct resolv_ctx *ctx = talloc_get_type(private_data, struct resolv_ctx);
4a1e58d85409fbb7a12ac244c3dbef8c0c1b15dfMichal Zidek /* NULLify the timeout_watcher so we don't
4a1e58d85409fbb7a12ac244c3dbef8c0c1b15dfMichal Zidek * free it in the _done() function if it
4a1e58d85409fbb7a12ac244c3dbef8c0c1b15dfMichal Zidek * gets called. Now that we're already in
4a1e58d85409fbb7a12ac244c3dbef8c0c1b15dfMichal Zidek * the handler, tevent will take care of
4a1e58d85409fbb7a12ac244c3dbef8c0c1b15dfMichal Zidek * freeing it when it returns.
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny ares_process_fd(ctx->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zelenyschedule_timeout_watcher(struct tevent_context *ev, struct resolv_ctx *ctx)
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zelenyunschedule_timeout_watcher(struct resolv_ctx *ctx)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher DEBUG(9, ("Unscheduling DNS timeout watcher\n"));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void fd_event_add(struct resolv_ctx *ctx, int s, int flags);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void fd_event_close(struct resolv_ctx *ctx, int s);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher * When ares is ready to read or write to a file descriptor, it will
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher * call this callback. If both read and write are 0, it means that ares
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher * will soon close the socket. We are mainly using this function to register
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher * new file descriptors with tevent.
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherfd_event(void *data, int s, int fd_read, int fd_write)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct resolv_ctx *ctx = talloc_get_type(data, struct resolv_ctx);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* The socket is about to get closed. */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* Are we already watching this file descriptor? */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherfd_event_add(struct resolv_ctx *ctx, int s, int flags)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* The file descriptor is new, register it with tevent. */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher DEBUG(1, ("Out of memory allocating fd_watch structure\n"));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher talloc_set_destructor(watch, fd_watch_destructor);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher watch->fde = tevent_add_fd(ctx->ev_ctx, watch, s, flags,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* Remove the socket from list */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherresolv_ctx_destructor(struct resolv_ctx *ctx)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher DEBUG(1, ("Ares channel already destroyed?\n"));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* Set ctx->channel to NULL first, so that callbacks that get
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher * ARES_EDESTRUCTION won't retry. */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherrecreate_ares_channel(struct resolv_ctx *ctx)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher DEBUG(4, ("Initializing new c-ares channel\n"));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* FIXME: the options would contain
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher * the nameservers to contact, the domains
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher * to search... => get from confdb
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret = ares_init_options(&new_channel, &options,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher DEBUG(1, ("Failed to initialize ares channel: %s\n",
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher DEBUG(4, ("Destroying the old c-ares channel\n"));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherresolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ctx = talloc_zero(mem_ctx, struct resolv_ctx);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher talloc_set_destructor(ctx, resolv_ctx_destructor);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher DEBUG(4, ("Recreating all c-ares channels\n"));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherresolv_copy_hostent(TALLOC_CTX *mem_ctx, struct hostent *src)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret = talloc_zero(mem_ctx, struct hostent);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->h_name = talloc_strdup(ret, src->h_name);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (len = 0; src->h_aliases[len] != NULL; len++);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->h_aliases = talloc_size(ret, sizeof(char *) * (len + 1));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (i = 0; i < len; i++) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->h_aliases[i] = talloc_strdup(ret->h_aliases, src->h_aliases[i]);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (len = 0; src->h_addr_list[len] != NULL; len++);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->h_addr_list = talloc_size(ret, sizeof(char *) * (len + 1));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (i = 0; i < len; i++) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->h_addr_list[i] = talloc_memdup(ret->h_addr_list,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherresolv_copy_in_addr(TALLOC_CTX *mem_ctx, struct resolv_addr *ret,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->ipaddr = talloc_array(mem_ctx, uint8_t, sizeof(struct in_addr));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher memcpy(ret->ipaddr, &attl->ipaddr, sizeof(struct in_addr));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherresolv_copy_in6_addr(TALLOC_CTX *mem_ctx, struct resolv_addr *ret,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->ipaddr = talloc_array(mem_ctx, uint8_t, sizeof(struct in6_addr));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher memcpy(ret->ipaddr, &a6ttl->ip6addr, sizeof(struct in6_addr));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherresolv_copy_hostent_common(TALLOC_CTX *mem_ctx, struct hostent *src)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret = talloc_zero(mem_ctx, struct resolv_hostent);
4e2d9fe30bf8b692972a9654c60d2d90ed355815Stephen Gallagher ret->name = talloc_strdup(ret, src->h_name);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (len = 0; src->h_aliases[len] != NULL; len++);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->aliases = talloc_array(ret, char *, len + 1);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (i = 0; i < len; i++) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->aliases[i] = talloc_strdup(ret->aliases, src->h_aliases[i]);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherresolv_copy_hostent2(TALLOC_CTX *mem_ctx, struct hostent *src)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret = resolv_copy_hostent_common(mem_ctx, src);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (len = 0; src->h_addr_list[len] != NULL; len++);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->addr_list = talloc_array(ret, struct resolv_addr *, len + 1);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (i = 0; i < len; i++) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->addr_list[i] = talloc_zero(ret->addr_list,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->addr_list[i]->ipaddr = talloc_memdup(ret->addr_list[i],
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->addr_list[i]->ttl = RESOLV_DEFAULT_TTL;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherresolv_copy_hostent_ares(TALLOC_CTX *mem_ctx, struct hostent *src,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret = resolv_copy_hostent_common(mem_ctx, src);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->addr_list = talloc_array(ret, struct resolv_addr *,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (i = 0; i < num_ares_ttl_data; i++) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret->addr_list[i] = talloc_zero(ret->addr_list,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher cret = resolv_copy_in_addr(ret->addr_list, ret->addr_list[i],
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher &((struct ares_addrttl *) ares_ttl_data)[i]);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher cret = resolv_copy_in6_addr(ret->addr_list, ret->addr_list[i],
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher &((struct ares_addr6ttl *) ares_ttl_data)[i]);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher DEBUG(1, ("Unknown address family %d\n"));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher/* =================== Resolve host name in files =========================*/
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* Part of the query. */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* query result */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* returned by ares. */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher/* Fake up an async interface even though files would
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher * always be blocking */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherresolv_gethostbyname_files_send(TALLOC_CTX *mem_ctx,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher DEBUG(4, ("Trying to resolve %s record of '%s' in files\n",
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher state->family == AF_INET ? "A" : "AAAA", state->name));
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher state->status = ares_gethostbyname_file(state->resolv_ctx->channel,
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher state->rhostent = resolv_copy_hostent2(state, hostent);
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher } else if (state->status == ARES_ENOTFOUND ||
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher /* Just say we didn't find anything and let the caller decide
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher * about retrying */
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher tevent_req_error(req, return_code(state->status));
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagherresolv_gethostbyname_files_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher int *status, struct resolv_hostent **rhostent)
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher struct gethostbyname_files_state *state = tevent_req_data(req,
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher /* Fill in even in case of error as status contains the
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher * c-ares return code */
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher *rhostent = talloc_steal(mem_ctx, state->rhostent);
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher/* ==================== Resolve host name in DNS =========================*/
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher /* Part of the query. */
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher /* query result */
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher /* These are returned by ares. */
int timeouts;
int retrying;
static struct tevent_req *
int family)
return NULL;
return NULL;
return NULL;
return req;
struct tevent_req);
struct gethostbyname_dns_state);
struct gethostbyname_dns_state);
/* If resolv.conf changed during processing of a request we might
int naddrttls;
void *addr;
case AF_INET:
if (!addr) {
goto fail;
&naddrttls);
case AF_INET6:
if (!addr) {
goto fail;
&naddrttls);
goto fail;
goto fail;
fail:
return ret;
struct gethostbyname_dns_state);
if (status) {
if (timeouts) {
if (rhostent) {
return EOK;
struct gethostbyname_state {
const char *name;
int family;
int status;
int timeouts;
int retrying;
struct tevent_req *
return NULL;
return NULL;
return NULL;
return req;
req);
if (status) {
if (timeouts) {
if (hostent) {
return EOK;
char *address;
return NULL;
errno = 0;
return NULL;
return address;
struct tevent_req);
struct gethostbyname_state);
if (!old_list) {
return EOK;
while (old_list) {
if (!new_list) {
return ENOMEM;
return ENOMEM;
return ENOMEM;
return EOK;
struct getsrv_state {
/* the SRV query - for example _ldap._tcp.example.com */
const char *query;
int status;
int timeouts;
int retrying;
struct tevent_req *
return NULL;
return NULL;
return NULL;
return req;
int ret;
goto fail;
goto fail;
goto fail;
fail:
if (status)
if (timeouts)
if (reply_list)
return EOK;
struct tevent_req);
struct getsrv_state);
#ifdef BUILD_TXT
if (!old_list) {
return EOK;
while (old_list) {
if (!new_list) {
return ENOMEM;
return ENOMEM;
return ENOMEM;
return EOK;
struct gettxt_state {
const char *query;
int status;
int timeouts;
int retrying;
struct tevent_req *
return NULL;
return NULL;
return NULL;
return req;
int ret;
goto fail;
goto fail;
goto fail;
fail:
if (status)
if (timeouts)
if (reply_list)
return EOK;
struct tevent_req);
struct gettxt_state);
if (!list) {
return NULL;
return single_step;
struct ares_srv_reply *l, *r;
if (!left)
return right;
if (!right)
return left;
r = right;
l = left;
res = l;
l = l->next;
res = r;
r = r->next;
return res_start;
return list;
return list;
int len,
int *totals;
return EOK;
if (!totals) {
return ENOMEM;
r = *(start);
while (r != NULL) {
if (r->weight == 0) {
if (prev) {
tmp = r;
r = r->next;
prev = r;
r = r->next;
total = 0;
prev = r;
return EIO;
if (prev) {
if (!new_start) {
new_start = r;
new_end = r;
new_end = r;
return EOK;
int ret;
int len;
return EIO;
while (pri_start) {
len++;
if (ret) {
return ret;
if (prev_end) {
return EOK;