File: udp_request.c

package info (click to toggle)
totd 1.4-3
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 660 kB
  • ctags: 512
  • sloc: ansic: 5,840; sh: 2,826; perl: 111; makefile: 110
file content (180 lines) | stat: -rw-r--r-- 5,214 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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/****************************************************************************
 * 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_request.c,v 3.53 2002/03/04 12:34:11 dillema Exp $>
 */

#include "totd.h"

/*
 * A request message is sent over the given socket, and a
 * timeout event registered:
 *	- if no response within timeout, retry routine is called. 
 * 	- if response is received, it replaces the request message in the
 *	  (child) context and the process routine is called.
 */

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_process (Context *cont) {
	char *fn = "udp_request_process()";
	struct sockaddr *sa;

	syslog (LOG_DEBUG, "%s: start", fn);

	/*
	 * ev_udp_in() called us to process an incoming response
	 * to the request we sent. The response message has been
	 * put in cont->mesg.p
	 */

	/*
	 * Timeout will still go off later, so we render
	 * it harmless here by removing the handler.
	 */
	if (cont->tout)
		cont->tout->handler = NULL;

	/* remove me from I/O list */
	sa = (struct sockaddr *) cont->current_ns->list_data;
	if (ev_udp_in_remove (sa, cont->q_id) < 0)
		return (request_abort (cont, -1));
	else
		return request_finish (cont);
}

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;
}