int udp_request_start (struct context *cont) { char *fn = "udp_request_start()"; struct sockaddr *sa; int timeout; syslog (LOG_DEBUG, "%s: start", fn); cont->process = udp_request_process; cont->retry = udp_request_retry; if (cont->mesg_len > MAX_PACKET) { syslog (LOG_NOTICE, "Query to big for UDP datagram."); return (request_abort (cont, 1)); /* try TCP instead? */ } sa = (struct sockaddr *) cont->current_ns->list_data; if (net_mesg_send (NULL, cont->mesg.p, cont->mesg_len, sa) < 0) { syslog (LOG_NOTICE, "send failed: %m"); /* force immediate retry */ timeout = 0; syslog (LOG_DEBUG, "%s: force retry at zero timeout", fn); } else { /* put me to input list */ if (ev_udp_in_register (cont, sa, SOCKADDR_SIZEOF (*sa), cont->q_id) < 0) return (request_abort (cont, -1)); timeout = cont->timeout; syslog (LOG_DEBUG, "Query times out in %d seconds", timeout); } /* put me on timeout event queue */ if (context_timeout_register (cont, timeout) < 0) return (request_abort (cont, -1)); syslog (LOG_DEBUG, "%s: end", fn); /* SUCCESS */ return 0; }
int udp_request_retry (Context *cont) { char *fn = "udp_request_retry()"; struct sockaddr *sa; int len, timeout; syslog (LOG_DEBUG, "%s: start", fn); /* remove old I/O List (if it is still there) */ sa = (struct sockaddr *) cont->current_ns->list_data; ev_udp_in_remove (sa, cont->q_id); /* * Fast GiveUp Hack. * * decisions like this should only be made in request.c, * so this hack is ugly too. */ if (cont->q_type == RT_AAAA || cont->q_type == RT_A6) { /* * Some nameservers just do not like IPv6 * address records. We guess that is the case * here and tell the parent to continue if it * can. * * We perform the code of request_finish() * here, but without the parsing of the * response (we have none). * * Note that this means that a timeout for a IPv6 * address record request never causes a shift to * the next forwarder! Seems reasonable in the * for the majority of cases. */ syslog (LOG_DEBUG, "Giving up quickly on IPv6 address record"); /* re-initialize answer, nameserver, additional record lists */ list_destroy (cont->an_list, rrset_freev); list_destroy (cont->ns_list, rrset_freev); list_destroy (cont->ar_list, rrset_freev); cont->an_list = list_init (); cont->ns_list = list_init (); cont->ar_list = list_init (); if (!cont->an_list || !cont->ns_list || !cont->ar_list) return request_abort (cont, -1); if (cont->parent) { syslog (LOG_DEBUG, "%s: process parent context", fn); cont->parent->process (cont->parent); } /* ... so we cleanup ourselves in any case */ context_destroy (cont); syslog (LOG_DEBUG, "%s: return success", fn); return 0; /* SUCCESS */ } if (request_retry (cont) < 0) return (request_abort (cont, -2)); /* send/forward the message/request again */ sa = (struct sockaddr *) cont->current_ns->list_data; len = net_mesg_send (NULL, cont->mesg.p, cont->mesg_len, sa); if (len < cont->mesg_len) { if (len < 0) syslog (LOG_NOTICE, "retry failed(default socket): %m"); else syslog (LOG_NOTICE, "can't send whole datagram"); /* forcing immediate retry */ timeout = 0; } else { /* we retried the request, now wait for response or timeout */ timeout = cont->timeout; /* put me to input list */ if (ev_udp_in_register (cont, sa, SOCKADDR_SIZEOF (*sa), cont->q_id) < 0) return (request_abort (cont, -1)); } /* put me to timeout list */ if (context_timeout_register (cont, timeout) < 0) return (request_abort (cont, -1)); /* no state change... */ syslog (LOG_DEBUG, "%s: end", fn); return 0; }
int udp_response_finish (Context *cont) { const char *fn = "udp_response_finish()"; int len; syslog (LOG_DEBUG, "%s: start", fn); /* * We trim the response down if it doesn't fit in a default UDP * data in a somewhat crude way. Could be smarter, like trim * A records from response for pure IPv6 queries and so. See, if * it is needed often. Maybe better to support EDNS0 option instead. * * RFC1035 says we should truncate the message to 512 bytes and set the * tr bit in the header, but it is really vague on when a response does * not fit. RFC2181 clarifies the use of the tr bit in section 9. * * Note that similar logic is still present in mesg_assemble() in * ne_mesg.c (may disappear if we add the logic to assemble_response() * instead). */ assemble_response (cont); if (cont->mesg_len < 0 || cont->mesg_len > MAX_PACKET) { /* put the message on a diet */ list_destroy (cont->ar_list, rrset_freev); cont->ar_list = NULL; syslog (LOG_DEBUG, "Overweight, dropping additional section"); assemble_response (cont); } if (cont->mesg_len < 0 || cont->mesg_len > MAX_PACKET) { /* * ok, water adn bread then then. shuold check * RFCs, it may be better to return error msg * instead of slimmed answer. */ list_destroy (cont->ns_list, rrset_freev); cont->ns_list = NULL; syslog (LOG_DEBUG, "Overweight, dropping authority section"); assemble_response (cont); } if (cont->mesg_len < 0 || cont->mesg_len > MAX_PACKET) { /* ok, nothing viable left, so die */ list_destroy (cont->an_list, rrset_freev); cont->an_list = NULL; assemble_response (cont); cont->mesg.hdr->tc = 1; syslog (LOG_WARNING, "Obese, answers too big for UDP"); } if (cont->mesg_len < 0 || cont->mesg_len > MAX_PACKET) { syslog (LOG_ERR, "Even error msg is too big for UDP"); return (response_abort (cont, 1)); } /* send the answer */ len = net_mesg_send (cont->netifaddr, cont->mesg.p, cont->mesg_len, cont->peer); if (len < cont->mesg_len) { syslog (LOG_NOTICE, "failed to send message."); return (response_abort (cont, -1)); } context_destroy (cont); return 0; }