static void _getdns_cancel_reply(getdns_context *context, connection *conn) { struct mem_funcs *mf; if (!conn) return; if (context && context->server && _getdns_rbtree_search(&context->server->connections_set, conn) != &conn->super) return; if (conn->l->transport == GETDNS_TRANSPORT_TCP) { tcp_connection *tcp_conn = (tcp_connection *)conn; if (tcp_conn->to_answer > 0 && --tcp_conn->to_answer == 0 && tcp_conn->fd == -1) tcp_connection_destroy(tcp_conn); } else if (conn->l->transport == GETDNS_TRANSPORT_UDP && (mf = &conn->l->set->context->mf)) { listen_set *set = conn->l->set; /* Unlink this connection */ (void) _getdns_rbtree_delete( &set->connections_set, conn); DEBUG_SERVER("[connection del] count: %d\n", (int)set->connections_set.count); if ((*conn->prev_next = conn->next)) conn->next->prev_next = conn->prev_next; GETDNS_FREE(*mf, conn); free_listen_set_when_done(set); } }
static void tcp_connection_destroy(tcp_connection *conn) { struct mem_funcs *mf; getdns_eventloop *loop; tcp_to_write *cur, *next; mf = &conn->super.l->set->context->mf; if (getdns_context_get_eventloop(conn->super.l->set->context, &loop)) return; if (conn->event.ev) loop->vmt->clear(loop, &conn->event); if (conn->event.read_cb||conn->event.write_cb||conn->event.timeout_cb) { conn->event.read_cb = conn->event.write_cb = conn->event.timeout_cb = NULL; } if (conn->fd >= 0) { (void) _getdns_closesocket(conn->fd); conn->fd = -1; } if (conn->read_buf) { GETDNS_FREE(*mf, conn->read_buf); conn->read_buf = conn->read_pos = NULL; conn->to_read = 0; } if ((cur = conn->to_write)) { while (cur) { next = cur->next; GETDNS_FREE(*mf, cur); cur = next; } conn->to_write = NULL; } if (conn->to_answer > 0) return; /* Unlink this connection */ (void) _getdns_rbtree_delete( &conn->super.l->set->connections_set, conn); DEBUG_SERVER("[connection del] count: %d\n", (int)conn->super.l->set->connections_set.count); if ((*conn->super.prev_next = conn->super.next)) conn->super.next->prev_next = conn->super.prev_next; free_listen_set_when_done(conn->super.l->set); GETDNS_FREE(*mf, conn); }
/** call timeouts handlers, and return how long to wait for next one or -1 */ void _getdns_handle_timeouts(struct _getdns_event_base* base, struct timeval* now, struct timeval* wait) { struct _getdns_event* p; #ifndef S_SPLINT_S wait->tv_sec = (time_t)-1; #endif //verbose(VERB_CLIENT, "winsock_event handle_timeouts"); while((_getdns_rbnode_t*)(p = (struct _getdns_event*)_getdns_rbtree_first(base->times)) !=RBTREE_NULL) { #ifndef S_SPLINT_S if(p->ev_timeout.tv_sec > now->tv_sec || (p->ev_timeout.tv_sec==now->tv_sec && p->ev_timeout.tv_usec > now->tv_usec)) { /* there is a next larger timeout. wait for it */ wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec; if(now->tv_usec > p->ev_timeout.tv_usec) { wait->tv_sec--; wait->tv_usec = 1000000 - (now->tv_usec - p->ev_timeout.tv_usec); } else { wait->tv_usec = p->ev_timeout.tv_usec - now->tv_usec; } //verbose(VERB_CLIENT, "winsock_event wait=" ARG_LL "d.%6.6d", // (long long)wait->tv_sec, (int)wait->tv_usec); return; } #endif /* event times out, remove it */ (void)_getdns_rbtree_delete(base->times, p); p->ev_events &= ~EV_TIMEOUT; fptr_ok(fptr_whitelist_event(p->ev_callback)); (*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg); } //verbose(VERB_CLIENT, "winsock_event wait=(-1)"); }
int _getdns_event_del(struct _getdns_event *ev) { //verbose(VERB_ALGO, "event_del %p added=%d fd=%d tv=" ARG_LL "d %s%s%s", // ev, ev->added, ev->ev_fd, // (ev->ev_events&EV_TIMEOUT)?(long long)ev->ev_timeout.tv_sec*1000+ // (long long)ev->ev_timeout.tv_usec/1000:-1, // (ev->ev_events&EV_READ)?" EV_READ":"", // (ev->ev_events&EV_WRITE)?" EV_WRITE":"", // (ev->ev_events&EV_TIMEOUT)?" EV_TIMEOUT":""); if(!ev->added) return 0; //log_assert(ev->added); if((ev->ev_events&EV_TIMEOUT)) (void)_getdns_rbtree_delete(ev->ev_base->times, &ev->node); if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) { //log_assert(ev->ev_base->max > 0); /* remove item and compact the list */ ev->ev_base->items[ev->idx] = ev->ev_base->items[ev->ev_base->max-1]; ev->ev_base->items[ev->ev_base->max-1] = NULL; ev->ev_base->max--; if(ev->idx < ev->ev_base->max) ev->ev_base->items[ev->idx]->idx = ev->idx; zero_waitfor(ev->ev_base->waitfor, ev->hEvent); if(WSAEventSelect(ev->ev_fd, ev->hEvent, 0) != 0) log_err("getdns: WSAEventSelect(disable) failed: %s", wsa_strerror(WSAGetLastError())); if(!WSACloseEvent(ev->hEvent)) log_err("getdns: WSACloseEvent failed: %s", wsa_strerror(WSAGetLastError())); } ev->just_checked = 0; ev->added = 0; return 0; }
getdns_return_t getdns_reply(getdns_context *context, const getdns_dict *reply, getdns_transaction_t request_id) { /* TODO: Check request_id at context->outbound_requests */ connection *conn = (connection *)(intptr_t)request_id; struct mem_funcs *mf; getdns_eventloop *loop; uint8_t buf[65536]; size_t len; getdns_return_t r; if (!conn) return GETDNS_RETURN_INVALID_PARAMETER; if (!context || !context->server) { if (!context) context = conn->l->set->context; } else if (_getdns_rbtree_search(&context->server->connections_set, conn) != &conn->super) return GETDNS_RETURN_NO_SUCH_LIST_ITEM; if (!reply) { _getdns_cancel_reply(context, conn); return GETDNS_RETURN_GOOD; } if (!(mf = &conn->l->set->context->mf)) return GETDNS_RETURN_GENERIC_ERROR;; if ((r = getdns_context_get_eventloop(conn->l->set->context, &loop))) return r; len = sizeof(buf); if ((r = getdns_msg_dict2wire_buf(reply, buf, &len))) return r; else if (conn->l->transport == GETDNS_TRANSPORT_UDP) { listener *l = conn->l; if (conn->l->fd >= 0 && sendto(conn->l->fd, (void *)buf, len, 0, (struct sockaddr *)&conn->remote_in, conn->addrlen) == -1) { /* TODO: handle _getdns_socketerror_wants_retry() */ /* IO error, never cleanup a listener because of I/O error */ DEBUG_SERVER("I/O error from sendto(): %s\n", _getdns_errnostr()); } /* Unlink this connection */ (void) _getdns_rbtree_delete( &l->set->connections_set, conn); DEBUG_SERVER("[connection del] count: %d\n", (int)l->set->connections_set.count); if ((*conn->prev_next = conn->next)) conn->next->prev_next = conn->prev_next; GETDNS_FREE(*mf, conn); if (l->fd < 0) free_listen_set_when_done(l->set); } else if (conn->l->transport == GETDNS_TRANSPORT_TCP) { tcp_connection *conn = (tcp_connection *)(intptr_t)request_id; tcp_to_write **to_write_p; tcp_to_write *to_write; if (conn->fd == -1) { if (conn->to_answer > 0) --conn->to_answer; tcp_connection_destroy(conn); return GETDNS_RETURN_GOOD; } if (!(to_write = (tcp_to_write *)GETDNS_XMALLOC( *mf, uint8_t, sizeof(tcp_to_write) + len + 2))) { tcp_connection_destroy(conn); return GETDNS_RETURN_MEMORY_ERROR; } to_write->write_buf_len = len + 2; to_write->write_buf[0] = (len >> 8) & 0xFF; to_write->write_buf[1] = len & 0xFF; to_write->written = 0; to_write->next = NULL; (void) memcpy(to_write->write_buf + 2, buf, len); /* Append to_write to conn->to_write list */ for ( to_write_p = &conn->to_write ; *to_write_p ; to_write_p = &(*to_write_p)->next) ; /* pass */ *to_write_p = to_write; if (conn->to_answer > 0) conn->to_answer--; /* When event is scheduled, and doesn't have tcp_write_cb: * reschedule. */ if (conn->event.write_cb == NULL) { if (conn->event.ev) loop->vmt->clear(loop, &conn->event); conn->event.write_cb = tcp_write_cb; (void) loop->vmt->schedule(loop, conn->fd, DOWNSTREAM_IDLE_TIMEOUT, &conn->event); } }