/** Callback for uping listener socket. * Reads a uping from the socket and respond, but not more than 10 * times per second. * @param[in] ev I/O event for uping socket. */ static void uping_echo_callback(struct Event* ev) { struct Socket *sock; struct irc_sockaddr from; unsigned int len = 0; static time_t last = 0; static int counter = 0; char buf[BUFSIZE + 1]; assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR); sock = ev_socket(ev); assert(sock == &upingSock_v4 || sock == &upingSock_v6); Debug((DEBUG_DEBUG, "UPING: uping_echo")); if (IO_SUCCESS != os_recvfrom_nonb(s_fd(sock), buf, BUFSIZE, &len, &from)) return; /* * count em even if we're getting flooded so we can tell we're getting * flooded. */ ++ServerStats->uping_recv; if (len < 19) return; else if (CurrentTime != last) { counter = 0; last = CurrentTime; } else if (++counter > 10) return; os_sendto_nonb(s_fd(sock), buf, len, NULL, 0, &from); }
/** Reads a uping from the socket and respond, but not more than 10 * times per second. */ void uping_echo() { struct sockaddr_in from = { 0 }; unsigned int len = 0; static time_t last = 0; static int counter = 0; char buf[BUFSIZE + 1]; Debug((DEBUG_DEBUG, "UPING: uping_echo")); if (IO_SUCCESS != os_recvfrom_nonb(UPingFileDescriptor, buf, BUFSIZE, &len, &from)) return; /* * count em even if we're getting flooded so we can tell we're getting * flooded. */ ++ServerStats->uping_recv; if (CurrentTime == last) { if (++counter > 10) return; } else { counter = 0; last = CurrentTime; } if (len < 19) return; sendto(UPingFileDescriptor, buf, len, 0, (struct sockaddr*) &from, sizeof(from)); }
/** Read the response from an outbound uping. * @param[in] pptr UPing to check. */ void uping_read(struct UPing* pptr) { struct irc_sockaddr sin; struct timeval tv; unsigned int len; unsigned int pingtime; char* s; char buf[BUFSIZE + 1]; IOResult ior; assert(0 != pptr); gettimeofday(&tv, NULL); ior = os_recvfrom_nonb(pptr->fd, buf, BUFSIZE, &len, &sin); if (IO_BLOCKED == ior) return; else if (IO_FAILURE == ior) { const char* msg = strerror(errno); if (!msg) msg = "Unknown error"; sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: receive error: " "%s", pptr->client, msg); uping_end(pptr); return; } if (len < 19) return; /* Broken packet */ ++pptr->received; buf[len] = 0; pingtime = (tv.tv_sec - atol(&buf[1])) * 1000 + (tv.tv_usec - atol(buf + strlen(buf) + 1)) / 1000; pptr->ms_ave += pingtime; if (!pptr->ms_min || pptr->ms_min > pingtime) pptr->ms_min = pingtime; if (pingtime > pptr->ms_max) pptr->ms_max = pingtime; timer_chg(&pptr->killer, TT_RELATIVE, UPINGTIMEOUT); s = pptr->buf + strlen(pptr->buf); sprintf(s, " %u", pingtime); if (pptr->received == pptr->count) uping_end(pptr); return; }
/** Read a DNS reply from the nameserver and process it. * @param[in] ev I/O activity event for resolver socket. */ static void res_readreply(struct Event *ev) { struct irc_sockaddr lsin; struct Socket *sock; char buf[sizeof(HEADER) + MAXPACKET]; HEADER *header; struct reslist *request = NULL; unsigned int rc; int answer_count; assert((ev_socket(ev) == &res_socket_v4) || (ev_socket(ev) == &res_socket_v6)); sock = ev_socket(ev); if (IO_SUCCESS != os_recvfrom_nonb(s_fd(sock), buf, sizeof(buf), &rc, &lsin) || (rc <= sizeof(HEADER))) return; /* * check against possibly fake replies */ if (!res_ourserver(&lsin)) return; /* * convert DNS reply reader from Network byte order to CPU byte order. */ header = (HEADER *)buf; header->ancount = ntohs(header->ancount); header->qdcount = ntohs(header->qdcount); header->nscount = ntohs(header->nscount); header->arcount = ntohs(header->arcount); /* * response for an id which we have already received an answer for * just ignore this response. */ if (0 == (request = find_id(header->id))) return; if ((header->rcode != NO_ERRORS) || (header->ancount == 0)) { if (SERVFAIL == header->rcode || NXDOMAIN == header->rcode) { /* * If a bad error was returned, we stop here and don't send * send any more (no retries granted). */ Debug((DEBUG_DNS, "Request %p has bad response (state %d type %d rcode %d)", request, request->state, request->type, header->rcode)); (*request->callback)(request->callback_ctx, NULL, NULL); rem_request(request); } else { /* * If we haven't already tried this, and we're looking up AAAA, try A * now */ if (request->state == REQ_AAAA && request->type == T_AAAA) { request->timeout += feature_int(FEAT_IRCD_RES_TIMEOUT); resend_query(request); } else if (request->type == T_PTR && request->state != REQ_INT && !irc_in_addr_is_ipv4(&request->addr)) { request->state = REQ_INT; request->timeout += feature_int(FEAT_IRCD_RES_TIMEOUT); resend_query(request); } } return; } /* * If this fails there was an error decoding the received packet, * try it again and hope it works the next time. */ answer_count = proc_answer(request, header, buf, buf + rc); if (answer_count) { if (request->type == T_PTR) { if (request->name == NULL) { /* * got a PTR response with no name, something bogus is happening * don't bother trying again, the client address doesn't resolve */ Debug((DEBUG_DNS, "Request %p PTR had empty name", request)); (*request->callback)(request->callback_ctx, NULL, NULL); rem_request(request); return; } /* * Lookup the 'authoritative' name that we were given for the * ip#. */ #ifdef IPV6 if (!irc_in_addr_is_ipv4(&request->addr)) do_query_name(request->callback, request->callback_ctx, request->name, NULL, T_AAAA); else #endif do_query_name(request->callback, request->callback_ctx, request->name, NULL, T_A); Debug((DEBUG_DNS, "Request %p switching to forward resolution", request)); rem_request(request); } else { /* * got a name and address response, client resolved */ (*request->callback)(request->callback_ctx, &request->addr, request->name); Debug((DEBUG_DNS, "Request %p got forward resolution", request)); rem_request(request); } } else if (!request->sent) { /* XXX - we got a response for a query we didn't send with a valid id? * this should never happen, bail here and leave the client unresolved */ assert(0); /* XXX don't leak it */ Debug((DEBUG_DNS, "Request %p was unexpected(!)", request)); rem_request(request); } }