File: udp_response.c

package info (click to toggle)
totd 1.4-4.3
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 712 kB
  • ctags: 512
  • sloc: ansic: 5,840; sh: 2,929; perl: 116; makefile: 113
file content (165 lines) | stat: -rw-r--r-- 5,272 bytes parent folder | download | duplicates (4)
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;
}