1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
/****************************************************************************
* Copyright (C) 1998 WIDE Project. All rights reserved.
* Copyright (C) 1999,2000,2001,2002 University of Tromso. All rights reserved.
* Copyright (C) 2002 Invenia Innovation AS. All rights reserved.
*
* Author: Feike W. Dillema, feico@pasta.cs.uit.no.
* based on newbie code by Yusuke DOI, Keio Univ. Murai Lab.
****************************************************************************/
/*
* <$Id: udp_response.c,v 3.57 2002/03/04 12:34:11 dillema Exp $>
*/
#include "totd.h"
/*
* When a new query is received over a UDP socket from a client, the
* udp_response state machine is started. See also ev_udp_in_read(),
* which is the only place from where udp_response_start() is called.
*
* udp_response_start() starts a new transaction. First creates a new
* root context for it, and starts the forwarding of the original query.
*
* forwarding is performed by starting a new child request, which
* if all goes well result in some resource records in our context.
*
* when the forwarded request finished successfully, udp_response_finish()
* is responsible for interpreting it and sending a response back to the client.
*
*/
int udp_response_start (u_char *mesg_buf, int mesg_len, struct sockaddr *sa_p,
Nia *inif) {
const char *fn = "udp_response_start()";
Context *cont;
syslog (LOG_DEBUG, "%s: start", fn);
/* create context */
cont = context_create();
if (!cont)
return (response_abort (cont, -1));
cont->mesg.p = mesg_buf;
cont->mesg_len = mesg_len;
cont->wp = mesg_buf + mesg_len; /* just after the answer section */
cont->q_id = cont->mesg.hdr->id;
cont->netifaddr = nia_copy (inif);
memcpy (cont->peer, sa_p, SOCKADDR_SIZEOF(*sa_p));
if (cont->mesg.hdr->opcode == OP_QUERY) {
syslog (LOG_DEBUG, "%s: OPCODE = OP_QUERY", fn);
/* query-response specific variables */
cont->process = udp_response_recursive_process;
cont->retry = udp_response_recursive_retry;
/* do the forwarding, send request to forwarder */
switch (request_start (cont, QUERY_TCP)) {
case 0:
/* We got a response */
return (0);
case 1:
/* Something wrong with the query */
cont->mesg.hdr->rcode = RC_FMTERR;
return (udp_response_finish (cont));
default:
/* We failed ourselves somehow */
cont->mesg.hdr->rcode = RC_SERVERERR;
return (udp_response_finish (cont));
}
} else {
syslog (LOG_NOTICE, "%s: OPCODE unknown(%d)", fn,
cont->mesg.hdr->opcode);
cont->mesg.hdr->rcode = RC_NIMP;
return udp_response_finish (cont);
}
/* NOTREACHED */
return 0;
}
int udp_response_recursive_process (Context *cont) {
switch (recursive_process (cont)) {
case 0:
/* continue, not ready yet */
return 0;
case 1:
/* finished with success */
return udp_response_finish (cont);
default:
/* we failed ourselves */
cont->mesg.hdr->rcode = RC_SERVERERR;
return udp_response_finish (cont);
}
}
int udp_response_recursive_retry (Context *cont) {
syslog (LOG_ERR, "udp_response_recursive_retry should not be called.");
return response_abort (cont, 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;
}
|