1*00b67f09SDavid van Moolenbroek /* $NetBSD: client.c,v 1.13 2015/07/08 17:28:55 christos Exp $ */
2*00b67f09SDavid van Moolenbroek
3*00b67f09SDavid van Moolenbroek /*
4*00b67f09SDavid van Moolenbroek * Copyright (C) 2004-2015 Internet Systems Consortium, Inc. ("ISC")
5*00b67f09SDavid van Moolenbroek * Copyright (C) 1999-2003 Internet Software Consortium.
6*00b67f09SDavid van Moolenbroek *
7*00b67f09SDavid van Moolenbroek * Permission to use, copy, modify, and/or distribute this software for any
8*00b67f09SDavid van Moolenbroek * purpose with or without fee is hereby granted, provided that the above
9*00b67f09SDavid van Moolenbroek * copyright notice and this permission notice appear in all copies.
10*00b67f09SDavid van Moolenbroek *
11*00b67f09SDavid van Moolenbroek * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12*00b67f09SDavid van Moolenbroek * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13*00b67f09SDavid van Moolenbroek * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14*00b67f09SDavid van Moolenbroek * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15*00b67f09SDavid van Moolenbroek * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16*00b67f09SDavid van Moolenbroek * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*00b67f09SDavid van Moolenbroek * PERFORMANCE OF THIS SOFTWARE.
18*00b67f09SDavid van Moolenbroek */
19*00b67f09SDavid van Moolenbroek
20*00b67f09SDavid van Moolenbroek /* Id: client.c,v 1.286 2012/01/31 23:47:30 tbox Exp */
21*00b67f09SDavid van Moolenbroek
22*00b67f09SDavid van Moolenbroek #include <config.h>
23*00b67f09SDavid van Moolenbroek
24*00b67f09SDavid van Moolenbroek #include <isc/formatcheck.h>
25*00b67f09SDavid van Moolenbroek #include <isc/mutex.h>
26*00b67f09SDavid van Moolenbroek #include <isc/once.h>
27*00b67f09SDavid van Moolenbroek #include <isc/platform.h>
28*00b67f09SDavid van Moolenbroek #include <isc/print.h>
29*00b67f09SDavid van Moolenbroek #include <isc/queue.h>
30*00b67f09SDavid van Moolenbroek #include <isc/random.h>
31*00b67f09SDavid van Moolenbroek #include <isc/serial.h>
32*00b67f09SDavid van Moolenbroek #include <isc/stats.h>
33*00b67f09SDavid van Moolenbroek #include <isc/stdio.h>
34*00b67f09SDavid van Moolenbroek #include <isc/string.h>
35*00b67f09SDavid van Moolenbroek #include <isc/task.h>
36*00b67f09SDavid van Moolenbroek #include <isc/timer.h>
37*00b67f09SDavid van Moolenbroek #include <isc/util.h>
38*00b67f09SDavid van Moolenbroek
39*00b67f09SDavid van Moolenbroek #ifdef AES_SIT
40*00b67f09SDavid van Moolenbroek #include <isc/aes.h>
41*00b67f09SDavid van Moolenbroek #else
42*00b67f09SDavid van Moolenbroek #include <isc/hmacsha.h>
43*00b67f09SDavid van Moolenbroek #endif
44*00b67f09SDavid van Moolenbroek
45*00b67f09SDavid van Moolenbroek #include <dns/db.h>
46*00b67f09SDavid van Moolenbroek #include <dns/dispatch.h>
47*00b67f09SDavid van Moolenbroek #include <dns/events.h>
48*00b67f09SDavid van Moolenbroek #include <dns/message.h>
49*00b67f09SDavid van Moolenbroek #include <dns/peer.h>
50*00b67f09SDavid van Moolenbroek #include <dns/rcode.h>
51*00b67f09SDavid van Moolenbroek #include <dns/rdata.h>
52*00b67f09SDavid van Moolenbroek #include <dns/rdataclass.h>
53*00b67f09SDavid van Moolenbroek #include <dns/rdatalist.h>
54*00b67f09SDavid van Moolenbroek #include <dns/rdataset.h>
55*00b67f09SDavid van Moolenbroek #include <dns/resolver.h>
56*00b67f09SDavid van Moolenbroek #include <dns/stats.h>
57*00b67f09SDavid van Moolenbroek #include <dns/tsig.h>
58*00b67f09SDavid van Moolenbroek #include <dns/view.h>
59*00b67f09SDavid van Moolenbroek #include <dns/zone.h>
60*00b67f09SDavid van Moolenbroek
61*00b67f09SDavid van Moolenbroek #include <named/interfacemgr.h>
62*00b67f09SDavid van Moolenbroek #include <named/log.h>
63*00b67f09SDavid van Moolenbroek #include <named/notify.h>
64*00b67f09SDavid van Moolenbroek #include <named/os.h>
65*00b67f09SDavid van Moolenbroek #include <named/server.h>
66*00b67f09SDavid van Moolenbroek #include <named/update.h>
67*00b67f09SDavid van Moolenbroek
68*00b67f09SDavid van Moolenbroek #include "pfilter.h"
69*00b67f09SDavid van Moolenbroek
70*00b67f09SDavid van Moolenbroek /***
71*00b67f09SDavid van Moolenbroek *** Client
72*00b67f09SDavid van Moolenbroek ***/
73*00b67f09SDavid van Moolenbroek
74*00b67f09SDavid van Moolenbroek /*! \file
75*00b67f09SDavid van Moolenbroek * Client Routines
76*00b67f09SDavid van Moolenbroek *
77*00b67f09SDavid van Moolenbroek * Important note!
78*00b67f09SDavid van Moolenbroek *
79*00b67f09SDavid van Moolenbroek * All client state changes, other than that from idle to listening, occur
80*00b67f09SDavid van Moolenbroek * as a result of events. This guarantees serialization and avoids the
81*00b67f09SDavid van Moolenbroek * need for locking.
82*00b67f09SDavid van Moolenbroek *
83*00b67f09SDavid van Moolenbroek * If a routine is ever created that allows someone other than the client's
84*00b67f09SDavid van Moolenbroek * task to change the client, then the client will have to be locked.
85*00b67f09SDavid van Moolenbroek */
86*00b67f09SDavid van Moolenbroek
87*00b67f09SDavid van Moolenbroek #define NS_CLIENT_TRACE
88*00b67f09SDavid van Moolenbroek #ifdef NS_CLIENT_TRACE
89*00b67f09SDavid van Moolenbroek #define CTRACE(m) ns_client_log(client, \
90*00b67f09SDavid van Moolenbroek NS_LOGCATEGORY_CLIENT, \
91*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, \
92*00b67f09SDavid van Moolenbroek ISC_LOG_DEBUG(3), \
93*00b67f09SDavid van Moolenbroek "%s", (m))
94*00b67f09SDavid van Moolenbroek #define MTRACE(m) isc_log_write(ns_g_lctx, \
95*00b67f09SDavid van Moolenbroek NS_LOGCATEGORY_GENERAL, \
96*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, \
97*00b67f09SDavid van Moolenbroek ISC_LOG_DEBUG(3), \
98*00b67f09SDavid van Moolenbroek "clientmgr @%p: %s", manager, (m))
99*00b67f09SDavid van Moolenbroek #else
100*00b67f09SDavid van Moolenbroek #define CTRACE(m) ((void)(m))
101*00b67f09SDavid van Moolenbroek #define MTRACE(m) ((void)(m))
102*00b67f09SDavid van Moolenbroek #endif
103*00b67f09SDavid van Moolenbroek
104*00b67f09SDavid van Moolenbroek #define TCP_CLIENT(c) (((c)->attributes & NS_CLIENTATTR_TCP) != 0)
105*00b67f09SDavid van Moolenbroek
106*00b67f09SDavid van Moolenbroek #define TCP_BUFFER_SIZE (65535 + 2)
107*00b67f09SDavid van Moolenbroek #define SEND_BUFFER_SIZE 4096
108*00b67f09SDavid van Moolenbroek #define RECV_BUFFER_SIZE 4096
109*00b67f09SDavid van Moolenbroek
110*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USETHREADS
111*00b67f09SDavid van Moolenbroek #define NMCTXS 100
112*00b67f09SDavid van Moolenbroek /*%<
113*00b67f09SDavid van Moolenbroek * Number of 'mctx pools' for clients. (Should this be configurable?)
114*00b67f09SDavid van Moolenbroek * When enabling threads, we use a pool of memory contexts shared by
115*00b67f09SDavid van Moolenbroek * client objects, since concurrent access to a shared context would cause
116*00b67f09SDavid van Moolenbroek * heavy contentions. The above constant is expected to be enough for
117*00b67f09SDavid van Moolenbroek * completely avoiding contentions among threads for an authoritative-only
118*00b67f09SDavid van Moolenbroek * server.
119*00b67f09SDavid van Moolenbroek */
120*00b67f09SDavid van Moolenbroek #else
121*00b67f09SDavid van Moolenbroek #define NMCTXS 0
122*00b67f09SDavid van Moolenbroek /*%<
123*00b67f09SDavid van Moolenbroek * If named with built without thread, simply share manager's context. Using
124*00b67f09SDavid van Moolenbroek * a separate context in this case would simply waste memory.
125*00b67f09SDavid van Moolenbroek */
126*00b67f09SDavid van Moolenbroek #endif
127*00b67f09SDavid van Moolenbroek
128*00b67f09SDavid van Moolenbroek #define SIT_SIZE 24U /* 8 + 4 + 4 + 8 */
129*00b67f09SDavid van Moolenbroek
130*00b67f09SDavid van Moolenbroek /*% nameserver client manager structure */
131*00b67f09SDavid van Moolenbroek struct ns_clientmgr {
132*00b67f09SDavid van Moolenbroek /* Unlocked. */
133*00b67f09SDavid van Moolenbroek unsigned int magic;
134*00b67f09SDavid van Moolenbroek
135*00b67f09SDavid van Moolenbroek /* The queue object has its own locks */
136*00b67f09SDavid van Moolenbroek client_queue_t inactive; /*%< To be recycled */
137*00b67f09SDavid van Moolenbroek
138*00b67f09SDavid van Moolenbroek isc_mem_t * mctx;
139*00b67f09SDavid van Moolenbroek isc_taskmgr_t * taskmgr;
140*00b67f09SDavid van Moolenbroek isc_timermgr_t * timermgr;
141*00b67f09SDavid van Moolenbroek
142*00b67f09SDavid van Moolenbroek /* Lock covers manager state. */
143*00b67f09SDavid van Moolenbroek isc_mutex_t lock;
144*00b67f09SDavid van Moolenbroek isc_boolean_t exiting;
145*00b67f09SDavid van Moolenbroek
146*00b67f09SDavid van Moolenbroek /* Lock covers the clients list */
147*00b67f09SDavid van Moolenbroek isc_mutex_t listlock;
148*00b67f09SDavid van Moolenbroek client_list_t clients; /*%< All active clients */
149*00b67f09SDavid van Moolenbroek
150*00b67f09SDavid van Moolenbroek /* Lock covers the recursing list */
151*00b67f09SDavid van Moolenbroek isc_mutex_t reclock;
152*00b67f09SDavid van Moolenbroek client_list_t recursing; /*%< Recursing clients */
153*00b67f09SDavid van Moolenbroek
154*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
155*00b67f09SDavid van Moolenbroek /*%< mctx pool for clients. */
156*00b67f09SDavid van Moolenbroek unsigned int nextmctx;
157*00b67f09SDavid van Moolenbroek isc_mem_t * mctxpool[NMCTXS];
158*00b67f09SDavid van Moolenbroek #endif
159*00b67f09SDavid van Moolenbroek };
160*00b67f09SDavid van Moolenbroek
161*00b67f09SDavid van Moolenbroek #define MANAGER_MAGIC ISC_MAGIC('N', 'S', 'C', 'm')
162*00b67f09SDavid van Moolenbroek #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, MANAGER_MAGIC)
163*00b67f09SDavid van Moolenbroek
164*00b67f09SDavid van Moolenbroek /*!
165*00b67f09SDavid van Moolenbroek * Client object states. Ordering is significant: higher-numbered
166*00b67f09SDavid van Moolenbroek * states are generally "more active", meaning that the client can
167*00b67f09SDavid van Moolenbroek * have more dynamically allocated data, outstanding events, etc.
168*00b67f09SDavid van Moolenbroek * In the list below, any such properties listed for state N
169*00b67f09SDavid van Moolenbroek * also apply to any state > N.
170*00b67f09SDavid van Moolenbroek *
171*00b67f09SDavid van Moolenbroek * To force the client into a less active state, set client->newstate
172*00b67f09SDavid van Moolenbroek * to that state and call exit_check(). This will cause any
173*00b67f09SDavid van Moolenbroek * activities defined for higher-numbered states to be aborted.
174*00b67f09SDavid van Moolenbroek */
175*00b67f09SDavid van Moolenbroek
176*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_FREED 0
177*00b67f09SDavid van Moolenbroek /*%<
178*00b67f09SDavid van Moolenbroek * The client object no longer exists.
179*00b67f09SDavid van Moolenbroek */
180*00b67f09SDavid van Moolenbroek
181*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_INACTIVE 1
182*00b67f09SDavid van Moolenbroek /*%<
183*00b67f09SDavid van Moolenbroek * The client object exists and has a task and timer.
184*00b67f09SDavid van Moolenbroek * Its "query" struct and sendbuf are initialized.
185*00b67f09SDavid van Moolenbroek * It is on the client manager's list of inactive clients.
186*00b67f09SDavid van Moolenbroek * It has a message and OPT, both in the reset state.
187*00b67f09SDavid van Moolenbroek */
188*00b67f09SDavid van Moolenbroek
189*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_READY 2
190*00b67f09SDavid van Moolenbroek /*%<
191*00b67f09SDavid van Moolenbroek * The client object is either a TCP or a UDP one, and
192*00b67f09SDavid van Moolenbroek * it is associated with a network interface. It is on the
193*00b67f09SDavid van Moolenbroek * client manager's list of active clients.
194*00b67f09SDavid van Moolenbroek *
195*00b67f09SDavid van Moolenbroek * If it is a TCP client object, it has a TCP listener socket
196*00b67f09SDavid van Moolenbroek * and an outstanding TCP listen request.
197*00b67f09SDavid van Moolenbroek *
198*00b67f09SDavid van Moolenbroek * If it is a UDP client object, it has a UDP listener socket
199*00b67f09SDavid van Moolenbroek * and an outstanding UDP receive request.
200*00b67f09SDavid van Moolenbroek */
201*00b67f09SDavid van Moolenbroek
202*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_READING 3
203*00b67f09SDavid van Moolenbroek /*%<
204*00b67f09SDavid van Moolenbroek * The client object is a TCP client object that has received
205*00b67f09SDavid van Moolenbroek * a connection. It has a tcpsocket, tcpmsg, TCP quota, and an
206*00b67f09SDavid van Moolenbroek * outstanding TCP read request. This state is not used for
207*00b67f09SDavid van Moolenbroek * UDP client objects.
208*00b67f09SDavid van Moolenbroek */
209*00b67f09SDavid van Moolenbroek
210*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_WORKING 4
211*00b67f09SDavid van Moolenbroek /*%<
212*00b67f09SDavid van Moolenbroek * The client object has received a request and is working
213*00b67f09SDavid van Moolenbroek * on it. It has a view, and it may have any of a non-reset OPT,
214*00b67f09SDavid van Moolenbroek * recursion quota, and an outstanding write request.
215*00b67f09SDavid van Moolenbroek */
216*00b67f09SDavid van Moolenbroek
217*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_RECURSING 5
218*00b67f09SDavid van Moolenbroek /*%<
219*00b67f09SDavid van Moolenbroek * The client object is recursing. It will be on the 'recursing'
220*00b67f09SDavid van Moolenbroek * list.
221*00b67f09SDavid van Moolenbroek */
222*00b67f09SDavid van Moolenbroek
223*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_MAX 9
224*00b67f09SDavid van Moolenbroek /*%<
225*00b67f09SDavid van Moolenbroek * Sentinel value used to indicate "no state". When client->newstate
226*00b67f09SDavid van Moolenbroek * has this value, we are not attempting to exit the current state.
227*00b67f09SDavid van Moolenbroek * Must be greater than any valid state.
228*00b67f09SDavid van Moolenbroek */
229*00b67f09SDavid van Moolenbroek
230*00b67f09SDavid van Moolenbroek /*
231*00b67f09SDavid van Moolenbroek * Enable ns_client_dropport() by default.
232*00b67f09SDavid van Moolenbroek */
233*00b67f09SDavid van Moolenbroek #ifndef NS_CLIENT_DROPPORT
234*00b67f09SDavid van Moolenbroek #define NS_CLIENT_DROPPORT 1
235*00b67f09SDavid van Moolenbroek #endif
236*00b67f09SDavid van Moolenbroek
237*00b67f09SDavid van Moolenbroek unsigned int ns_client_requests;
238*00b67f09SDavid van Moolenbroek
239*00b67f09SDavid van Moolenbroek static void client_read(ns_client_t *client);
240*00b67f09SDavid van Moolenbroek static void client_accept(ns_client_t *client);
241*00b67f09SDavid van Moolenbroek static void client_udprecv(ns_client_t *client);
242*00b67f09SDavid van Moolenbroek static void clientmgr_destroy(ns_clientmgr_t *manager);
243*00b67f09SDavid van Moolenbroek static isc_boolean_t exit_check(ns_client_t *client);
244*00b67f09SDavid van Moolenbroek static void ns_client_endrequest(ns_client_t *client);
245*00b67f09SDavid van Moolenbroek static void client_start(isc_task_t *task, isc_event_t *event);
246*00b67f09SDavid van Moolenbroek static void client_request(isc_task_t *task, isc_event_t *event);
247*00b67f09SDavid van Moolenbroek static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
248*00b67f09SDavid van Moolenbroek static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
249*00b67f09SDavid van Moolenbroek dns_dispatch_t *disp, isc_boolean_t tcp);
250*00b67f09SDavid van Moolenbroek static inline isc_boolean_t
251*00b67f09SDavid van Moolenbroek allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl);
252*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
253*00b67f09SDavid van Moolenbroek static void compute_sit(ns_client_t *client, isc_uint32_t when,
254*00b67f09SDavid van Moolenbroek isc_uint32_t nonce, isc_buffer_t *buf);
255*00b67f09SDavid van Moolenbroek #endif
256*00b67f09SDavid van Moolenbroek
257*00b67f09SDavid van Moolenbroek void
ns_client_recursing(ns_client_t * client)258*00b67f09SDavid van Moolenbroek ns_client_recursing(ns_client_t *client) {
259*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
260*00b67f09SDavid van Moolenbroek REQUIRE(client->state == NS_CLIENTSTATE_WORKING);
261*00b67f09SDavid van Moolenbroek
262*00b67f09SDavid van Moolenbroek LOCK(&client->manager->reclock);
263*00b67f09SDavid van Moolenbroek client->newstate = client->state = NS_CLIENTSTATE_RECURSING;
264*00b67f09SDavid van Moolenbroek ISC_LIST_APPEND(client->manager->recursing, client, rlink);
265*00b67f09SDavid van Moolenbroek UNLOCK(&client->manager->reclock);
266*00b67f09SDavid van Moolenbroek }
267*00b67f09SDavid van Moolenbroek
268*00b67f09SDavid van Moolenbroek void
ns_client_killoldestquery(ns_client_t * client)269*00b67f09SDavid van Moolenbroek ns_client_killoldestquery(ns_client_t *client) {
270*00b67f09SDavid van Moolenbroek ns_client_t *oldest;
271*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
272*00b67f09SDavid van Moolenbroek
273*00b67f09SDavid van Moolenbroek LOCK(&client->manager->reclock);
274*00b67f09SDavid van Moolenbroek oldest = ISC_LIST_HEAD(client->manager->recursing);
275*00b67f09SDavid van Moolenbroek if (oldest != NULL) {
276*00b67f09SDavid van Moolenbroek ISC_LIST_UNLINK(client->manager->recursing, oldest, rlink);
277*00b67f09SDavid van Moolenbroek UNLOCK(&client->manager->reclock);
278*00b67f09SDavid van Moolenbroek ns_query_cancel(oldest);
279*00b67f09SDavid van Moolenbroek } else
280*00b67f09SDavid van Moolenbroek UNLOCK(&client->manager->reclock);
281*00b67f09SDavid van Moolenbroek }
282*00b67f09SDavid van Moolenbroek
283*00b67f09SDavid van Moolenbroek void
ns_client_settimeout(ns_client_t * client,unsigned int seconds)284*00b67f09SDavid van Moolenbroek ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
285*00b67f09SDavid van Moolenbroek isc_result_t result;
286*00b67f09SDavid van Moolenbroek isc_interval_t interval;
287*00b67f09SDavid van Moolenbroek
288*00b67f09SDavid van Moolenbroek isc_interval_set(&interval, seconds, 0);
289*00b67f09SDavid van Moolenbroek result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
290*00b67f09SDavid van Moolenbroek &interval, ISC_FALSE);
291*00b67f09SDavid van Moolenbroek client->timerset = ISC_TRUE;
292*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
293*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
294*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
295*00b67f09SDavid van Moolenbroek "setting timeout: %s",
296*00b67f09SDavid van Moolenbroek isc_result_totext(result));
297*00b67f09SDavid van Moolenbroek /* Continue anyway. */
298*00b67f09SDavid van Moolenbroek }
299*00b67f09SDavid van Moolenbroek }
300*00b67f09SDavid van Moolenbroek
301*00b67f09SDavid van Moolenbroek /*%
302*00b67f09SDavid van Moolenbroek * Check for a deactivation or shutdown request and take appropriate
303*00b67f09SDavid van Moolenbroek * action. Returns ISC_TRUE if either is in progress; in this case
304*00b67f09SDavid van Moolenbroek * the caller must no longer use the client object as it may have been
305*00b67f09SDavid van Moolenbroek * freed.
306*00b67f09SDavid van Moolenbroek */
307*00b67f09SDavid van Moolenbroek static isc_boolean_t
exit_check(ns_client_t * client)308*00b67f09SDavid van Moolenbroek exit_check(ns_client_t *client) {
309*00b67f09SDavid van Moolenbroek isc_boolean_t destroy_manager = ISC_FALSE;
310*00b67f09SDavid van Moolenbroek ns_clientmgr_t *manager = NULL;
311*00b67f09SDavid van Moolenbroek
312*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
313*00b67f09SDavid van Moolenbroek manager = client->manager;
314*00b67f09SDavid van Moolenbroek
315*00b67f09SDavid van Moolenbroek if (client->state <= client->newstate)
316*00b67f09SDavid van Moolenbroek return (ISC_FALSE); /* Business as usual. */
317*00b67f09SDavid van Moolenbroek
318*00b67f09SDavid van Moolenbroek INSIST(client->newstate < NS_CLIENTSTATE_RECURSING);
319*00b67f09SDavid van Moolenbroek
320*00b67f09SDavid van Moolenbroek /*
321*00b67f09SDavid van Moolenbroek * We need to detach from the view early when shutting down
322*00b67f09SDavid van Moolenbroek * the server to break the following vicious circle:
323*00b67f09SDavid van Moolenbroek *
324*00b67f09SDavid van Moolenbroek * - The resolver will not shut down until the view refcount is zero
325*00b67f09SDavid van Moolenbroek * - The view refcount does not go to zero until all clients detach
326*00b67f09SDavid van Moolenbroek * - The client does not detach from the view until references is zero
327*00b67f09SDavid van Moolenbroek * - references does not go to zero until the resolver has shut down
328*00b67f09SDavid van Moolenbroek *
329*00b67f09SDavid van Moolenbroek * Keep the view attached until any outstanding updates complete.
330*00b67f09SDavid van Moolenbroek */
331*00b67f09SDavid van Moolenbroek if (client->nupdates == 0 &&
332*00b67f09SDavid van Moolenbroek client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
333*00b67f09SDavid van Moolenbroek dns_view_detach(&client->view);
334*00b67f09SDavid van Moolenbroek
335*00b67f09SDavid van Moolenbroek if (client->state == NS_CLIENTSTATE_WORKING ||
336*00b67f09SDavid van Moolenbroek client->state == NS_CLIENTSTATE_RECURSING)
337*00b67f09SDavid van Moolenbroek {
338*00b67f09SDavid van Moolenbroek INSIST(client->newstate <= NS_CLIENTSTATE_READING);
339*00b67f09SDavid van Moolenbroek /*
340*00b67f09SDavid van Moolenbroek * Let the update processing complete.
341*00b67f09SDavid van Moolenbroek */
342*00b67f09SDavid van Moolenbroek if (client->nupdates > 0)
343*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
344*00b67f09SDavid van Moolenbroek
345*00b67f09SDavid van Moolenbroek /*
346*00b67f09SDavid van Moolenbroek * We are trying to abort request processing.
347*00b67f09SDavid van Moolenbroek */
348*00b67f09SDavid van Moolenbroek if (client->nsends > 0) {
349*00b67f09SDavid van Moolenbroek isc_socket_t *socket;
350*00b67f09SDavid van Moolenbroek if (TCP_CLIENT(client))
351*00b67f09SDavid van Moolenbroek socket = client->tcpsocket;
352*00b67f09SDavid van Moolenbroek else
353*00b67f09SDavid van Moolenbroek socket = client->udpsocket;
354*00b67f09SDavid van Moolenbroek isc_socket_cancel(socket, client->task,
355*00b67f09SDavid van Moolenbroek ISC_SOCKCANCEL_SEND);
356*00b67f09SDavid van Moolenbroek }
357*00b67f09SDavid van Moolenbroek
358*00b67f09SDavid van Moolenbroek if (! (client->nsends == 0 && client->nrecvs == 0 &&
359*00b67f09SDavid van Moolenbroek client->references == 0))
360*00b67f09SDavid van Moolenbroek {
361*00b67f09SDavid van Moolenbroek /*
362*00b67f09SDavid van Moolenbroek * Still waiting for I/O cancel completion.
363*00b67f09SDavid van Moolenbroek * or lingering references.
364*00b67f09SDavid van Moolenbroek */
365*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
366*00b67f09SDavid van Moolenbroek }
367*00b67f09SDavid van Moolenbroek
368*00b67f09SDavid van Moolenbroek /*
369*00b67f09SDavid van Moolenbroek * I/O cancel is complete. Burn down all state
370*00b67f09SDavid van Moolenbroek * related to the current request. Ensure that
371*00b67f09SDavid van Moolenbroek * the client is no longer on the recursing list.
372*00b67f09SDavid van Moolenbroek *
373*00b67f09SDavid van Moolenbroek * We need to check whether the client is still linked,
374*00b67f09SDavid van Moolenbroek * because it may already have been removed from the
375*00b67f09SDavid van Moolenbroek * recursing list by ns_client_killoldestquery()
376*00b67f09SDavid van Moolenbroek */
377*00b67f09SDavid van Moolenbroek if (client->state == NS_CLIENTSTATE_RECURSING) {
378*00b67f09SDavid van Moolenbroek LOCK(&manager->reclock);
379*00b67f09SDavid van Moolenbroek if (ISC_LINK_LINKED(client, rlink))
380*00b67f09SDavid van Moolenbroek ISC_LIST_UNLINK(manager->recursing,
381*00b67f09SDavid van Moolenbroek client, rlink);
382*00b67f09SDavid van Moolenbroek UNLOCK(&manager->reclock);
383*00b67f09SDavid van Moolenbroek }
384*00b67f09SDavid van Moolenbroek ns_client_endrequest(client);
385*00b67f09SDavid van Moolenbroek
386*00b67f09SDavid van Moolenbroek client->state = NS_CLIENTSTATE_READING;
387*00b67f09SDavid van Moolenbroek INSIST(client->recursionquota == NULL);
388*00b67f09SDavid van Moolenbroek
389*00b67f09SDavid van Moolenbroek if (NS_CLIENTSTATE_READING == client->newstate) {
390*00b67f09SDavid van Moolenbroek client_read(client);
391*00b67f09SDavid van Moolenbroek client->newstate = NS_CLIENTSTATE_MAX;
392*00b67f09SDavid van Moolenbroek return (ISC_TRUE); /* We're done. */
393*00b67f09SDavid van Moolenbroek }
394*00b67f09SDavid van Moolenbroek }
395*00b67f09SDavid van Moolenbroek
396*00b67f09SDavid van Moolenbroek if (client->state == NS_CLIENTSTATE_READING) {
397*00b67f09SDavid van Moolenbroek /*
398*00b67f09SDavid van Moolenbroek * We are trying to abort the current TCP connection,
399*00b67f09SDavid van Moolenbroek * if any.
400*00b67f09SDavid van Moolenbroek */
401*00b67f09SDavid van Moolenbroek INSIST(client->recursionquota == NULL);
402*00b67f09SDavid van Moolenbroek INSIST(client->newstate <= NS_CLIENTSTATE_READY);
403*00b67f09SDavid van Moolenbroek if (client->nreads > 0)
404*00b67f09SDavid van Moolenbroek dns_tcpmsg_cancelread(&client->tcpmsg);
405*00b67f09SDavid van Moolenbroek if (! client->nreads == 0) {
406*00b67f09SDavid van Moolenbroek /* Still waiting for read cancel completion. */
407*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
408*00b67f09SDavid van Moolenbroek }
409*00b67f09SDavid van Moolenbroek
410*00b67f09SDavid van Moolenbroek if (client->tcpmsg_valid) {
411*00b67f09SDavid van Moolenbroek dns_tcpmsg_invalidate(&client->tcpmsg);
412*00b67f09SDavid van Moolenbroek client->tcpmsg_valid = ISC_FALSE;
413*00b67f09SDavid van Moolenbroek }
414*00b67f09SDavid van Moolenbroek if (client->tcpsocket != NULL) {
415*00b67f09SDavid van Moolenbroek CTRACE("closetcp");
416*00b67f09SDavid van Moolenbroek isc_socket_detach(&client->tcpsocket);
417*00b67f09SDavid van Moolenbroek }
418*00b67f09SDavid van Moolenbroek
419*00b67f09SDavid van Moolenbroek if (client->tcpquota != NULL)
420*00b67f09SDavid van Moolenbroek isc_quota_detach(&client->tcpquota);
421*00b67f09SDavid van Moolenbroek
422*00b67f09SDavid van Moolenbroek if (client->timerset) {
423*00b67f09SDavid van Moolenbroek (void)isc_timer_reset(client->timer,
424*00b67f09SDavid van Moolenbroek isc_timertype_inactive,
425*00b67f09SDavid van Moolenbroek NULL, NULL, ISC_TRUE);
426*00b67f09SDavid van Moolenbroek client->timerset = ISC_FALSE;
427*00b67f09SDavid van Moolenbroek }
428*00b67f09SDavid van Moolenbroek
429*00b67f09SDavid van Moolenbroek client->peeraddr_valid = ISC_FALSE;
430*00b67f09SDavid van Moolenbroek
431*00b67f09SDavid van Moolenbroek client->state = NS_CLIENTSTATE_READY;
432*00b67f09SDavid van Moolenbroek INSIST(client->recursionquota == NULL);
433*00b67f09SDavid van Moolenbroek
434*00b67f09SDavid van Moolenbroek /*
435*00b67f09SDavid van Moolenbroek * Now the client is ready to accept a new TCP connection
436*00b67f09SDavid van Moolenbroek * or UDP request, but we may have enough clients doing
437*00b67f09SDavid van Moolenbroek * that already. Check whether this client needs to remain
438*00b67f09SDavid van Moolenbroek * active and force it to go inactive if not.
439*00b67f09SDavid van Moolenbroek *
440*00b67f09SDavid van Moolenbroek * UDP clients go inactive at this point, but TCP clients
441*00b67f09SDavid van Moolenbroek * may remain active if we have fewer active TCP client
442*00b67f09SDavid van Moolenbroek * objects than desired due to an earlier quota exhaustion.
443*00b67f09SDavid van Moolenbroek */
444*00b67f09SDavid van Moolenbroek if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) {
445*00b67f09SDavid van Moolenbroek LOCK(&client->interface->lock);
446*00b67f09SDavid van Moolenbroek if (client->interface->ntcpcurrent <
447*00b67f09SDavid van Moolenbroek client->interface->ntcptarget)
448*00b67f09SDavid van Moolenbroek client->mortal = ISC_FALSE;
449*00b67f09SDavid van Moolenbroek UNLOCK(&client->interface->lock);
450*00b67f09SDavid van Moolenbroek }
451*00b67f09SDavid van Moolenbroek
452*00b67f09SDavid van Moolenbroek /*
453*00b67f09SDavid van Moolenbroek * We don't need the client; send it to the inactive
454*00b67f09SDavid van Moolenbroek * queue for recycling.
455*00b67f09SDavid van Moolenbroek */
456*00b67f09SDavid van Moolenbroek if (client->mortal) {
457*00b67f09SDavid van Moolenbroek if (client->newstate > NS_CLIENTSTATE_INACTIVE)
458*00b67f09SDavid van Moolenbroek client->newstate = NS_CLIENTSTATE_INACTIVE;
459*00b67f09SDavid van Moolenbroek }
460*00b67f09SDavid van Moolenbroek
461*00b67f09SDavid van Moolenbroek if (NS_CLIENTSTATE_READY == client->newstate) {
462*00b67f09SDavid van Moolenbroek if (TCP_CLIENT(client)) {
463*00b67f09SDavid van Moolenbroek client_accept(client);
464*00b67f09SDavid van Moolenbroek } else
465*00b67f09SDavid van Moolenbroek client_udprecv(client);
466*00b67f09SDavid van Moolenbroek client->newstate = NS_CLIENTSTATE_MAX;
467*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
468*00b67f09SDavid van Moolenbroek }
469*00b67f09SDavid van Moolenbroek }
470*00b67f09SDavid van Moolenbroek
471*00b67f09SDavid van Moolenbroek if (client->state == NS_CLIENTSTATE_READY) {
472*00b67f09SDavid van Moolenbroek INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);
473*00b67f09SDavid van Moolenbroek
474*00b67f09SDavid van Moolenbroek /*
475*00b67f09SDavid van Moolenbroek * We are trying to enter the inactive state.
476*00b67f09SDavid van Moolenbroek */
477*00b67f09SDavid van Moolenbroek if (client->naccepts > 0)
478*00b67f09SDavid van Moolenbroek isc_socket_cancel(client->tcplistener, client->task,
479*00b67f09SDavid van Moolenbroek ISC_SOCKCANCEL_ACCEPT);
480*00b67f09SDavid van Moolenbroek
481*00b67f09SDavid van Moolenbroek /* Still waiting for accept cancel completion. */
482*00b67f09SDavid van Moolenbroek if (! (client->naccepts == 0))
483*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
484*00b67f09SDavid van Moolenbroek
485*00b67f09SDavid van Moolenbroek /* Accept cancel is complete. */
486*00b67f09SDavid van Moolenbroek if (client->nrecvs > 0)
487*00b67f09SDavid van Moolenbroek isc_socket_cancel(client->udpsocket, client->task,
488*00b67f09SDavid van Moolenbroek ISC_SOCKCANCEL_RECV);
489*00b67f09SDavid van Moolenbroek
490*00b67f09SDavid van Moolenbroek /* Still waiting for recv cancel completion. */
491*00b67f09SDavid van Moolenbroek if (! (client->nrecvs == 0))
492*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
493*00b67f09SDavid van Moolenbroek
494*00b67f09SDavid van Moolenbroek /* Still waiting for control event to be delivered */
495*00b67f09SDavid van Moolenbroek if (client->nctls > 0)
496*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
497*00b67f09SDavid van Moolenbroek
498*00b67f09SDavid van Moolenbroek /* Deactivate the client. */
499*00b67f09SDavid van Moolenbroek if (client->interface)
500*00b67f09SDavid van Moolenbroek ns_interface_detach(&client->interface);
501*00b67f09SDavid van Moolenbroek
502*00b67f09SDavid van Moolenbroek INSIST(client->naccepts == 0);
503*00b67f09SDavid van Moolenbroek INSIST(client->recursionquota == NULL);
504*00b67f09SDavid van Moolenbroek if (client->tcplistener != NULL)
505*00b67f09SDavid van Moolenbroek isc_socket_detach(&client->tcplistener);
506*00b67f09SDavid van Moolenbroek
507*00b67f09SDavid van Moolenbroek if (client->udpsocket != NULL)
508*00b67f09SDavid van Moolenbroek isc_socket_detach(&client->udpsocket);
509*00b67f09SDavid van Moolenbroek
510*00b67f09SDavid van Moolenbroek if (client->dispatch != NULL)
511*00b67f09SDavid van Moolenbroek dns_dispatch_detach(&client->dispatch);
512*00b67f09SDavid van Moolenbroek
513*00b67f09SDavid van Moolenbroek client->attributes = 0;
514*00b67f09SDavid van Moolenbroek client->mortal = ISC_FALSE;
515*00b67f09SDavid van Moolenbroek
516*00b67f09SDavid van Moolenbroek /*
517*00b67f09SDavid van Moolenbroek * Put the client on the inactive list. If we are aiming for
518*00b67f09SDavid van Moolenbroek * the "freed" state, it will be removed from the inactive
519*00b67f09SDavid van Moolenbroek * list shortly, and we need to keep the manager locked until
520*00b67f09SDavid van Moolenbroek * that has been done, lest the manager decide to reactivate
521*00b67f09SDavid van Moolenbroek * the dying client inbetween.
522*00b67f09SDavid van Moolenbroek */
523*00b67f09SDavid van Moolenbroek client->state = NS_CLIENTSTATE_INACTIVE;
524*00b67f09SDavid van Moolenbroek INSIST(client->recursionquota == NULL);
525*00b67f09SDavid van Moolenbroek
526*00b67f09SDavid van Moolenbroek if (client->state == client->newstate) {
527*00b67f09SDavid van Moolenbroek client->newstate = NS_CLIENTSTATE_MAX;
528*00b67f09SDavid van Moolenbroek if (!ns_g_clienttest && manager != NULL &&
529*00b67f09SDavid van Moolenbroek !manager->exiting)
530*00b67f09SDavid van Moolenbroek ISC_QUEUE_PUSH(manager->inactive, client,
531*00b67f09SDavid van Moolenbroek ilink);
532*00b67f09SDavid van Moolenbroek if (client->needshutdown)
533*00b67f09SDavid van Moolenbroek isc_task_shutdown(client->task);
534*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
535*00b67f09SDavid van Moolenbroek }
536*00b67f09SDavid van Moolenbroek }
537*00b67f09SDavid van Moolenbroek
538*00b67f09SDavid van Moolenbroek if (client->state == NS_CLIENTSTATE_INACTIVE) {
539*00b67f09SDavid van Moolenbroek INSIST(client->newstate == NS_CLIENTSTATE_FREED);
540*00b67f09SDavid van Moolenbroek /*
541*00b67f09SDavid van Moolenbroek * We are trying to free the client.
542*00b67f09SDavid van Moolenbroek *
543*00b67f09SDavid van Moolenbroek * When "shuttingdown" is true, either the task has received
544*00b67f09SDavid van Moolenbroek * its shutdown event or no shutdown event has ever been
545*00b67f09SDavid van Moolenbroek * set up. Thus, we have no outstanding shutdown
546*00b67f09SDavid van Moolenbroek * event at this point.
547*00b67f09SDavid van Moolenbroek */
548*00b67f09SDavid van Moolenbroek REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE);
549*00b67f09SDavid van Moolenbroek
550*00b67f09SDavid van Moolenbroek INSIST(client->recursionquota == NULL);
551*00b67f09SDavid van Moolenbroek INSIST(!ISC_QLINK_LINKED(client, ilink));
552*00b67f09SDavid van Moolenbroek
553*00b67f09SDavid van Moolenbroek if (manager != NULL) {
554*00b67f09SDavid van Moolenbroek LOCK(&manager->listlock);
555*00b67f09SDavid van Moolenbroek ISC_LIST_UNLINK(manager->clients, client, link);
556*00b67f09SDavid van Moolenbroek LOCK(&manager->lock);
557*00b67f09SDavid van Moolenbroek if (manager->exiting &&
558*00b67f09SDavid van Moolenbroek ISC_LIST_EMPTY(manager->clients))
559*00b67f09SDavid van Moolenbroek destroy_manager = ISC_TRUE;
560*00b67f09SDavid van Moolenbroek UNLOCK(&manager->lock);
561*00b67f09SDavid van Moolenbroek UNLOCK(&manager->listlock);
562*00b67f09SDavid van Moolenbroek }
563*00b67f09SDavid van Moolenbroek
564*00b67f09SDavid van Moolenbroek ns_query_free(client);
565*00b67f09SDavid van Moolenbroek isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
566*00b67f09SDavid van Moolenbroek isc_event_free((isc_event_t **)&client->sendevent);
567*00b67f09SDavid van Moolenbroek isc_event_free((isc_event_t **)&client->recvevent);
568*00b67f09SDavid van Moolenbroek isc_timer_detach(&client->timer);
569*00b67f09SDavid van Moolenbroek if (client->delaytimer != NULL)
570*00b67f09SDavid van Moolenbroek isc_timer_detach(&client->delaytimer);
571*00b67f09SDavid van Moolenbroek
572*00b67f09SDavid van Moolenbroek if (client->tcpbuf != NULL)
573*00b67f09SDavid van Moolenbroek isc_mem_put(client->mctx, client->tcpbuf,
574*00b67f09SDavid van Moolenbroek TCP_BUFFER_SIZE);
575*00b67f09SDavid van Moolenbroek if (client->opt != NULL) {
576*00b67f09SDavid van Moolenbroek INSIST(dns_rdataset_isassociated(client->opt));
577*00b67f09SDavid van Moolenbroek dns_rdataset_disassociate(client->opt);
578*00b67f09SDavid van Moolenbroek dns_message_puttemprdataset(client->message,
579*00b67f09SDavid van Moolenbroek &client->opt);
580*00b67f09SDavid van Moolenbroek }
581*00b67f09SDavid van Moolenbroek
582*00b67f09SDavid van Moolenbroek dns_message_destroy(&client->message);
583*00b67f09SDavid van Moolenbroek
584*00b67f09SDavid van Moolenbroek /*
585*00b67f09SDavid van Moolenbroek * Detaching the task must be done after unlinking from
586*00b67f09SDavid van Moolenbroek * the manager's lists because the manager accesses
587*00b67f09SDavid van Moolenbroek * client->task.
588*00b67f09SDavid van Moolenbroek */
589*00b67f09SDavid van Moolenbroek if (client->task != NULL)
590*00b67f09SDavid van Moolenbroek isc_task_detach(&client->task);
591*00b67f09SDavid van Moolenbroek
592*00b67f09SDavid van Moolenbroek CTRACE("free");
593*00b67f09SDavid van Moolenbroek client->magic = 0;
594*00b67f09SDavid van Moolenbroek
595*00b67f09SDavid van Moolenbroek /*
596*00b67f09SDavid van Moolenbroek * Check that there are no other external references to
597*00b67f09SDavid van Moolenbroek * the memory context.
598*00b67f09SDavid van Moolenbroek */
599*00b67f09SDavid van Moolenbroek if (ns_g_clienttest && isc_mem_references(client->mctx) != 1) {
600*00b67f09SDavid van Moolenbroek isc_mem_stats(client->mctx, stderr);
601*00b67f09SDavid van Moolenbroek INSIST(0);
602*00b67f09SDavid van Moolenbroek }
603*00b67f09SDavid van Moolenbroek
604*00b67f09SDavid van Moolenbroek /*
605*00b67f09SDavid van Moolenbroek * Destroy the fetchlock mutex that was created in
606*00b67f09SDavid van Moolenbroek * ns_query_init().
607*00b67f09SDavid van Moolenbroek */
608*00b67f09SDavid van Moolenbroek DESTROYLOCK(&client->query.fetchlock);
609*00b67f09SDavid van Moolenbroek
610*00b67f09SDavid van Moolenbroek isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
611*00b67f09SDavid van Moolenbroek }
612*00b67f09SDavid van Moolenbroek
613*00b67f09SDavid van Moolenbroek if (destroy_manager && manager != NULL)
614*00b67f09SDavid van Moolenbroek clientmgr_destroy(manager);
615*00b67f09SDavid van Moolenbroek
616*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
617*00b67f09SDavid van Moolenbroek }
618*00b67f09SDavid van Moolenbroek
619*00b67f09SDavid van Moolenbroek /*%
620*00b67f09SDavid van Moolenbroek * The client's task has received the client's control event
621*00b67f09SDavid van Moolenbroek * as part of the startup process.
622*00b67f09SDavid van Moolenbroek */
623*00b67f09SDavid van Moolenbroek static void
client_start(isc_task_t * task,isc_event_t * event)624*00b67f09SDavid van Moolenbroek client_start(isc_task_t *task, isc_event_t *event) {
625*00b67f09SDavid van Moolenbroek ns_client_t *client = (ns_client_t *) event->ev_arg;
626*00b67f09SDavid van Moolenbroek
627*00b67f09SDavid van Moolenbroek INSIST(task == client->task);
628*00b67f09SDavid van Moolenbroek
629*00b67f09SDavid van Moolenbroek UNUSED(task);
630*00b67f09SDavid van Moolenbroek
631*00b67f09SDavid van Moolenbroek INSIST(client->nctls == 1);
632*00b67f09SDavid van Moolenbroek client->nctls--;
633*00b67f09SDavid van Moolenbroek
634*00b67f09SDavid van Moolenbroek if (exit_check(client))
635*00b67f09SDavid van Moolenbroek return;
636*00b67f09SDavid van Moolenbroek
637*00b67f09SDavid van Moolenbroek if (TCP_CLIENT(client)) {
638*00b67f09SDavid van Moolenbroek client_accept(client);
639*00b67f09SDavid van Moolenbroek } else {
640*00b67f09SDavid van Moolenbroek client_udprecv(client);
641*00b67f09SDavid van Moolenbroek }
642*00b67f09SDavid van Moolenbroek }
643*00b67f09SDavid van Moolenbroek
644*00b67f09SDavid van Moolenbroek
645*00b67f09SDavid van Moolenbroek /*%
646*00b67f09SDavid van Moolenbroek * The client's task has received a shutdown event.
647*00b67f09SDavid van Moolenbroek */
648*00b67f09SDavid van Moolenbroek static void
client_shutdown(isc_task_t * task,isc_event_t * event)649*00b67f09SDavid van Moolenbroek client_shutdown(isc_task_t *task, isc_event_t *event) {
650*00b67f09SDavid van Moolenbroek ns_client_t *client;
651*00b67f09SDavid van Moolenbroek
652*00b67f09SDavid van Moolenbroek REQUIRE(event != NULL);
653*00b67f09SDavid van Moolenbroek REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
654*00b67f09SDavid van Moolenbroek client = event->ev_arg;
655*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
656*00b67f09SDavid van Moolenbroek REQUIRE(task == client->task);
657*00b67f09SDavid van Moolenbroek
658*00b67f09SDavid van Moolenbroek UNUSED(task);
659*00b67f09SDavid van Moolenbroek
660*00b67f09SDavid van Moolenbroek CTRACE("shutdown");
661*00b67f09SDavid van Moolenbroek
662*00b67f09SDavid van Moolenbroek isc_event_free(&event);
663*00b67f09SDavid van Moolenbroek
664*00b67f09SDavid van Moolenbroek if (client->shutdown != NULL) {
665*00b67f09SDavid van Moolenbroek (client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
666*00b67f09SDavid van Moolenbroek client->shutdown = NULL;
667*00b67f09SDavid van Moolenbroek client->shutdown_arg = NULL;
668*00b67f09SDavid van Moolenbroek }
669*00b67f09SDavid van Moolenbroek
670*00b67f09SDavid van Moolenbroek if (ISC_QLINK_LINKED(client, ilink))
671*00b67f09SDavid van Moolenbroek ISC_QUEUE_UNLINK(client->manager->inactive, client, ilink);
672*00b67f09SDavid van Moolenbroek
673*00b67f09SDavid van Moolenbroek client->newstate = NS_CLIENTSTATE_FREED;
674*00b67f09SDavid van Moolenbroek client->needshutdown = ISC_FALSE;
675*00b67f09SDavid van Moolenbroek (void)exit_check(client);
676*00b67f09SDavid van Moolenbroek }
677*00b67f09SDavid van Moolenbroek
678*00b67f09SDavid van Moolenbroek static void
ns_client_endrequest(ns_client_t * client)679*00b67f09SDavid van Moolenbroek ns_client_endrequest(ns_client_t *client) {
680*00b67f09SDavid van Moolenbroek INSIST(client->naccepts == 0);
681*00b67f09SDavid van Moolenbroek INSIST(client->nreads == 0);
682*00b67f09SDavid van Moolenbroek INSIST(client->nsends == 0);
683*00b67f09SDavid van Moolenbroek INSIST(client->nrecvs == 0);
684*00b67f09SDavid van Moolenbroek INSIST(client->nupdates == 0);
685*00b67f09SDavid van Moolenbroek INSIST(client->state == NS_CLIENTSTATE_WORKING ||
686*00b67f09SDavid van Moolenbroek client->state == NS_CLIENTSTATE_RECURSING);
687*00b67f09SDavid van Moolenbroek
688*00b67f09SDavid van Moolenbroek CTRACE("endrequest");
689*00b67f09SDavid van Moolenbroek
690*00b67f09SDavid van Moolenbroek if (client->next != NULL) {
691*00b67f09SDavid van Moolenbroek (client->next)(client);
692*00b67f09SDavid van Moolenbroek client->next = NULL;
693*00b67f09SDavid van Moolenbroek }
694*00b67f09SDavid van Moolenbroek
695*00b67f09SDavid van Moolenbroek if (client->view != NULL)
696*00b67f09SDavid van Moolenbroek dns_view_detach(&client->view);
697*00b67f09SDavid van Moolenbroek if (client->opt != NULL) {
698*00b67f09SDavid van Moolenbroek INSIST(dns_rdataset_isassociated(client->opt));
699*00b67f09SDavid van Moolenbroek dns_rdataset_disassociate(client->opt);
700*00b67f09SDavid van Moolenbroek dns_message_puttemprdataset(client->message, &client->opt);
701*00b67f09SDavid van Moolenbroek }
702*00b67f09SDavid van Moolenbroek
703*00b67f09SDavid van Moolenbroek client->signer = NULL;
704*00b67f09SDavid van Moolenbroek client->udpsize = 512;
705*00b67f09SDavid van Moolenbroek client->extflags = 0;
706*00b67f09SDavid van Moolenbroek client->ednsversion = -1;
707*00b67f09SDavid van Moolenbroek dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
708*00b67f09SDavid van Moolenbroek
709*00b67f09SDavid van Moolenbroek if (client->recursionquota != NULL) {
710*00b67f09SDavid van Moolenbroek isc_quota_detach(&client->recursionquota);
711*00b67f09SDavid van Moolenbroek isc_stats_decrement(ns_g_server->nsstats,
712*00b67f09SDavid van Moolenbroek dns_nsstatscounter_recursclients);
713*00b67f09SDavid van Moolenbroek }
714*00b67f09SDavid van Moolenbroek
715*00b67f09SDavid van Moolenbroek /*
716*00b67f09SDavid van Moolenbroek * Clear all client attributes that are specific to
717*00b67f09SDavid van Moolenbroek * the request; that's all except the TCP flag.
718*00b67f09SDavid van Moolenbroek */
719*00b67f09SDavid van Moolenbroek client->attributes &= NS_CLIENTATTR_TCP;
720*00b67f09SDavid van Moolenbroek }
721*00b67f09SDavid van Moolenbroek
722*00b67f09SDavid van Moolenbroek void
ns_client_next(ns_client_t * client,isc_result_t result)723*00b67f09SDavid van Moolenbroek ns_client_next(ns_client_t *client, isc_result_t result) {
724*00b67f09SDavid van Moolenbroek int newstate;
725*00b67f09SDavid van Moolenbroek
726*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
727*00b67f09SDavid van Moolenbroek REQUIRE(client->state == NS_CLIENTSTATE_WORKING ||
728*00b67f09SDavid van Moolenbroek client->state == NS_CLIENTSTATE_RECURSING ||
729*00b67f09SDavid van Moolenbroek client->state == NS_CLIENTSTATE_READING);
730*00b67f09SDavid van Moolenbroek
731*00b67f09SDavid van Moolenbroek CTRACE("next");
732*00b67f09SDavid van Moolenbroek
733*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
734*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
735*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
736*00b67f09SDavid van Moolenbroek "request failed: %s", isc_result_totext(result));
737*00b67f09SDavid van Moolenbroek
738*00b67f09SDavid van Moolenbroek /*
739*00b67f09SDavid van Moolenbroek * An error processing a TCP request may have left
740*00b67f09SDavid van Moolenbroek * the connection out of sync. To be safe, we always
741*00b67f09SDavid van Moolenbroek * sever the connection when result != ISC_R_SUCCESS.
742*00b67f09SDavid van Moolenbroek */
743*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS && TCP_CLIENT(client))
744*00b67f09SDavid van Moolenbroek newstate = NS_CLIENTSTATE_READING;
745*00b67f09SDavid van Moolenbroek else
746*00b67f09SDavid van Moolenbroek newstate = NS_CLIENTSTATE_READY;
747*00b67f09SDavid van Moolenbroek
748*00b67f09SDavid van Moolenbroek if (client->newstate > newstate)
749*00b67f09SDavid van Moolenbroek client->newstate = newstate;
750*00b67f09SDavid van Moolenbroek (void)exit_check(client);
751*00b67f09SDavid van Moolenbroek }
752*00b67f09SDavid van Moolenbroek
753*00b67f09SDavid van Moolenbroek
754*00b67f09SDavid van Moolenbroek static void
client_senddone(isc_task_t * task,isc_event_t * event)755*00b67f09SDavid van Moolenbroek client_senddone(isc_task_t *task, isc_event_t *event) {
756*00b67f09SDavid van Moolenbroek ns_client_t *client;
757*00b67f09SDavid van Moolenbroek isc_socketevent_t *sevent = (isc_socketevent_t *) event;
758*00b67f09SDavid van Moolenbroek
759*00b67f09SDavid van Moolenbroek REQUIRE(sevent != NULL);
760*00b67f09SDavid van Moolenbroek REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
761*00b67f09SDavid van Moolenbroek client = sevent->ev_arg;
762*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
763*00b67f09SDavid van Moolenbroek REQUIRE(task == client->task);
764*00b67f09SDavid van Moolenbroek REQUIRE(sevent == client->sendevent);
765*00b67f09SDavid van Moolenbroek
766*00b67f09SDavid van Moolenbroek UNUSED(task);
767*00b67f09SDavid van Moolenbroek
768*00b67f09SDavid van Moolenbroek CTRACE("senddone");
769*00b67f09SDavid van Moolenbroek
770*00b67f09SDavid van Moolenbroek if (sevent->result != ISC_R_SUCCESS)
771*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
772*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
773*00b67f09SDavid van Moolenbroek "error sending response: %s",
774*00b67f09SDavid van Moolenbroek isc_result_totext(sevent->result));
775*00b67f09SDavid van Moolenbroek
776*00b67f09SDavid van Moolenbroek INSIST(client->nsends > 0);
777*00b67f09SDavid van Moolenbroek client->nsends--;
778*00b67f09SDavid van Moolenbroek
779*00b67f09SDavid van Moolenbroek if (client->tcpbuf != NULL) {
780*00b67f09SDavid van Moolenbroek INSIST(TCP_CLIENT(client));
781*00b67f09SDavid van Moolenbroek isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
782*00b67f09SDavid van Moolenbroek client->tcpbuf = NULL;
783*00b67f09SDavid van Moolenbroek }
784*00b67f09SDavid van Moolenbroek
785*00b67f09SDavid van Moolenbroek ns_client_next(client, ISC_R_SUCCESS);
786*00b67f09SDavid van Moolenbroek }
787*00b67f09SDavid van Moolenbroek
788*00b67f09SDavid van Moolenbroek /*%
789*00b67f09SDavid van Moolenbroek * We only want to fail with ISC_R_NOSPACE when called from
790*00b67f09SDavid van Moolenbroek * ns_client_sendraw() and not when called from ns_client_send(),
791*00b67f09SDavid van Moolenbroek * tcpbuffer is NULL when called from ns_client_sendraw() and
792*00b67f09SDavid van Moolenbroek * length != 0. tcpbuffer != NULL when called from ns_client_send()
793*00b67f09SDavid van Moolenbroek * and length == 0.
794*00b67f09SDavid van Moolenbroek */
795*00b67f09SDavid van Moolenbroek
796*00b67f09SDavid van Moolenbroek static isc_result_t
client_allocsendbuf(ns_client_t * client,isc_buffer_t * buffer,isc_buffer_t * tcpbuffer,isc_uint32_t length,unsigned char * sendbuf,unsigned char ** datap)797*00b67f09SDavid van Moolenbroek client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
798*00b67f09SDavid van Moolenbroek isc_buffer_t *tcpbuffer, isc_uint32_t length,
799*00b67f09SDavid van Moolenbroek unsigned char *sendbuf, unsigned char **datap)
800*00b67f09SDavid van Moolenbroek {
801*00b67f09SDavid van Moolenbroek unsigned char *data;
802*00b67f09SDavid van Moolenbroek isc_uint32_t bufsize;
803*00b67f09SDavid van Moolenbroek isc_result_t result;
804*00b67f09SDavid van Moolenbroek
805*00b67f09SDavid van Moolenbroek INSIST(datap != NULL);
806*00b67f09SDavid van Moolenbroek INSIST((tcpbuffer == NULL && length != 0) ||
807*00b67f09SDavid van Moolenbroek (tcpbuffer != NULL && length == 0));
808*00b67f09SDavid van Moolenbroek
809*00b67f09SDavid van Moolenbroek if (TCP_CLIENT(client)) {
810*00b67f09SDavid van Moolenbroek INSIST(client->tcpbuf == NULL);
811*00b67f09SDavid van Moolenbroek if (length + 2 > TCP_BUFFER_SIZE) {
812*00b67f09SDavid van Moolenbroek result = ISC_R_NOSPACE;
813*00b67f09SDavid van Moolenbroek goto done;
814*00b67f09SDavid van Moolenbroek }
815*00b67f09SDavid van Moolenbroek client->tcpbuf = isc_mem_get(client->mctx, TCP_BUFFER_SIZE);
816*00b67f09SDavid van Moolenbroek if (client->tcpbuf == NULL) {
817*00b67f09SDavid van Moolenbroek result = ISC_R_NOMEMORY;
818*00b67f09SDavid van Moolenbroek goto done;
819*00b67f09SDavid van Moolenbroek }
820*00b67f09SDavid van Moolenbroek data = client->tcpbuf;
821*00b67f09SDavid van Moolenbroek if (tcpbuffer != NULL) {
822*00b67f09SDavid van Moolenbroek isc_buffer_init(tcpbuffer, data, TCP_BUFFER_SIZE);
823*00b67f09SDavid van Moolenbroek isc_buffer_init(buffer, data + 2, TCP_BUFFER_SIZE - 2);
824*00b67f09SDavid van Moolenbroek } else {
825*00b67f09SDavid van Moolenbroek isc_buffer_init(buffer, data, TCP_BUFFER_SIZE);
826*00b67f09SDavid van Moolenbroek INSIST(length <= 0xffff);
827*00b67f09SDavid van Moolenbroek isc_buffer_putuint16(buffer, (isc_uint16_t)length);
828*00b67f09SDavid van Moolenbroek }
829*00b67f09SDavid van Moolenbroek } else {
830*00b67f09SDavid van Moolenbroek data = sendbuf;
831*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
832*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_HAVESIT) == 0) {
833*00b67f09SDavid van Moolenbroek if (client->view != NULL)
834*00b67f09SDavid van Moolenbroek bufsize = client->view->situdp;
835*00b67f09SDavid van Moolenbroek else
836*00b67f09SDavid van Moolenbroek bufsize = 512;
837*00b67f09SDavid van Moolenbroek } else
838*00b67f09SDavid van Moolenbroek bufsize = client->udpsize;
839*00b67f09SDavid van Moolenbroek if (bufsize > client->udpsize)
840*00b67f09SDavid van Moolenbroek bufsize = client->udpsize;
841*00b67f09SDavid van Moolenbroek if (bufsize > SEND_BUFFER_SIZE)
842*00b67f09SDavid van Moolenbroek bufsize = SEND_BUFFER_SIZE;
843*00b67f09SDavid van Moolenbroek #else
844*00b67f09SDavid van Moolenbroek if (client->udpsize < SEND_BUFFER_SIZE)
845*00b67f09SDavid van Moolenbroek bufsize = client->udpsize;
846*00b67f09SDavid van Moolenbroek else
847*00b67f09SDavid van Moolenbroek bufsize = SEND_BUFFER_SIZE;
848*00b67f09SDavid van Moolenbroek #endif
849*00b67f09SDavid van Moolenbroek if (length > bufsize) {
850*00b67f09SDavid van Moolenbroek result = ISC_R_NOSPACE;
851*00b67f09SDavid van Moolenbroek goto done;
852*00b67f09SDavid van Moolenbroek }
853*00b67f09SDavid van Moolenbroek isc_buffer_init(buffer, data, bufsize);
854*00b67f09SDavid van Moolenbroek }
855*00b67f09SDavid van Moolenbroek *datap = data;
856*00b67f09SDavid van Moolenbroek result = ISC_R_SUCCESS;
857*00b67f09SDavid van Moolenbroek
858*00b67f09SDavid van Moolenbroek done:
859*00b67f09SDavid van Moolenbroek return (result);
860*00b67f09SDavid van Moolenbroek }
861*00b67f09SDavid van Moolenbroek
862*00b67f09SDavid van Moolenbroek static isc_result_t
client_sendpkg(ns_client_t * client,isc_buffer_t * buffer)863*00b67f09SDavid van Moolenbroek client_sendpkg(ns_client_t *client, isc_buffer_t *buffer) {
864*00b67f09SDavid van Moolenbroek struct in6_pktinfo *pktinfo;
865*00b67f09SDavid van Moolenbroek isc_result_t result;
866*00b67f09SDavid van Moolenbroek isc_region_t r;
867*00b67f09SDavid van Moolenbroek isc_sockaddr_t *address;
868*00b67f09SDavid van Moolenbroek isc_socket_t *socket;
869*00b67f09SDavid van Moolenbroek isc_netaddr_t netaddr;
870*00b67f09SDavid van Moolenbroek int match;
871*00b67f09SDavid van Moolenbroek unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
872*00b67f09SDavid van Moolenbroek isc_dscp_t dispdscp = -1;
873*00b67f09SDavid van Moolenbroek
874*00b67f09SDavid van Moolenbroek if (TCP_CLIENT(client)) {
875*00b67f09SDavid van Moolenbroek socket = client->tcpsocket;
876*00b67f09SDavid van Moolenbroek address = NULL;
877*00b67f09SDavid van Moolenbroek } else {
878*00b67f09SDavid van Moolenbroek socket = client->udpsocket;
879*00b67f09SDavid van Moolenbroek address = &client->peeraddr;
880*00b67f09SDavid van Moolenbroek
881*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
882*00b67f09SDavid van Moolenbroek if (ns_g_server->blackholeacl != NULL &&
883*00b67f09SDavid van Moolenbroek dns_acl_match(&netaddr, NULL,
884*00b67f09SDavid van Moolenbroek ns_g_server->blackholeacl,
885*00b67f09SDavid van Moolenbroek &ns_g_server->aclenv,
886*00b67f09SDavid van Moolenbroek &match, NULL) == ISC_R_SUCCESS &&
887*00b67f09SDavid van Moolenbroek match > 0)
888*00b67f09SDavid van Moolenbroek return (DNS_R_BLACKHOLED);
889*00b67f09SDavid van Moolenbroek sockflags |= ISC_SOCKFLAG_NORETRY;
890*00b67f09SDavid van Moolenbroek }
891*00b67f09SDavid van Moolenbroek
892*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0 &&
893*00b67f09SDavid van Moolenbroek (client->attributes & NS_CLIENTATTR_MULTICAST) == 0)
894*00b67f09SDavid van Moolenbroek pktinfo = &client->pktinfo;
895*00b67f09SDavid van Moolenbroek else
896*00b67f09SDavid van Moolenbroek pktinfo = NULL;
897*00b67f09SDavid van Moolenbroek
898*00b67f09SDavid van Moolenbroek if (client->dispatch != NULL) {
899*00b67f09SDavid van Moolenbroek dispdscp = dns_dispatch_getdscp(client->dispatch);
900*00b67f09SDavid van Moolenbroek if (dispdscp != -1)
901*00b67f09SDavid van Moolenbroek client->dscp = dispdscp;
902*00b67f09SDavid van Moolenbroek }
903*00b67f09SDavid van Moolenbroek
904*00b67f09SDavid van Moolenbroek if (client->dscp == -1) {
905*00b67f09SDavid van Moolenbroek client->sendevent->attributes &= ~ISC_SOCKEVENTATTR_DSCP;
906*00b67f09SDavid van Moolenbroek client->sendevent->dscp = 0;
907*00b67f09SDavid van Moolenbroek } else {
908*00b67f09SDavid van Moolenbroek client->sendevent->attributes |= ISC_SOCKEVENTATTR_DSCP;
909*00b67f09SDavid van Moolenbroek client->sendevent->dscp = client->dscp;
910*00b67f09SDavid van Moolenbroek }
911*00b67f09SDavid van Moolenbroek
912*00b67f09SDavid van Moolenbroek isc_buffer_usedregion(buffer, &r);
913*00b67f09SDavid van Moolenbroek
914*00b67f09SDavid van Moolenbroek CTRACE("sendto");
915*00b67f09SDavid van Moolenbroek
916*00b67f09SDavid van Moolenbroek result = isc_socket_sendto2(socket, &r, client->task,
917*00b67f09SDavid van Moolenbroek address, pktinfo,
918*00b67f09SDavid van Moolenbroek client->sendevent, sockflags);
919*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS || result == ISC_R_INPROGRESS) {
920*00b67f09SDavid van Moolenbroek client->nsends++;
921*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
922*00b67f09SDavid van Moolenbroek client_senddone(client->task,
923*00b67f09SDavid van Moolenbroek (isc_event_t *)client->sendevent);
924*00b67f09SDavid van Moolenbroek result = ISC_R_SUCCESS;
925*00b67f09SDavid van Moolenbroek }
926*00b67f09SDavid van Moolenbroek return (result);
927*00b67f09SDavid van Moolenbroek }
928*00b67f09SDavid van Moolenbroek
929*00b67f09SDavid van Moolenbroek void
ns_client_sendraw(ns_client_t * client,dns_message_t * message)930*00b67f09SDavid van Moolenbroek ns_client_sendraw(ns_client_t *client, dns_message_t *message) {
931*00b67f09SDavid van Moolenbroek isc_result_t result;
932*00b67f09SDavid van Moolenbroek unsigned char *data;
933*00b67f09SDavid van Moolenbroek isc_buffer_t buffer;
934*00b67f09SDavid van Moolenbroek isc_region_t r;
935*00b67f09SDavid van Moolenbroek isc_region_t *mr;
936*00b67f09SDavid van Moolenbroek unsigned char sendbuf[SEND_BUFFER_SIZE];
937*00b67f09SDavid van Moolenbroek
938*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
939*00b67f09SDavid van Moolenbroek
940*00b67f09SDavid van Moolenbroek CTRACE("sendraw");
941*00b67f09SDavid van Moolenbroek
942*00b67f09SDavid van Moolenbroek mr = dns_message_getrawmessage(message);
943*00b67f09SDavid van Moolenbroek if (mr == NULL) {
944*00b67f09SDavid van Moolenbroek result = ISC_R_UNEXPECTEDEND;
945*00b67f09SDavid van Moolenbroek goto done;
946*00b67f09SDavid van Moolenbroek }
947*00b67f09SDavid van Moolenbroek
948*00b67f09SDavid van Moolenbroek result = client_allocsendbuf(client, &buffer, NULL, mr->length,
949*00b67f09SDavid van Moolenbroek sendbuf, &data);
950*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
951*00b67f09SDavid van Moolenbroek goto done;
952*00b67f09SDavid van Moolenbroek
953*00b67f09SDavid van Moolenbroek /*
954*00b67f09SDavid van Moolenbroek * Copy message to buffer and fixup id.
955*00b67f09SDavid van Moolenbroek */
956*00b67f09SDavid van Moolenbroek isc_buffer_availableregion(&buffer, &r);
957*00b67f09SDavid van Moolenbroek result = isc_buffer_copyregion(&buffer, mr);
958*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
959*00b67f09SDavid van Moolenbroek goto done;
960*00b67f09SDavid van Moolenbroek r.base[0] = (client->message->id >> 8) & 0xff;
961*00b67f09SDavid van Moolenbroek r.base[1] = client->message->id & 0xff;
962*00b67f09SDavid van Moolenbroek
963*00b67f09SDavid van Moolenbroek result = client_sendpkg(client, &buffer);
964*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
965*00b67f09SDavid van Moolenbroek return;
966*00b67f09SDavid van Moolenbroek
967*00b67f09SDavid van Moolenbroek done:
968*00b67f09SDavid van Moolenbroek if (client->tcpbuf != NULL) {
969*00b67f09SDavid van Moolenbroek isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
970*00b67f09SDavid van Moolenbroek client->tcpbuf = NULL;
971*00b67f09SDavid van Moolenbroek }
972*00b67f09SDavid van Moolenbroek ns_client_next(client, result);
973*00b67f09SDavid van Moolenbroek }
974*00b67f09SDavid van Moolenbroek
975*00b67f09SDavid van Moolenbroek static void
client_send(ns_client_t * client)976*00b67f09SDavid van Moolenbroek client_send(ns_client_t *client) {
977*00b67f09SDavid van Moolenbroek isc_result_t result;
978*00b67f09SDavid van Moolenbroek unsigned char *data;
979*00b67f09SDavid van Moolenbroek isc_buffer_t buffer;
980*00b67f09SDavid van Moolenbroek isc_buffer_t tcpbuffer;
981*00b67f09SDavid van Moolenbroek isc_region_t r;
982*00b67f09SDavid van Moolenbroek dns_compress_t cctx;
983*00b67f09SDavid van Moolenbroek isc_boolean_t cleanup_cctx = ISC_FALSE;
984*00b67f09SDavid van Moolenbroek unsigned char sendbuf[SEND_BUFFER_SIZE];
985*00b67f09SDavid van Moolenbroek unsigned int render_opts;
986*00b67f09SDavid van Moolenbroek unsigned int preferred_glue;
987*00b67f09SDavid van Moolenbroek isc_boolean_t opt_included = ISC_FALSE;
988*00b67f09SDavid van Moolenbroek
989*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
990*00b67f09SDavid van Moolenbroek
991*00b67f09SDavid van Moolenbroek CTRACE("send");
992*00b67f09SDavid van Moolenbroek
993*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_RA) != 0)
994*00b67f09SDavid van Moolenbroek client->message->flags |= DNS_MESSAGEFLAG_RA;
995*00b67f09SDavid van Moolenbroek
996*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0)
997*00b67f09SDavid van Moolenbroek render_opts = 0;
998*00b67f09SDavid van Moolenbroek else
999*00b67f09SDavid van Moolenbroek render_opts = DNS_MESSAGERENDER_OMITDNSSEC;
1000*00b67f09SDavid van Moolenbroek
1001*00b67f09SDavid van Moolenbroek preferred_glue = 0;
1002*00b67f09SDavid van Moolenbroek if (client->view != NULL) {
1003*00b67f09SDavid van Moolenbroek if (client->view->preferred_glue == dns_rdatatype_a)
1004*00b67f09SDavid van Moolenbroek preferred_glue = DNS_MESSAGERENDER_PREFER_A;
1005*00b67f09SDavid van Moolenbroek else if (client->view->preferred_glue == dns_rdatatype_aaaa)
1006*00b67f09SDavid van Moolenbroek preferred_glue = DNS_MESSAGERENDER_PREFER_AAAA;
1007*00b67f09SDavid van Moolenbroek }
1008*00b67f09SDavid van Moolenbroek
1009*00b67f09SDavid van Moolenbroek #ifdef ALLOW_FILTER_AAAA
1010*00b67f09SDavid van Moolenbroek /*
1011*00b67f09SDavid van Moolenbroek * filter-aaaa-on-v4 yes or break-dnssec option to suppress
1012*00b67f09SDavid van Moolenbroek * AAAA records.
1013*00b67f09SDavid van Moolenbroek *
1014*00b67f09SDavid van Moolenbroek * We already know that request came via IPv4,
1015*00b67f09SDavid van Moolenbroek * that we have both AAAA and A records,
1016*00b67f09SDavid van Moolenbroek * and that we either have no signatures that the client wants
1017*00b67f09SDavid van Moolenbroek * or we are supposed to break DNSSEC.
1018*00b67f09SDavid van Moolenbroek *
1019*00b67f09SDavid van Moolenbroek * Override preferred glue if necessary.
1020*00b67f09SDavid van Moolenbroek */
1021*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_FILTER_AAAA) != 0) {
1022*00b67f09SDavid van Moolenbroek render_opts |= DNS_MESSAGERENDER_FILTER_AAAA;
1023*00b67f09SDavid van Moolenbroek if (preferred_glue == DNS_MESSAGERENDER_PREFER_AAAA)
1024*00b67f09SDavid van Moolenbroek preferred_glue = DNS_MESSAGERENDER_PREFER_A;
1025*00b67f09SDavid van Moolenbroek }
1026*00b67f09SDavid van Moolenbroek #endif
1027*00b67f09SDavid van Moolenbroek
1028*00b67f09SDavid van Moolenbroek /*
1029*00b67f09SDavid van Moolenbroek * Create an OPT for our reply.
1030*00b67f09SDavid van Moolenbroek */
1031*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_WANTOPT) != 0) {
1032*00b67f09SDavid van Moolenbroek result = ns_client_addopt(client, client->message,
1033*00b67f09SDavid van Moolenbroek &client->opt);
1034*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1035*00b67f09SDavid van Moolenbroek goto done;
1036*00b67f09SDavid van Moolenbroek }
1037*00b67f09SDavid van Moolenbroek
1038*00b67f09SDavid van Moolenbroek /*
1039*00b67f09SDavid van Moolenbroek * XXXRTH The following doesn't deal with TCP buffer resizing.
1040*00b67f09SDavid van Moolenbroek */
1041*00b67f09SDavid van Moolenbroek result = client_allocsendbuf(client, &buffer, &tcpbuffer, 0,
1042*00b67f09SDavid van Moolenbroek sendbuf, &data);
1043*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1044*00b67f09SDavid van Moolenbroek goto done;
1045*00b67f09SDavid van Moolenbroek
1046*00b67f09SDavid van Moolenbroek result = dns_compress_init(&cctx, -1, client->mctx);
1047*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1048*00b67f09SDavid van Moolenbroek goto done;
1049*00b67f09SDavid van Moolenbroek if (client->peeraddr_valid && client->view != NULL) {
1050*00b67f09SDavid van Moolenbroek isc_netaddr_t netaddr;
1051*00b67f09SDavid van Moolenbroek dns_name_t *name = NULL;
1052*00b67f09SDavid van Moolenbroek
1053*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
1054*00b67f09SDavid van Moolenbroek if (client->message->tsigkey != NULL)
1055*00b67f09SDavid van Moolenbroek name = &client->message->tsigkey->name;
1056*00b67f09SDavid van Moolenbroek if (client->view->nocasecompress == NULL ||
1057*00b67f09SDavid van Moolenbroek !allowed(&netaddr, name, client->view->nocasecompress))
1058*00b67f09SDavid van Moolenbroek {
1059*00b67f09SDavid van Moolenbroek dns_compress_setsensitive(&cctx, ISC_TRUE);
1060*00b67f09SDavid van Moolenbroek }
1061*00b67f09SDavid van Moolenbroek }
1062*00b67f09SDavid van Moolenbroek cleanup_cctx = ISC_TRUE;
1063*00b67f09SDavid van Moolenbroek
1064*00b67f09SDavid van Moolenbroek result = dns_message_renderbegin(client->message, &cctx, &buffer);
1065*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1066*00b67f09SDavid van Moolenbroek goto done;
1067*00b67f09SDavid van Moolenbroek
1068*00b67f09SDavid van Moolenbroek if (client->opt != NULL) {
1069*00b67f09SDavid van Moolenbroek result = dns_message_setopt(client->message, client->opt);
1070*00b67f09SDavid van Moolenbroek opt_included = ISC_TRUE;
1071*00b67f09SDavid van Moolenbroek client->opt = NULL;
1072*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1073*00b67f09SDavid van Moolenbroek goto done;
1074*00b67f09SDavid van Moolenbroek }
1075*00b67f09SDavid van Moolenbroek result = dns_message_rendersection(client->message,
1076*00b67f09SDavid van Moolenbroek DNS_SECTION_QUESTION, 0);
1077*00b67f09SDavid van Moolenbroek if (result == ISC_R_NOSPACE) {
1078*00b67f09SDavid van Moolenbroek client->message->flags |= DNS_MESSAGEFLAG_TC;
1079*00b67f09SDavid van Moolenbroek goto renderend;
1080*00b67f09SDavid van Moolenbroek }
1081*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1082*00b67f09SDavid van Moolenbroek goto done;
1083*00b67f09SDavid van Moolenbroek /*
1084*00b67f09SDavid van Moolenbroek * Stop after the question if TC was set for rate limiting.
1085*00b67f09SDavid van Moolenbroek */
1086*00b67f09SDavid van Moolenbroek if ((client->message->flags & DNS_MESSAGEFLAG_TC) != 0)
1087*00b67f09SDavid van Moolenbroek goto renderend;
1088*00b67f09SDavid van Moolenbroek result = dns_message_rendersection(client->message,
1089*00b67f09SDavid van Moolenbroek DNS_SECTION_ANSWER,
1090*00b67f09SDavid van Moolenbroek DNS_MESSAGERENDER_PARTIAL |
1091*00b67f09SDavid van Moolenbroek render_opts);
1092*00b67f09SDavid van Moolenbroek if (result == ISC_R_NOSPACE) {
1093*00b67f09SDavid van Moolenbroek client->message->flags |= DNS_MESSAGEFLAG_TC;
1094*00b67f09SDavid van Moolenbroek goto renderend;
1095*00b67f09SDavid van Moolenbroek }
1096*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1097*00b67f09SDavid van Moolenbroek goto done;
1098*00b67f09SDavid van Moolenbroek result = dns_message_rendersection(client->message,
1099*00b67f09SDavid van Moolenbroek DNS_SECTION_AUTHORITY,
1100*00b67f09SDavid van Moolenbroek DNS_MESSAGERENDER_PARTIAL |
1101*00b67f09SDavid van Moolenbroek render_opts);
1102*00b67f09SDavid van Moolenbroek if (result == ISC_R_NOSPACE) {
1103*00b67f09SDavid van Moolenbroek client->message->flags |= DNS_MESSAGEFLAG_TC;
1104*00b67f09SDavid van Moolenbroek goto renderend;
1105*00b67f09SDavid van Moolenbroek }
1106*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1107*00b67f09SDavid van Moolenbroek goto done;
1108*00b67f09SDavid van Moolenbroek result = dns_message_rendersection(client->message,
1109*00b67f09SDavid van Moolenbroek DNS_SECTION_ADDITIONAL,
1110*00b67f09SDavid van Moolenbroek preferred_glue | render_opts);
1111*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
1112*00b67f09SDavid van Moolenbroek goto done;
1113*00b67f09SDavid van Moolenbroek renderend:
1114*00b67f09SDavid van Moolenbroek result = dns_message_renderend(client->message);
1115*00b67f09SDavid van Moolenbroek
1116*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1117*00b67f09SDavid van Moolenbroek goto done;
1118*00b67f09SDavid van Moolenbroek
1119*00b67f09SDavid van Moolenbroek if (cleanup_cctx) {
1120*00b67f09SDavid van Moolenbroek dns_compress_invalidate(&cctx);
1121*00b67f09SDavid van Moolenbroek cleanup_cctx = ISC_FALSE;
1122*00b67f09SDavid van Moolenbroek }
1123*00b67f09SDavid van Moolenbroek
1124*00b67f09SDavid van Moolenbroek if (TCP_CLIENT(client)) {
1125*00b67f09SDavid van Moolenbroek isc_buffer_usedregion(&buffer, &r);
1126*00b67f09SDavid van Moolenbroek isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t) r.length);
1127*00b67f09SDavid van Moolenbroek isc_buffer_add(&tcpbuffer, r.length);
1128*00b67f09SDavid van Moolenbroek result = client_sendpkg(client, &tcpbuffer);
1129*00b67f09SDavid van Moolenbroek } else
1130*00b67f09SDavid van Moolenbroek result = client_sendpkg(client, &buffer);
1131*00b67f09SDavid van Moolenbroek
1132*00b67f09SDavid van Moolenbroek /* update statistics (XXXJT: is it okay to access message->xxxkey?) */
1133*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_response);
1134*00b67f09SDavid van Moolenbroek if (opt_included) {
1135*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1136*00b67f09SDavid van Moolenbroek dns_nsstatscounter_edns0out);
1137*00b67f09SDavid van Moolenbroek }
1138*00b67f09SDavid van Moolenbroek if (client->message->tsigkey != NULL) {
1139*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1140*00b67f09SDavid van Moolenbroek dns_nsstatscounter_tsigout);
1141*00b67f09SDavid van Moolenbroek }
1142*00b67f09SDavid van Moolenbroek if (client->message->sig0key != NULL) {
1143*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1144*00b67f09SDavid van Moolenbroek dns_nsstatscounter_sig0out);
1145*00b67f09SDavid van Moolenbroek }
1146*00b67f09SDavid van Moolenbroek if ((client->message->flags & DNS_MESSAGEFLAG_TC) != 0)
1147*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1148*00b67f09SDavid van Moolenbroek dns_nsstatscounter_truncatedresp);
1149*00b67f09SDavid van Moolenbroek
1150*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
1151*00b67f09SDavid van Moolenbroek return;
1152*00b67f09SDavid van Moolenbroek
1153*00b67f09SDavid van Moolenbroek done:
1154*00b67f09SDavid van Moolenbroek if (client->tcpbuf != NULL) {
1155*00b67f09SDavid van Moolenbroek isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
1156*00b67f09SDavid van Moolenbroek client->tcpbuf = NULL;
1157*00b67f09SDavid van Moolenbroek }
1158*00b67f09SDavid van Moolenbroek
1159*00b67f09SDavid van Moolenbroek if (cleanup_cctx)
1160*00b67f09SDavid van Moolenbroek dns_compress_invalidate(&cctx);
1161*00b67f09SDavid van Moolenbroek
1162*00b67f09SDavid van Moolenbroek ns_client_next(client, result);
1163*00b67f09SDavid van Moolenbroek }
1164*00b67f09SDavid van Moolenbroek
1165*00b67f09SDavid van Moolenbroek /*
1166*00b67f09SDavid van Moolenbroek * Completes the sending of a delayed client response.
1167*00b67f09SDavid van Moolenbroek */
1168*00b67f09SDavid van Moolenbroek static void
client_delay(isc_task_t * task,isc_event_t * event)1169*00b67f09SDavid van Moolenbroek client_delay(isc_task_t *task, isc_event_t *event) {
1170*00b67f09SDavid van Moolenbroek ns_client_t *client;
1171*00b67f09SDavid van Moolenbroek
1172*00b67f09SDavid van Moolenbroek REQUIRE(event != NULL);
1173*00b67f09SDavid van Moolenbroek REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
1174*00b67f09SDavid van Moolenbroek event->ev_type == ISC_TIMEREVENT_IDLE);
1175*00b67f09SDavid van Moolenbroek client = event->ev_arg;
1176*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
1177*00b67f09SDavid van Moolenbroek REQUIRE(task == client->task);
1178*00b67f09SDavid van Moolenbroek REQUIRE(client->delaytimer != NULL);
1179*00b67f09SDavid van Moolenbroek
1180*00b67f09SDavid van Moolenbroek UNUSED(task);
1181*00b67f09SDavid van Moolenbroek
1182*00b67f09SDavid van Moolenbroek CTRACE("client_delay");
1183*00b67f09SDavid van Moolenbroek
1184*00b67f09SDavid van Moolenbroek isc_event_free(&event);
1185*00b67f09SDavid van Moolenbroek isc_timer_detach(&client->delaytimer);
1186*00b67f09SDavid van Moolenbroek
1187*00b67f09SDavid van Moolenbroek client_send(client);
1188*00b67f09SDavid van Moolenbroek ns_client_detach(&client);
1189*00b67f09SDavid van Moolenbroek }
1190*00b67f09SDavid van Moolenbroek
1191*00b67f09SDavid van Moolenbroek void
ns_client_send(ns_client_t * client)1192*00b67f09SDavid van Moolenbroek ns_client_send(ns_client_t *client) {
1193*00b67f09SDavid van Moolenbroek
1194*00b67f09SDavid van Moolenbroek /*
1195*00b67f09SDavid van Moolenbroek * Delay the response by ns_g_delay ms.
1196*00b67f09SDavid van Moolenbroek */
1197*00b67f09SDavid van Moolenbroek if (ns_g_delay != 0) {
1198*00b67f09SDavid van Moolenbroek ns_client_t *dummy = NULL;
1199*00b67f09SDavid van Moolenbroek isc_result_t result;
1200*00b67f09SDavid van Moolenbroek isc_interval_t interval;
1201*00b67f09SDavid van Moolenbroek
1202*00b67f09SDavid van Moolenbroek /*
1203*00b67f09SDavid van Moolenbroek * Replace ourselves if we have not already been replaced.
1204*00b67f09SDavid van Moolenbroek */
1205*00b67f09SDavid van Moolenbroek if (!client->mortal) {
1206*00b67f09SDavid van Moolenbroek result = ns_client_replace(client);
1207*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1208*00b67f09SDavid van Moolenbroek goto nodelay;
1209*00b67f09SDavid van Moolenbroek }
1210*00b67f09SDavid van Moolenbroek
1211*00b67f09SDavid van Moolenbroek ns_client_attach(client, &dummy);
1212*00b67f09SDavid van Moolenbroek if (ns_g_delay >= 1000)
1213*00b67f09SDavid van Moolenbroek isc_interval_set(&interval, ns_g_delay / 1000,
1214*00b67f09SDavid van Moolenbroek (ns_g_delay % 1000) * 1000000);
1215*00b67f09SDavid van Moolenbroek else
1216*00b67f09SDavid van Moolenbroek isc_interval_set(&interval, 0, ns_g_delay * 1000000);
1217*00b67f09SDavid van Moolenbroek result = isc_timer_create(client->manager->timermgr,
1218*00b67f09SDavid van Moolenbroek isc_timertype_once, NULL, &interval,
1219*00b67f09SDavid van Moolenbroek client->task, client_delay,
1220*00b67f09SDavid van Moolenbroek client, &client->delaytimer);
1221*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
1222*00b67f09SDavid van Moolenbroek return;
1223*00b67f09SDavid van Moolenbroek
1224*00b67f09SDavid van Moolenbroek ns_client_detach(&dummy);
1225*00b67f09SDavid van Moolenbroek }
1226*00b67f09SDavid van Moolenbroek
1227*00b67f09SDavid van Moolenbroek nodelay:
1228*00b67f09SDavid van Moolenbroek client_send(client);
1229*00b67f09SDavid van Moolenbroek }
1230*00b67f09SDavid van Moolenbroek
1231*00b67f09SDavid van Moolenbroek #if NS_CLIENT_DROPPORT
1232*00b67f09SDavid van Moolenbroek #define DROPPORT_NO 0
1233*00b67f09SDavid van Moolenbroek #define DROPPORT_REQUEST 1
1234*00b67f09SDavid van Moolenbroek #define DROPPORT_RESPONSE 2
1235*00b67f09SDavid van Moolenbroek /*%
1236*00b67f09SDavid van Moolenbroek * ns_client_dropport determines if certain requests / responses
1237*00b67f09SDavid van Moolenbroek * should be dropped based on the port number.
1238*00b67f09SDavid van Moolenbroek *
1239*00b67f09SDavid van Moolenbroek * Returns:
1240*00b67f09SDavid van Moolenbroek * \li 0: Don't drop.
1241*00b67f09SDavid van Moolenbroek * \li 1: Drop request.
1242*00b67f09SDavid van Moolenbroek * \li 2: Drop (error) response.
1243*00b67f09SDavid van Moolenbroek */
1244*00b67f09SDavid van Moolenbroek static int
ns_client_dropport(in_port_t port)1245*00b67f09SDavid van Moolenbroek ns_client_dropport(in_port_t port) {
1246*00b67f09SDavid van Moolenbroek switch (port) {
1247*00b67f09SDavid van Moolenbroek case 7: /* echo */
1248*00b67f09SDavid van Moolenbroek case 13: /* daytime */
1249*00b67f09SDavid van Moolenbroek case 19: /* chargen */
1250*00b67f09SDavid van Moolenbroek case 37: /* time */
1251*00b67f09SDavid van Moolenbroek return (DROPPORT_REQUEST);
1252*00b67f09SDavid van Moolenbroek case 464: /* kpasswd */
1253*00b67f09SDavid van Moolenbroek return (DROPPORT_RESPONSE);
1254*00b67f09SDavid van Moolenbroek }
1255*00b67f09SDavid van Moolenbroek return (DROPPORT_NO);
1256*00b67f09SDavid van Moolenbroek }
1257*00b67f09SDavid van Moolenbroek #endif
1258*00b67f09SDavid van Moolenbroek
1259*00b67f09SDavid van Moolenbroek void
ns_client_error(ns_client_t * client,isc_result_t result)1260*00b67f09SDavid van Moolenbroek ns_client_error(ns_client_t *client, isc_result_t result) {
1261*00b67f09SDavid van Moolenbroek dns_rcode_t rcode;
1262*00b67f09SDavid van Moolenbroek dns_message_t *message;
1263*00b67f09SDavid van Moolenbroek
1264*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
1265*00b67f09SDavid van Moolenbroek
1266*00b67f09SDavid van Moolenbroek CTRACE("error");
1267*00b67f09SDavid van Moolenbroek
1268*00b67f09SDavid van Moolenbroek message = client->message;
1269*00b67f09SDavid van Moolenbroek rcode = dns_result_torcode(result);
1270*00b67f09SDavid van Moolenbroek
1271*00b67f09SDavid van Moolenbroek #if NS_CLIENT_DROPPORT
1272*00b67f09SDavid van Moolenbroek /*
1273*00b67f09SDavid van Moolenbroek * Don't send FORMERR to ports on the drop port list.
1274*00b67f09SDavid van Moolenbroek */
1275*00b67f09SDavid van Moolenbroek if (rcode == dns_rcode_formerr &&
1276*00b67f09SDavid van Moolenbroek ns_client_dropport(isc_sockaddr_getport(&client->peeraddr)) !=
1277*00b67f09SDavid van Moolenbroek DROPPORT_NO) {
1278*00b67f09SDavid van Moolenbroek char buf[64];
1279*00b67f09SDavid van Moolenbroek isc_buffer_t b;
1280*00b67f09SDavid van Moolenbroek
1281*00b67f09SDavid van Moolenbroek isc_buffer_init(&b, buf, sizeof(buf) - 1);
1282*00b67f09SDavid van Moolenbroek if (dns_rcode_totext(rcode, &b) != ISC_R_SUCCESS)
1283*00b67f09SDavid van Moolenbroek isc_buffer_putstr(&b, "UNKNOWN RCODE");
1284*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1285*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
1286*00b67f09SDavid van Moolenbroek "dropped error (%.*s) response: suspicious port",
1287*00b67f09SDavid van Moolenbroek (int)isc_buffer_usedlength(&b), buf);
1288*00b67f09SDavid van Moolenbroek ns_client_next(client, ISC_R_SUCCESS);
1289*00b67f09SDavid van Moolenbroek return;
1290*00b67f09SDavid van Moolenbroek }
1291*00b67f09SDavid van Moolenbroek #endif
1292*00b67f09SDavid van Moolenbroek
1293*00b67f09SDavid van Moolenbroek /*
1294*00b67f09SDavid van Moolenbroek * Try to rate limit error responses.
1295*00b67f09SDavid van Moolenbroek */
1296*00b67f09SDavid van Moolenbroek if (client->view != NULL && client->view->rrl != NULL) {
1297*00b67f09SDavid van Moolenbroek isc_boolean_t wouldlog;
1298*00b67f09SDavid van Moolenbroek char log_buf[DNS_RRL_LOG_BUF_LEN];
1299*00b67f09SDavid van Moolenbroek dns_rrl_result_t rrl_result;
1300*00b67f09SDavid van Moolenbroek
1301*00b67f09SDavid van Moolenbroek INSIST(rcode != dns_rcode_noerror &&
1302*00b67f09SDavid van Moolenbroek rcode != dns_rcode_nxdomain);
1303*00b67f09SDavid van Moolenbroek wouldlog = isc_log_wouldlog(ns_g_lctx, DNS_RRL_LOG_DROP);
1304*00b67f09SDavid van Moolenbroek rrl_result = dns_rrl(client->view, &client->peeraddr,
1305*00b67f09SDavid van Moolenbroek TCP_CLIENT(client),
1306*00b67f09SDavid van Moolenbroek dns_rdataclass_in, dns_rdatatype_none,
1307*00b67f09SDavid van Moolenbroek NULL, result, client->now,
1308*00b67f09SDavid van Moolenbroek wouldlog, log_buf, sizeof(log_buf));
1309*00b67f09SDavid van Moolenbroek if (rrl_result != DNS_RRL_RESULT_OK) {
1310*00b67f09SDavid van Moolenbroek /*
1311*00b67f09SDavid van Moolenbroek * Log dropped errors in the query category
1312*00b67f09SDavid van Moolenbroek * so that they are not lost in silence.
1313*00b67f09SDavid van Moolenbroek * Starts of rate-limited bursts are logged in
1314*00b67f09SDavid van Moolenbroek * NS_LOGCATEGORY_RRL.
1315*00b67f09SDavid van Moolenbroek */
1316*00b67f09SDavid van Moolenbroek if (wouldlog) {
1317*00b67f09SDavid van Moolenbroek ns_client_log(client,
1318*00b67f09SDavid van Moolenbroek NS_LOGCATEGORY_QUERY_EERRORS,
1319*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT,
1320*00b67f09SDavid van Moolenbroek DNS_RRL_LOG_DROP,
1321*00b67f09SDavid van Moolenbroek "%s", log_buf);
1322*00b67f09SDavid van Moolenbroek }
1323*00b67f09SDavid van Moolenbroek /*
1324*00b67f09SDavid van Moolenbroek * Some error responses cannot be 'slipped',
1325*00b67f09SDavid van Moolenbroek * so don't try to slip any error responses.
1326*00b67f09SDavid van Moolenbroek */
1327*00b67f09SDavid van Moolenbroek if (!client->view->rrl->log_only) {
1328*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1329*00b67f09SDavid van Moolenbroek dns_nsstatscounter_ratedropped);
1330*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1331*00b67f09SDavid van Moolenbroek dns_nsstatscounter_dropped);
1332*00b67f09SDavid van Moolenbroek ns_client_next(client, DNS_R_DROP);
1333*00b67f09SDavid van Moolenbroek return;
1334*00b67f09SDavid van Moolenbroek }
1335*00b67f09SDavid van Moolenbroek }
1336*00b67f09SDavid van Moolenbroek }
1337*00b67f09SDavid van Moolenbroek
1338*00b67f09SDavid van Moolenbroek /*
1339*00b67f09SDavid van Moolenbroek * Message may be an in-progress reply that we had trouble
1340*00b67f09SDavid van Moolenbroek * with, in which case QR will be set. We need to clear QR before
1341*00b67f09SDavid van Moolenbroek * calling dns_message_reply() to avoid triggering an assertion.
1342*00b67f09SDavid van Moolenbroek */
1343*00b67f09SDavid van Moolenbroek message->flags &= ~DNS_MESSAGEFLAG_QR;
1344*00b67f09SDavid van Moolenbroek /*
1345*00b67f09SDavid van Moolenbroek * AA and AD shouldn't be set.
1346*00b67f09SDavid van Moolenbroek */
1347*00b67f09SDavid van Moolenbroek message->flags &= ~(DNS_MESSAGEFLAG_AA | DNS_MESSAGEFLAG_AD);
1348*00b67f09SDavid van Moolenbroek result = dns_message_reply(message, ISC_TRUE);
1349*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
1350*00b67f09SDavid van Moolenbroek /*
1351*00b67f09SDavid van Moolenbroek * It could be that we've got a query with a good header,
1352*00b67f09SDavid van Moolenbroek * but a bad question section, so we try again with
1353*00b67f09SDavid van Moolenbroek * want_question_section set to ISC_FALSE.
1354*00b67f09SDavid van Moolenbroek */
1355*00b67f09SDavid van Moolenbroek result = dns_message_reply(message, ISC_FALSE);
1356*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
1357*00b67f09SDavid van Moolenbroek ns_client_next(client, result);
1358*00b67f09SDavid van Moolenbroek return;
1359*00b67f09SDavid van Moolenbroek }
1360*00b67f09SDavid van Moolenbroek }
1361*00b67f09SDavid van Moolenbroek message->rcode = rcode;
1362*00b67f09SDavid van Moolenbroek
1363*00b67f09SDavid van Moolenbroek /*
1364*00b67f09SDavid van Moolenbroek * FORMERR loop avoidance: If we sent a FORMERR message
1365*00b67f09SDavid van Moolenbroek * with the same ID to the same client less than two
1366*00b67f09SDavid van Moolenbroek * seconds ago, assume that we are in an infinite error
1367*00b67f09SDavid van Moolenbroek * packet dialog with a server for some protocol whose
1368*00b67f09SDavid van Moolenbroek * error responses look enough like DNS queries to
1369*00b67f09SDavid van Moolenbroek * elicit a FORMERR response. Drop a packet to break
1370*00b67f09SDavid van Moolenbroek * the loop.
1371*00b67f09SDavid van Moolenbroek */
1372*00b67f09SDavid van Moolenbroek if (rcode == dns_rcode_formerr) {
1373*00b67f09SDavid van Moolenbroek if (isc_sockaddr_equal(&client->peeraddr,
1374*00b67f09SDavid van Moolenbroek &client->formerrcache.addr) &&
1375*00b67f09SDavid van Moolenbroek message->id == client->formerrcache.id &&
1376*00b67f09SDavid van Moolenbroek client->requesttime - client->formerrcache.time < 2) {
1377*00b67f09SDavid van Moolenbroek /* Drop packet. */
1378*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1379*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
1380*00b67f09SDavid van Moolenbroek "possible error packet loop, "
1381*00b67f09SDavid van Moolenbroek "FORMERR dropped");
1382*00b67f09SDavid van Moolenbroek ns_client_next(client, result);
1383*00b67f09SDavid van Moolenbroek return;
1384*00b67f09SDavid van Moolenbroek }
1385*00b67f09SDavid van Moolenbroek client->formerrcache.addr = client->peeraddr;
1386*00b67f09SDavid van Moolenbroek client->formerrcache.time = client->requesttime;
1387*00b67f09SDavid van Moolenbroek client->formerrcache.id = message->id;
1388*00b67f09SDavid van Moolenbroek }
1389*00b67f09SDavid van Moolenbroek ns_client_send(client);
1390*00b67f09SDavid van Moolenbroek }
1391*00b67f09SDavid van Moolenbroek
1392*00b67f09SDavid van Moolenbroek isc_result_t
ns_client_addopt(ns_client_t * client,dns_message_t * message,dns_rdataset_t ** opt)1393*00b67f09SDavid van Moolenbroek ns_client_addopt(ns_client_t *client, dns_message_t *message,
1394*00b67f09SDavid van Moolenbroek dns_rdataset_t **opt)
1395*00b67f09SDavid van Moolenbroek {
1396*00b67f09SDavid van Moolenbroek char nsid[BUFSIZ], *nsidp;
1397*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
1398*00b67f09SDavid van Moolenbroek unsigned char sit[SIT_SIZE];
1399*00b67f09SDavid van Moolenbroek #endif
1400*00b67f09SDavid van Moolenbroek isc_result_t result;
1401*00b67f09SDavid van Moolenbroek dns_view_t *view;
1402*00b67f09SDavid van Moolenbroek dns_resolver_t *resolver;
1403*00b67f09SDavid van Moolenbroek isc_uint16_t udpsize;
1404*00b67f09SDavid van Moolenbroek dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS];
1405*00b67f09SDavid van Moolenbroek int count = 0;
1406*00b67f09SDavid van Moolenbroek unsigned int flags;
1407*00b67f09SDavid van Moolenbroek unsigned char expire[4];
1408*00b67f09SDavid van Moolenbroek
1409*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
1410*00b67f09SDavid van Moolenbroek REQUIRE(opt != NULL && *opt == NULL);
1411*00b67f09SDavid van Moolenbroek REQUIRE(message != NULL);
1412*00b67f09SDavid van Moolenbroek
1413*00b67f09SDavid van Moolenbroek view = client->view;
1414*00b67f09SDavid van Moolenbroek resolver = (view != NULL) ? view->resolver : NULL;
1415*00b67f09SDavid van Moolenbroek if (resolver != NULL)
1416*00b67f09SDavid van Moolenbroek udpsize = dns_resolver_getudpsize(resolver);
1417*00b67f09SDavid van Moolenbroek else
1418*00b67f09SDavid van Moolenbroek udpsize = ns_g_udpsize;
1419*00b67f09SDavid van Moolenbroek
1420*00b67f09SDavid van Moolenbroek flags = client->extflags & DNS_MESSAGEEXTFLAG_REPLYPRESERVE;
1421*00b67f09SDavid van Moolenbroek
1422*00b67f09SDavid van Moolenbroek /* Set EDNS options if applicable */
1423*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_WANTNSID) != 0 &&
1424*00b67f09SDavid van Moolenbroek (ns_g_server->server_id != NULL ||
1425*00b67f09SDavid van Moolenbroek ns_g_server->server_usehostname)) {
1426*00b67f09SDavid van Moolenbroek if (ns_g_server->server_usehostname) {
1427*00b67f09SDavid van Moolenbroek result = ns_os_gethostname(nsid, sizeof(nsid));
1428*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
1429*00b67f09SDavid van Moolenbroek goto no_nsid;
1430*00b67f09SDavid van Moolenbroek }
1431*00b67f09SDavid van Moolenbroek nsidp = nsid;
1432*00b67f09SDavid van Moolenbroek } else
1433*00b67f09SDavid van Moolenbroek nsidp = ns_g_server->server_id;
1434*00b67f09SDavid van Moolenbroek
1435*00b67f09SDavid van Moolenbroek INSIST(count < DNS_EDNSOPTIONS);
1436*00b67f09SDavid van Moolenbroek ednsopts[count].code = DNS_OPT_NSID;
1437*00b67f09SDavid van Moolenbroek ednsopts[count].length = strlen(nsidp);
1438*00b67f09SDavid van Moolenbroek ednsopts[count].value = (unsigned char *)nsidp;
1439*00b67f09SDavid van Moolenbroek count++;
1440*00b67f09SDavid van Moolenbroek }
1441*00b67f09SDavid van Moolenbroek no_nsid:
1442*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
1443*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_WANTSIT) != 0) {
1444*00b67f09SDavid van Moolenbroek isc_buffer_t buf;
1445*00b67f09SDavid van Moolenbroek isc_stdtime_t now;
1446*00b67f09SDavid van Moolenbroek isc_uint32_t nonce;
1447*00b67f09SDavid van Moolenbroek
1448*00b67f09SDavid van Moolenbroek isc_buffer_init(&buf, sit, sizeof(sit));
1449*00b67f09SDavid van Moolenbroek isc_stdtime_get(&now);
1450*00b67f09SDavid van Moolenbroek isc_random_get(&nonce);
1451*00b67f09SDavid van Moolenbroek
1452*00b67f09SDavid van Moolenbroek compute_sit(client, now, nonce, &buf);
1453*00b67f09SDavid van Moolenbroek
1454*00b67f09SDavid van Moolenbroek INSIST(count < DNS_EDNSOPTIONS);
1455*00b67f09SDavid van Moolenbroek ednsopts[count].code = DNS_OPT_SIT;
1456*00b67f09SDavid van Moolenbroek ednsopts[count].length = SIT_SIZE;
1457*00b67f09SDavid van Moolenbroek ednsopts[count].value = sit;
1458*00b67f09SDavid van Moolenbroek count++;
1459*00b67f09SDavid van Moolenbroek }
1460*00b67f09SDavid van Moolenbroek #endif
1461*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_HAVEEXPIRE) != 0) {
1462*00b67f09SDavid van Moolenbroek isc_buffer_t buf;
1463*00b67f09SDavid van Moolenbroek
1464*00b67f09SDavid van Moolenbroek INSIST(count < DNS_EDNSOPTIONS);
1465*00b67f09SDavid van Moolenbroek
1466*00b67f09SDavid van Moolenbroek isc_buffer_init(&buf, expire, sizeof(expire));
1467*00b67f09SDavid van Moolenbroek isc_buffer_putuint32(&buf, client->expire);
1468*00b67f09SDavid van Moolenbroek ednsopts[count].code = DNS_OPT_EXPIRE;
1469*00b67f09SDavid van Moolenbroek ednsopts[count].length = 4;
1470*00b67f09SDavid van Moolenbroek ednsopts[count].value = expire;
1471*00b67f09SDavid van Moolenbroek count++;
1472*00b67f09SDavid van Moolenbroek }
1473*00b67f09SDavid van Moolenbroek
1474*00b67f09SDavid van Moolenbroek result = dns_message_buildopt(message, opt, 0, udpsize, flags,
1475*00b67f09SDavid van Moolenbroek ednsopts, count);
1476*00b67f09SDavid van Moolenbroek return (result);
1477*00b67f09SDavid van Moolenbroek }
1478*00b67f09SDavid van Moolenbroek
1479*00b67f09SDavid van Moolenbroek static inline isc_boolean_t
allowed(isc_netaddr_t * addr,dns_name_t * signer,dns_acl_t * acl)1480*00b67f09SDavid van Moolenbroek allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl) {
1481*00b67f09SDavid van Moolenbroek int match;
1482*00b67f09SDavid van Moolenbroek isc_result_t result;
1483*00b67f09SDavid van Moolenbroek
1484*00b67f09SDavid van Moolenbroek if (acl == NULL)
1485*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
1486*00b67f09SDavid van Moolenbroek result = dns_acl_match(addr, signer, acl, &ns_g_server->aclenv,
1487*00b67f09SDavid van Moolenbroek &match, NULL);
1488*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS && match > 0)
1489*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
1490*00b67f09SDavid van Moolenbroek return (ISC_FALSE);
1491*00b67f09SDavid van Moolenbroek }
1492*00b67f09SDavid van Moolenbroek
1493*00b67f09SDavid van Moolenbroek /*
1494*00b67f09SDavid van Moolenbroek * Callback to see if a non-recursive query coming from 'srcaddr' to
1495*00b67f09SDavid van Moolenbroek * 'destaddr', with optional key 'mykey' for class 'rdclass' would be
1496*00b67f09SDavid van Moolenbroek * delivered to 'myview'.
1497*00b67f09SDavid van Moolenbroek *
1498*00b67f09SDavid van Moolenbroek * We run this unlocked as both the view list and the interface list
1499*00b67f09SDavid van Moolenbroek * are updated when the appropriate task has exclusivity.
1500*00b67f09SDavid van Moolenbroek */
1501*00b67f09SDavid van Moolenbroek isc_boolean_t
ns_client_isself(dns_view_t * myview,dns_tsigkey_t * mykey,isc_sockaddr_t * srcaddr,isc_sockaddr_t * dstaddr,dns_rdataclass_t rdclass,void * arg)1502*00b67f09SDavid van Moolenbroek ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
1503*00b67f09SDavid van Moolenbroek isc_sockaddr_t *srcaddr, isc_sockaddr_t *dstaddr,
1504*00b67f09SDavid van Moolenbroek dns_rdataclass_t rdclass, void *arg)
1505*00b67f09SDavid van Moolenbroek {
1506*00b67f09SDavid van Moolenbroek dns_view_t *view;
1507*00b67f09SDavid van Moolenbroek dns_tsigkey_t *key = NULL;
1508*00b67f09SDavid van Moolenbroek dns_name_t *tsig = NULL;
1509*00b67f09SDavid van Moolenbroek isc_netaddr_t netsrc;
1510*00b67f09SDavid van Moolenbroek isc_netaddr_t netdst;
1511*00b67f09SDavid van Moolenbroek
1512*00b67f09SDavid van Moolenbroek UNUSED(arg);
1513*00b67f09SDavid van Moolenbroek
1514*00b67f09SDavid van Moolenbroek /*
1515*00b67f09SDavid van Moolenbroek * ns_g_server->interfacemgr is task exclusive locked.
1516*00b67f09SDavid van Moolenbroek */
1517*00b67f09SDavid van Moolenbroek if (ns_g_server->interfacemgr == NULL)
1518*00b67f09SDavid van Moolenbroek return (ISC_TRUE);
1519*00b67f09SDavid van Moolenbroek
1520*00b67f09SDavid van Moolenbroek if (!ns_interfacemgr_listeningon(ns_g_server->interfacemgr, dstaddr))
1521*00b67f09SDavid van Moolenbroek return (ISC_FALSE);
1522*00b67f09SDavid van Moolenbroek
1523*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&netsrc, srcaddr);
1524*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&netdst, dstaddr);
1525*00b67f09SDavid van Moolenbroek
1526*00b67f09SDavid van Moolenbroek for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
1527*00b67f09SDavid van Moolenbroek view != NULL;
1528*00b67f09SDavid van Moolenbroek view = ISC_LIST_NEXT(view, link)) {
1529*00b67f09SDavid van Moolenbroek
1530*00b67f09SDavid van Moolenbroek if (view->matchrecursiveonly)
1531*00b67f09SDavid van Moolenbroek continue;
1532*00b67f09SDavid van Moolenbroek
1533*00b67f09SDavid van Moolenbroek if (rdclass != view->rdclass)
1534*00b67f09SDavid van Moolenbroek continue;
1535*00b67f09SDavid van Moolenbroek
1536*00b67f09SDavid van Moolenbroek if (mykey != NULL) {
1537*00b67f09SDavid van Moolenbroek isc_boolean_t match;
1538*00b67f09SDavid van Moolenbroek isc_result_t result;
1539*00b67f09SDavid van Moolenbroek
1540*00b67f09SDavid van Moolenbroek result = dns_view_gettsig(view, &mykey->name, &key);
1541*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
1542*00b67f09SDavid van Moolenbroek continue;
1543*00b67f09SDavid van Moolenbroek match = dst_key_compare(mykey->key, key->key);
1544*00b67f09SDavid van Moolenbroek dns_tsigkey_detach(&key);
1545*00b67f09SDavid van Moolenbroek if (!match)
1546*00b67f09SDavid van Moolenbroek continue;
1547*00b67f09SDavid van Moolenbroek tsig = dns_tsigkey_identity(mykey);
1548*00b67f09SDavid van Moolenbroek }
1549*00b67f09SDavid van Moolenbroek
1550*00b67f09SDavid van Moolenbroek if (allowed(&netsrc, tsig, view->matchclients) &&
1551*00b67f09SDavid van Moolenbroek allowed(&netdst, tsig, view->matchdestinations))
1552*00b67f09SDavid van Moolenbroek break;
1553*00b67f09SDavid van Moolenbroek }
1554*00b67f09SDavid van Moolenbroek return (ISC_TF(view == myview));
1555*00b67f09SDavid van Moolenbroek }
1556*00b67f09SDavid van Moolenbroek
1557*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
1558*00b67f09SDavid van Moolenbroek static void
compute_sit(ns_client_t * client,isc_uint32_t when,isc_uint32_t nonce,isc_buffer_t * buf)1559*00b67f09SDavid van Moolenbroek compute_sit(ns_client_t *client, isc_uint32_t when, isc_uint32_t nonce,
1560*00b67f09SDavid van Moolenbroek isc_buffer_t *buf)
1561*00b67f09SDavid van Moolenbroek {
1562*00b67f09SDavid van Moolenbroek #ifdef AES_SIT
1563*00b67f09SDavid van Moolenbroek unsigned char digest[ISC_AES_BLOCK_LENGTH];
1564*00b67f09SDavid van Moolenbroek unsigned char input[4 + 4 + 16];
1565*00b67f09SDavid van Moolenbroek isc_netaddr_t netaddr;
1566*00b67f09SDavid van Moolenbroek unsigned char *cp;
1567*00b67f09SDavid van Moolenbroek unsigned int i;
1568*00b67f09SDavid van Moolenbroek
1569*00b67f09SDavid van Moolenbroek memset(input, 0, sizeof(input));
1570*00b67f09SDavid van Moolenbroek cp = isc_buffer_used(buf);
1571*00b67f09SDavid van Moolenbroek isc_buffer_putmem(buf, client->cookie, 8);
1572*00b67f09SDavid van Moolenbroek isc_buffer_putuint32(buf, nonce);
1573*00b67f09SDavid van Moolenbroek isc_buffer_putuint32(buf, when);
1574*00b67f09SDavid van Moolenbroek memmove(input, cp, 16);
1575*00b67f09SDavid van Moolenbroek isc_aes128_crypt(ns_g_server->secret, input, digest);
1576*00b67f09SDavid van Moolenbroek for (i = 0; i < 8; i++)
1577*00b67f09SDavid van Moolenbroek input[i] = digest[i] ^ digest[i + 8];
1578*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
1579*00b67f09SDavid van Moolenbroek switch (netaddr.family) {
1580*00b67f09SDavid van Moolenbroek case AF_INET:
1581*00b67f09SDavid van Moolenbroek memmove(input + 8, (unsigned char *)&netaddr.type.in, 4);
1582*00b67f09SDavid van Moolenbroek memset(input + 12, 0, 4);
1583*00b67f09SDavid van Moolenbroek isc_aes128_crypt(ns_g_server->secret, input, digest);
1584*00b67f09SDavid van Moolenbroek break;
1585*00b67f09SDavid van Moolenbroek case AF_INET6:
1586*00b67f09SDavid van Moolenbroek memmove(input + 8, (unsigned char *)&netaddr.type.in6, 16);
1587*00b67f09SDavid van Moolenbroek isc_aes128_crypt(ns_g_server->secret, input, digest);
1588*00b67f09SDavid van Moolenbroek for (i = 0; i < 8; i++)
1589*00b67f09SDavid van Moolenbroek input[i + 8] = digest[i] ^ digest[i + 8];
1590*00b67f09SDavid van Moolenbroek isc_aes128_crypt(ns_g_server->secret, input + 8, digest);
1591*00b67f09SDavid van Moolenbroek break;
1592*00b67f09SDavid van Moolenbroek }
1593*00b67f09SDavid van Moolenbroek for (i = 0; i < 8; i++)
1594*00b67f09SDavid van Moolenbroek digest[i] ^= digest[i + 8];
1595*00b67f09SDavid van Moolenbroek isc_buffer_putmem(buf, digest, 8);
1596*00b67f09SDavid van Moolenbroek #endif
1597*00b67f09SDavid van Moolenbroek #ifdef HMAC_SHA1_SIT
1598*00b67f09SDavid van Moolenbroek unsigned char digest[ISC_SHA1_DIGESTLENGTH];
1599*00b67f09SDavid van Moolenbroek isc_netaddr_t netaddr;
1600*00b67f09SDavid van Moolenbroek unsigned char *cp;
1601*00b67f09SDavid van Moolenbroek isc_hmacsha1_t hmacsha1;
1602*00b67f09SDavid van Moolenbroek
1603*00b67f09SDavid van Moolenbroek cp = isc_buffer_used(buf);
1604*00b67f09SDavid van Moolenbroek isc_buffer_putmem(buf, client->cookie, 8);
1605*00b67f09SDavid van Moolenbroek isc_buffer_putuint32(buf, nonce);
1606*00b67f09SDavid van Moolenbroek isc_buffer_putuint32(buf, when);
1607*00b67f09SDavid van Moolenbroek
1608*00b67f09SDavid van Moolenbroek isc_hmacsha1_init(&hmacsha1,
1609*00b67f09SDavid van Moolenbroek ns_g_server->secret,
1610*00b67f09SDavid van Moolenbroek ISC_SHA1_DIGESTLENGTH);
1611*00b67f09SDavid van Moolenbroek isc_hmacsha1_update(&hmacsha1, cp, 16);
1612*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
1613*00b67f09SDavid van Moolenbroek switch (netaddr.family) {
1614*00b67f09SDavid van Moolenbroek case AF_INET:
1615*00b67f09SDavid van Moolenbroek isc_hmacsha1_update(&hmacsha1,
1616*00b67f09SDavid van Moolenbroek (unsigned char *)&netaddr.type.in, 4);
1617*00b67f09SDavid van Moolenbroek break;
1618*00b67f09SDavid van Moolenbroek case AF_INET6:
1619*00b67f09SDavid van Moolenbroek isc_hmacsha1_update(&hmacsha1,
1620*00b67f09SDavid van Moolenbroek (unsigned char *)&netaddr.type.in6, 16);
1621*00b67f09SDavid van Moolenbroek break;
1622*00b67f09SDavid van Moolenbroek }
1623*00b67f09SDavid van Moolenbroek isc_hmacsha1_update(&hmacsha1, client->cookie, sizeof(client->cookie));
1624*00b67f09SDavid van Moolenbroek isc_hmacsha1_sign(&hmacsha1, digest, sizeof(digest));
1625*00b67f09SDavid van Moolenbroek isc_buffer_putmem(buf, digest, 8);
1626*00b67f09SDavid van Moolenbroek isc_hmacsha1_invalidate(&hmacsha1);
1627*00b67f09SDavid van Moolenbroek #endif
1628*00b67f09SDavid van Moolenbroek #ifdef HMAC_SHA256_SIT
1629*00b67f09SDavid van Moolenbroek unsigned char digest[ISC_SHA256_DIGESTLENGTH];
1630*00b67f09SDavid van Moolenbroek isc_netaddr_t netaddr;
1631*00b67f09SDavid van Moolenbroek unsigned char *cp;
1632*00b67f09SDavid van Moolenbroek isc_hmacsha256_t hmacsha256;
1633*00b67f09SDavid van Moolenbroek
1634*00b67f09SDavid van Moolenbroek cp = isc_buffer_used(buf);
1635*00b67f09SDavid van Moolenbroek isc_buffer_putmem(buf, client->cookie, 8);
1636*00b67f09SDavid van Moolenbroek isc_buffer_putuint32(buf, nonce);
1637*00b67f09SDavid van Moolenbroek isc_buffer_putuint32(buf, when);
1638*00b67f09SDavid van Moolenbroek
1639*00b67f09SDavid van Moolenbroek isc_hmacsha256_init(&hmacsha256,
1640*00b67f09SDavid van Moolenbroek ns_g_server->secret,
1641*00b67f09SDavid van Moolenbroek ISC_SHA256_DIGESTLENGTH);
1642*00b67f09SDavid van Moolenbroek isc_hmacsha256_update(&hmacsha256, cp, 16);
1643*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
1644*00b67f09SDavid van Moolenbroek switch (netaddr.family) {
1645*00b67f09SDavid van Moolenbroek case AF_INET:
1646*00b67f09SDavid van Moolenbroek isc_hmacsha256_update(&hmacsha256,
1647*00b67f09SDavid van Moolenbroek (unsigned char *)&netaddr.type.in, 4);
1648*00b67f09SDavid van Moolenbroek break;
1649*00b67f09SDavid van Moolenbroek case AF_INET6:
1650*00b67f09SDavid van Moolenbroek isc_hmacsha256_update(&hmacsha256,
1651*00b67f09SDavid van Moolenbroek (unsigned char *)&netaddr.type.in6, 16);
1652*00b67f09SDavid van Moolenbroek break;
1653*00b67f09SDavid van Moolenbroek }
1654*00b67f09SDavid van Moolenbroek isc_hmacsha256_update(&hmacsha256, client->cookie,
1655*00b67f09SDavid van Moolenbroek sizeof(client->cookie));
1656*00b67f09SDavid van Moolenbroek isc_hmacsha256_sign(&hmacsha256, digest, sizeof(digest));
1657*00b67f09SDavid van Moolenbroek isc_buffer_putmem(buf, digest, 8);
1658*00b67f09SDavid van Moolenbroek isc_hmacsha256_invalidate(&hmacsha256);
1659*00b67f09SDavid van Moolenbroek #endif
1660*00b67f09SDavid van Moolenbroek }
1661*00b67f09SDavid van Moolenbroek
1662*00b67f09SDavid van Moolenbroek static void
process_sit(ns_client_t * client,isc_buffer_t * buf,size_t optlen)1663*00b67f09SDavid van Moolenbroek process_sit(ns_client_t *client, isc_buffer_t *buf, size_t optlen) {
1664*00b67f09SDavid van Moolenbroek unsigned char dbuf[SIT_SIZE];
1665*00b67f09SDavid van Moolenbroek unsigned char *old;
1666*00b67f09SDavid van Moolenbroek isc_stdtime_t now;
1667*00b67f09SDavid van Moolenbroek isc_uint32_t when;
1668*00b67f09SDavid van Moolenbroek isc_uint32_t nonce;
1669*00b67f09SDavid van Moolenbroek isc_buffer_t db;
1670*00b67f09SDavid van Moolenbroek
1671*00b67f09SDavid van Moolenbroek client->attributes |= NS_CLIENTATTR_WANTSIT;
1672*00b67f09SDavid van Moolenbroek
1673*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1674*00b67f09SDavid van Moolenbroek dns_nsstatscounter_sitopt);
1675*00b67f09SDavid van Moolenbroek
1676*00b67f09SDavid van Moolenbroek if (optlen != SIT_SIZE) {
1677*00b67f09SDavid van Moolenbroek /*
1678*00b67f09SDavid van Moolenbroek * Not our token.
1679*00b67f09SDavid van Moolenbroek */
1680*00b67f09SDavid van Moolenbroek if (optlen >= 8U)
1681*00b67f09SDavid van Moolenbroek memmove(client->cookie, isc_buffer_current(buf), 8);
1682*00b67f09SDavid van Moolenbroek else
1683*00b67f09SDavid van Moolenbroek memset(client->cookie, 0, 8);
1684*00b67f09SDavid van Moolenbroek isc_buffer_forward(buf, (unsigned int)optlen);
1685*00b67f09SDavid van Moolenbroek
1686*00b67f09SDavid van Moolenbroek if (optlen == 8U)
1687*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1688*00b67f09SDavid van Moolenbroek dns_nsstatscounter_sitnew);
1689*00b67f09SDavid van Moolenbroek else
1690*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1691*00b67f09SDavid van Moolenbroek dns_nsstatscounter_sitbadsize);
1692*00b67f09SDavid van Moolenbroek return;
1693*00b67f09SDavid van Moolenbroek }
1694*00b67f09SDavid van Moolenbroek
1695*00b67f09SDavid van Moolenbroek /*
1696*00b67f09SDavid van Moolenbroek * Process all of the incoming buffer.
1697*00b67f09SDavid van Moolenbroek */
1698*00b67f09SDavid van Moolenbroek old = isc_buffer_current(buf);
1699*00b67f09SDavid van Moolenbroek memmove(client->cookie, old, 8);
1700*00b67f09SDavid van Moolenbroek isc_buffer_forward(buf, 8);
1701*00b67f09SDavid van Moolenbroek nonce = isc_buffer_getuint32(buf);
1702*00b67f09SDavid van Moolenbroek when = isc_buffer_getuint32(buf);
1703*00b67f09SDavid van Moolenbroek isc_buffer_forward(buf, 8);
1704*00b67f09SDavid van Moolenbroek
1705*00b67f09SDavid van Moolenbroek /*
1706*00b67f09SDavid van Moolenbroek * Allow for a 5 minute clock skew between servers sharing a secret.
1707*00b67f09SDavid van Moolenbroek * Only accept SIT if we have talked to the client in the last hour.
1708*00b67f09SDavid van Moolenbroek */
1709*00b67f09SDavid van Moolenbroek isc_stdtime_get(&now);
1710*00b67f09SDavid van Moolenbroek if (isc_serial_gt(when, (now + 300)) || /* In the future. */
1711*00b67f09SDavid van Moolenbroek isc_serial_lt(when, (now - 3600))) { /* In the past. */
1712*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1713*00b67f09SDavid van Moolenbroek dns_nsstatscounter_sitbadtime);
1714*00b67f09SDavid van Moolenbroek return;
1715*00b67f09SDavid van Moolenbroek }
1716*00b67f09SDavid van Moolenbroek
1717*00b67f09SDavid van Moolenbroek isc_buffer_init(&db, dbuf, sizeof(dbuf));
1718*00b67f09SDavid van Moolenbroek compute_sit(client, when, nonce, &db);
1719*00b67f09SDavid van Moolenbroek
1720*00b67f09SDavid van Moolenbroek if (memcmp(old, dbuf, SIT_SIZE) != 0) {
1721*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1722*00b67f09SDavid van Moolenbroek dns_nsstatscounter_sitnomatch);
1723*00b67f09SDavid van Moolenbroek return;
1724*00b67f09SDavid van Moolenbroek }
1725*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1726*00b67f09SDavid van Moolenbroek dns_nsstatscounter_sitmatch);
1727*00b67f09SDavid van Moolenbroek
1728*00b67f09SDavid van Moolenbroek client->attributes |= NS_CLIENTATTR_HAVESIT;
1729*00b67f09SDavid van Moolenbroek }
1730*00b67f09SDavid van Moolenbroek #endif
1731*00b67f09SDavid van Moolenbroek
1732*00b67f09SDavid van Moolenbroek static isc_result_t
process_opt(ns_client_t * client,dns_rdataset_t * opt)1733*00b67f09SDavid van Moolenbroek process_opt(ns_client_t *client, dns_rdataset_t *opt) {
1734*00b67f09SDavid van Moolenbroek dns_rdata_t rdata;
1735*00b67f09SDavid van Moolenbroek isc_buffer_t optbuf;
1736*00b67f09SDavid van Moolenbroek isc_result_t result;
1737*00b67f09SDavid van Moolenbroek isc_uint16_t optcode;
1738*00b67f09SDavid van Moolenbroek isc_uint16_t optlen;
1739*00b67f09SDavid van Moolenbroek
1740*00b67f09SDavid van Moolenbroek /*
1741*00b67f09SDavid van Moolenbroek * Set the client's UDP buffer size.
1742*00b67f09SDavid van Moolenbroek */
1743*00b67f09SDavid van Moolenbroek client->udpsize = opt->rdclass;
1744*00b67f09SDavid van Moolenbroek
1745*00b67f09SDavid van Moolenbroek /*
1746*00b67f09SDavid van Moolenbroek * If the requested UDP buffer size is less than 512,
1747*00b67f09SDavid van Moolenbroek * ignore it and use 512.
1748*00b67f09SDavid van Moolenbroek */
1749*00b67f09SDavid van Moolenbroek if (client->udpsize < 512)
1750*00b67f09SDavid van Moolenbroek client->udpsize = 512;
1751*00b67f09SDavid van Moolenbroek
1752*00b67f09SDavid van Moolenbroek /*
1753*00b67f09SDavid van Moolenbroek * Get the flags out of the OPT record.
1754*00b67f09SDavid van Moolenbroek */
1755*00b67f09SDavid van Moolenbroek client->extflags = (isc_uint16_t)(opt->ttl & 0xFFFF);
1756*00b67f09SDavid van Moolenbroek
1757*00b67f09SDavid van Moolenbroek /*
1758*00b67f09SDavid van Moolenbroek * Do we understand this version of EDNS?
1759*00b67f09SDavid van Moolenbroek *
1760*00b67f09SDavid van Moolenbroek * XXXRTH need library support for this!
1761*00b67f09SDavid van Moolenbroek */
1762*00b67f09SDavid van Moolenbroek client->ednsversion = (opt->ttl & 0x00FF0000) >> 16;
1763*00b67f09SDavid van Moolenbroek if (client->ednsversion > 0) {
1764*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1765*00b67f09SDavid van Moolenbroek dns_nsstatscounter_badednsver);
1766*00b67f09SDavid van Moolenbroek result = ns_client_addopt(client, client->message,
1767*00b67f09SDavid van Moolenbroek &client->opt);
1768*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
1769*00b67f09SDavid van Moolenbroek result = DNS_R_BADVERS;
1770*00b67f09SDavid van Moolenbroek ns_client_error(client, result);
1771*00b67f09SDavid van Moolenbroek goto cleanup;
1772*00b67f09SDavid van Moolenbroek }
1773*00b67f09SDavid van Moolenbroek
1774*00b67f09SDavid van Moolenbroek /* Check for NSID request */
1775*00b67f09SDavid van Moolenbroek result = dns_rdataset_first(opt);
1776*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS) {
1777*00b67f09SDavid van Moolenbroek dns_rdata_init(&rdata);
1778*00b67f09SDavid van Moolenbroek dns_rdataset_current(opt, &rdata);
1779*00b67f09SDavid van Moolenbroek isc_buffer_init(&optbuf, rdata.data, rdata.length);
1780*00b67f09SDavid van Moolenbroek isc_buffer_add(&optbuf, rdata.length);
1781*00b67f09SDavid van Moolenbroek while (isc_buffer_remaininglength(&optbuf) >= 4) {
1782*00b67f09SDavid van Moolenbroek optcode = isc_buffer_getuint16(&optbuf);
1783*00b67f09SDavid van Moolenbroek optlen = isc_buffer_getuint16(&optbuf);
1784*00b67f09SDavid van Moolenbroek switch (optcode) {
1785*00b67f09SDavid van Moolenbroek case DNS_OPT_NSID:
1786*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1787*00b67f09SDavid van Moolenbroek dns_nsstatscounter_nsidopt);
1788*00b67f09SDavid van Moolenbroek client->attributes |= NS_CLIENTATTR_WANTNSID;
1789*00b67f09SDavid van Moolenbroek isc_buffer_forward(&optbuf, optlen);
1790*00b67f09SDavid van Moolenbroek break;
1791*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
1792*00b67f09SDavid van Moolenbroek case DNS_OPT_SIT:
1793*00b67f09SDavid van Moolenbroek process_sit(client, &optbuf, optlen);
1794*00b67f09SDavid van Moolenbroek break;
1795*00b67f09SDavid van Moolenbroek #endif
1796*00b67f09SDavid van Moolenbroek case DNS_OPT_EXPIRE:
1797*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1798*00b67f09SDavid van Moolenbroek dns_nsstatscounter_expireopt);
1799*00b67f09SDavid van Moolenbroek client->attributes |= NS_CLIENTATTR_WANTEXPIRE;
1800*00b67f09SDavid van Moolenbroek isc_buffer_forward(&optbuf, optlen);
1801*00b67f09SDavid van Moolenbroek break;
1802*00b67f09SDavid van Moolenbroek default:
1803*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1804*00b67f09SDavid van Moolenbroek dns_nsstatscounter_otheropt);
1805*00b67f09SDavid van Moolenbroek isc_buffer_forward(&optbuf, optlen);
1806*00b67f09SDavid van Moolenbroek break;
1807*00b67f09SDavid van Moolenbroek }
1808*00b67f09SDavid van Moolenbroek }
1809*00b67f09SDavid van Moolenbroek }
1810*00b67f09SDavid van Moolenbroek
1811*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_edns0in);
1812*00b67f09SDavid van Moolenbroek client->attributes |= NS_CLIENTATTR_WANTOPT;
1813*00b67f09SDavid van Moolenbroek
1814*00b67f09SDavid van Moolenbroek cleanup:
1815*00b67f09SDavid van Moolenbroek return (result);
1816*00b67f09SDavid van Moolenbroek }
1817*00b67f09SDavid van Moolenbroek
1818*00b67f09SDavid van Moolenbroek /*
1819*00b67f09SDavid van Moolenbroek * Handle an incoming request event from the socket (UDP case)
1820*00b67f09SDavid van Moolenbroek * or tcpmsg (TCP case).
1821*00b67f09SDavid van Moolenbroek */
1822*00b67f09SDavid van Moolenbroek static void
client_request(isc_task_t * task,isc_event_t * event)1823*00b67f09SDavid van Moolenbroek client_request(isc_task_t *task, isc_event_t *event) {
1824*00b67f09SDavid van Moolenbroek ns_client_t *client;
1825*00b67f09SDavid van Moolenbroek isc_socketevent_t *sevent;
1826*00b67f09SDavid van Moolenbroek isc_result_t result;
1827*00b67f09SDavid van Moolenbroek isc_result_t sigresult = ISC_R_SUCCESS;
1828*00b67f09SDavid van Moolenbroek isc_buffer_t *buffer;
1829*00b67f09SDavid van Moolenbroek isc_buffer_t tbuffer;
1830*00b67f09SDavid van Moolenbroek dns_view_t *view;
1831*00b67f09SDavid van Moolenbroek dns_rdataset_t *opt;
1832*00b67f09SDavid van Moolenbroek dns_name_t *signame;
1833*00b67f09SDavid van Moolenbroek isc_boolean_t ra; /* Recursion available. */
1834*00b67f09SDavid van Moolenbroek isc_netaddr_t netaddr;
1835*00b67f09SDavid van Moolenbroek int match;
1836*00b67f09SDavid van Moolenbroek dns_messageid_t id;
1837*00b67f09SDavid van Moolenbroek unsigned int flags;
1838*00b67f09SDavid van Moolenbroek isc_boolean_t notimp;
1839*00b67f09SDavid van Moolenbroek
1840*00b67f09SDavid van Moolenbroek REQUIRE(event != NULL);
1841*00b67f09SDavid van Moolenbroek client = event->ev_arg;
1842*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
1843*00b67f09SDavid van Moolenbroek REQUIRE(task == client->task);
1844*00b67f09SDavid van Moolenbroek
1845*00b67f09SDavid van Moolenbroek INSIST(client->recursionquota == NULL);
1846*00b67f09SDavid van Moolenbroek
1847*00b67f09SDavid van Moolenbroek INSIST(client->state == (TCP_CLIENT(client) ?
1848*00b67f09SDavid van Moolenbroek NS_CLIENTSTATE_READING :
1849*00b67f09SDavid van Moolenbroek NS_CLIENTSTATE_READY));
1850*00b67f09SDavid van Moolenbroek
1851*00b67f09SDavid van Moolenbroek ns_client_requests++;
1852*00b67f09SDavid van Moolenbroek
1853*00b67f09SDavid van Moolenbroek if (event->ev_type == ISC_SOCKEVENT_RECVDONE) {
1854*00b67f09SDavid van Moolenbroek INSIST(!TCP_CLIENT(client));
1855*00b67f09SDavid van Moolenbroek sevent = (isc_socketevent_t *)event;
1856*00b67f09SDavid van Moolenbroek REQUIRE(sevent == client->recvevent);
1857*00b67f09SDavid van Moolenbroek isc_buffer_init(&tbuffer, sevent->region.base, sevent->n);
1858*00b67f09SDavid van Moolenbroek isc_buffer_add(&tbuffer, sevent->n);
1859*00b67f09SDavid van Moolenbroek buffer = &tbuffer;
1860*00b67f09SDavid van Moolenbroek result = sevent->result;
1861*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS) {
1862*00b67f09SDavid van Moolenbroek client->peeraddr = sevent->address;
1863*00b67f09SDavid van Moolenbroek client->peeraddr_valid = ISC_TRUE;
1864*00b67f09SDavid van Moolenbroek }
1865*00b67f09SDavid van Moolenbroek if ((sevent->attributes & ISC_SOCKEVENTATTR_DSCP) != 0) {
1866*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1867*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(90),
1868*00b67f09SDavid van Moolenbroek "received DSCP %d", sevent->dscp);
1869*00b67f09SDavid van Moolenbroek if (client->dscp == -1)
1870*00b67f09SDavid van Moolenbroek client->dscp = sevent->dscp;
1871*00b67f09SDavid van Moolenbroek }
1872*00b67f09SDavid van Moolenbroek if ((sevent->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
1873*00b67f09SDavid van Moolenbroek client->attributes |= NS_CLIENTATTR_PKTINFO;
1874*00b67f09SDavid van Moolenbroek client->pktinfo = sevent->pktinfo;
1875*00b67f09SDavid van Moolenbroek }
1876*00b67f09SDavid van Moolenbroek if ((sevent->attributes & ISC_SOCKEVENTATTR_MULTICAST) != 0)
1877*00b67f09SDavid van Moolenbroek client->attributes |= NS_CLIENTATTR_MULTICAST;
1878*00b67f09SDavid van Moolenbroek client->nrecvs--;
1879*00b67f09SDavid van Moolenbroek } else {
1880*00b67f09SDavid van Moolenbroek INSIST(TCP_CLIENT(client));
1881*00b67f09SDavid van Moolenbroek REQUIRE(event->ev_type == DNS_EVENT_TCPMSG);
1882*00b67f09SDavid van Moolenbroek REQUIRE(event->ev_sender == &client->tcpmsg);
1883*00b67f09SDavid van Moolenbroek buffer = &client->tcpmsg.buffer;
1884*00b67f09SDavid van Moolenbroek result = client->tcpmsg.result;
1885*00b67f09SDavid van Moolenbroek INSIST(client->nreads == 1);
1886*00b67f09SDavid van Moolenbroek /*
1887*00b67f09SDavid van Moolenbroek * client->peeraddr was set when the connection was accepted.
1888*00b67f09SDavid van Moolenbroek */
1889*00b67f09SDavid van Moolenbroek client->nreads--;
1890*00b67f09SDavid van Moolenbroek }
1891*00b67f09SDavid van Moolenbroek
1892*00b67f09SDavid van Moolenbroek if (exit_check(client))
1893*00b67f09SDavid van Moolenbroek goto cleanup;
1894*00b67f09SDavid van Moolenbroek client->state = client->newstate = NS_CLIENTSTATE_WORKING;
1895*00b67f09SDavid van Moolenbroek
1896*00b67f09SDavid van Moolenbroek isc_task_getcurrenttime(task, &client->requesttime);
1897*00b67f09SDavid van Moolenbroek client->now = client->requesttime;
1898*00b67f09SDavid van Moolenbroek
1899*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
1900*00b67f09SDavid van Moolenbroek if (TCP_CLIENT(client)) {
1901*00b67f09SDavid van Moolenbroek ns_client_next(client, result);
1902*00b67f09SDavid van Moolenbroek } else {
1903*00b67f09SDavid van Moolenbroek if (result != ISC_R_CANCELED)
1904*00b67f09SDavid van Moolenbroek isc_log_write(ns_g_lctx, NS_LOGCATEGORY_CLIENT,
1905*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT,
1906*00b67f09SDavid van Moolenbroek ISC_LOG_ERROR,
1907*00b67f09SDavid van Moolenbroek "UDP client handler shutting "
1908*00b67f09SDavid van Moolenbroek "down due to fatal receive "
1909*00b67f09SDavid van Moolenbroek "error: %s",
1910*00b67f09SDavid van Moolenbroek isc_result_totext(result));
1911*00b67f09SDavid van Moolenbroek isc_task_shutdown(client->task);
1912*00b67f09SDavid van Moolenbroek }
1913*00b67f09SDavid van Moolenbroek goto cleanup;
1914*00b67f09SDavid van Moolenbroek }
1915*00b67f09SDavid van Moolenbroek
1916*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
1917*00b67f09SDavid van Moolenbroek
1918*00b67f09SDavid van Moolenbroek #if NS_CLIENT_DROPPORT
1919*00b67f09SDavid van Moolenbroek if (ns_client_dropport(isc_sockaddr_getport(&client->peeraddr)) ==
1920*00b67f09SDavid van Moolenbroek DROPPORT_REQUEST) {
1921*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1922*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
1923*00b67f09SDavid van Moolenbroek "dropped request: suspicious port");
1924*00b67f09SDavid van Moolenbroek ns_client_next(client, ISC_R_SUCCESS);
1925*00b67f09SDavid van Moolenbroek goto cleanup;
1926*00b67f09SDavid van Moolenbroek }
1927*00b67f09SDavid van Moolenbroek #endif
1928*00b67f09SDavid van Moolenbroek
1929*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1930*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
1931*00b67f09SDavid van Moolenbroek "%s request",
1932*00b67f09SDavid van Moolenbroek TCP_CLIENT(client) ? "TCP" : "UDP");
1933*00b67f09SDavid van Moolenbroek
1934*00b67f09SDavid van Moolenbroek /*
1935*00b67f09SDavid van Moolenbroek * Check the blackhole ACL for UDP only, since TCP is done in
1936*00b67f09SDavid van Moolenbroek * client_newconn.
1937*00b67f09SDavid van Moolenbroek */
1938*00b67f09SDavid van Moolenbroek if (!TCP_CLIENT(client)) {
1939*00b67f09SDavid van Moolenbroek
1940*00b67f09SDavid van Moolenbroek if (ns_g_server->blackholeacl != NULL &&
1941*00b67f09SDavid van Moolenbroek dns_acl_match(&netaddr, NULL, ns_g_server->blackholeacl,
1942*00b67f09SDavid van Moolenbroek &ns_g_server->aclenv,
1943*00b67f09SDavid van Moolenbroek &match, NULL) == ISC_R_SUCCESS &&
1944*00b67f09SDavid van Moolenbroek match > 0)
1945*00b67f09SDavid van Moolenbroek {
1946*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1947*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
1948*00b67f09SDavid van Moolenbroek "blackholed UDP datagram");
1949*00b67f09SDavid van Moolenbroek ns_client_next(client, ISC_R_SUCCESS);
1950*00b67f09SDavid van Moolenbroek goto cleanup;
1951*00b67f09SDavid van Moolenbroek }
1952*00b67f09SDavid van Moolenbroek }
1953*00b67f09SDavid van Moolenbroek
1954*00b67f09SDavid van Moolenbroek /*
1955*00b67f09SDavid van Moolenbroek * Silently drop multicast requests for the present.
1956*00b67f09SDavid van Moolenbroek * XXXMPA revisit this as mDNS spec was published.
1957*00b67f09SDavid van Moolenbroek */
1958*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0) {
1959*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1960*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
1961*00b67f09SDavid van Moolenbroek "dropping multicast request");
1962*00b67f09SDavid van Moolenbroek ns_client_next(client, DNS_R_REFUSED);
1963*00b67f09SDavid van Moolenbroek goto cleanup;
1964*00b67f09SDavid van Moolenbroek }
1965*00b67f09SDavid van Moolenbroek
1966*00b67f09SDavid van Moolenbroek result = dns_message_peekheader(buffer, &id, &flags);
1967*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
1968*00b67f09SDavid van Moolenbroek /*
1969*00b67f09SDavid van Moolenbroek * There isn't enough header to determine whether
1970*00b67f09SDavid van Moolenbroek * this was a request or a response. Drop it.
1971*00b67f09SDavid van Moolenbroek */
1972*00b67f09SDavid van Moolenbroek ns_client_next(client, result);
1973*00b67f09SDavid van Moolenbroek goto cleanup;
1974*00b67f09SDavid van Moolenbroek }
1975*00b67f09SDavid van Moolenbroek
1976*00b67f09SDavid van Moolenbroek /*
1977*00b67f09SDavid van Moolenbroek * The client object handles requests, not responses.
1978*00b67f09SDavid van Moolenbroek * If this is a UDP response, forward it to the dispatcher.
1979*00b67f09SDavid van Moolenbroek * If it's a TCP response, discard it here.
1980*00b67f09SDavid van Moolenbroek */
1981*00b67f09SDavid van Moolenbroek if ((flags & DNS_MESSAGEFLAG_QR) != 0) {
1982*00b67f09SDavid van Moolenbroek if (TCP_CLIENT(client)) {
1983*00b67f09SDavid van Moolenbroek CTRACE("unexpected response");
1984*00b67f09SDavid van Moolenbroek ns_client_next(client, DNS_R_FORMERR);
1985*00b67f09SDavid van Moolenbroek goto cleanup;
1986*00b67f09SDavid van Moolenbroek } else {
1987*00b67f09SDavid van Moolenbroek dns_dispatch_importrecv(client->dispatch, event);
1988*00b67f09SDavid van Moolenbroek ns_client_next(client, ISC_R_SUCCESS);
1989*00b67f09SDavid van Moolenbroek goto cleanup;
1990*00b67f09SDavid van Moolenbroek }
1991*00b67f09SDavid van Moolenbroek }
1992*00b67f09SDavid van Moolenbroek
1993*00b67f09SDavid van Moolenbroek /*
1994*00b67f09SDavid van Moolenbroek * Update some statistics counters. Don't count responses.
1995*00b67f09SDavid van Moolenbroek */
1996*00b67f09SDavid van Moolenbroek if (isc_sockaddr_pf(&client->peeraddr) == PF_INET) {
1997*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
1998*00b67f09SDavid van Moolenbroek dns_nsstatscounter_requestv4);
1999*00b67f09SDavid van Moolenbroek } else {
2000*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
2001*00b67f09SDavid van Moolenbroek dns_nsstatscounter_requestv6);
2002*00b67f09SDavid van Moolenbroek }
2003*00b67f09SDavid van Moolenbroek if (TCP_CLIENT(client))
2004*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
2005*00b67f09SDavid van Moolenbroek dns_nsstatscounter_requesttcp);
2006*00b67f09SDavid van Moolenbroek
2007*00b67f09SDavid van Moolenbroek /*
2008*00b67f09SDavid van Moolenbroek * It's a request. Parse it.
2009*00b67f09SDavid van Moolenbroek */
2010*00b67f09SDavid van Moolenbroek result = dns_message_parse(client->message, buffer, 0);
2011*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
2012*00b67f09SDavid van Moolenbroek /*
2013*00b67f09SDavid van Moolenbroek * Parsing the request failed. Send a response
2014*00b67f09SDavid van Moolenbroek * (typically FORMERR or SERVFAIL).
2015*00b67f09SDavid van Moolenbroek */
2016*00b67f09SDavid van Moolenbroek ns_client_error(client, result);
2017*00b67f09SDavid van Moolenbroek goto cleanup;
2018*00b67f09SDavid van Moolenbroek }
2019*00b67f09SDavid van Moolenbroek
2020*00b67f09SDavid van Moolenbroek dns_opcodestats_increment(ns_g_server->opcodestats,
2021*00b67f09SDavid van Moolenbroek client->message->opcode);
2022*00b67f09SDavid van Moolenbroek switch (client->message->opcode) {
2023*00b67f09SDavid van Moolenbroek case dns_opcode_query:
2024*00b67f09SDavid van Moolenbroek case dns_opcode_update:
2025*00b67f09SDavid van Moolenbroek case dns_opcode_notify:
2026*00b67f09SDavid van Moolenbroek notimp = ISC_FALSE;
2027*00b67f09SDavid van Moolenbroek break;
2028*00b67f09SDavid van Moolenbroek case dns_opcode_iquery:
2029*00b67f09SDavid van Moolenbroek default:
2030*00b67f09SDavid van Moolenbroek notimp = ISC_TRUE;
2031*00b67f09SDavid van Moolenbroek break;
2032*00b67f09SDavid van Moolenbroek }
2033*00b67f09SDavid van Moolenbroek
2034*00b67f09SDavid van Moolenbroek client->message->rcode = dns_rcode_noerror;
2035*00b67f09SDavid van Moolenbroek
2036*00b67f09SDavid van Moolenbroek /* RFC1123 section 6.1.3.2 */
2037*00b67f09SDavid van Moolenbroek if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0)
2038*00b67f09SDavid van Moolenbroek client->message->flags &= ~DNS_MESSAGEFLAG_RD;
2039*00b67f09SDavid van Moolenbroek
2040*00b67f09SDavid van Moolenbroek /*
2041*00b67f09SDavid van Moolenbroek * Deal with EDNS.
2042*00b67f09SDavid van Moolenbroek */
2043*00b67f09SDavid van Moolenbroek if (ns_g_noedns)
2044*00b67f09SDavid van Moolenbroek opt = NULL;
2045*00b67f09SDavid van Moolenbroek else
2046*00b67f09SDavid van Moolenbroek opt = dns_message_getopt(client->message);
2047*00b67f09SDavid van Moolenbroek if (opt != NULL) {
2048*00b67f09SDavid van Moolenbroek /*
2049*00b67f09SDavid van Moolenbroek * Are we dropping all EDNS queries?
2050*00b67f09SDavid van Moolenbroek */
2051*00b67f09SDavid van Moolenbroek if (ns_g_dropedns) {
2052*00b67f09SDavid van Moolenbroek ns_client_next(client, ISC_R_SUCCESS);
2053*00b67f09SDavid van Moolenbroek goto cleanup;
2054*00b67f09SDavid van Moolenbroek }
2055*00b67f09SDavid van Moolenbroek result = process_opt(client, opt);
2056*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2057*00b67f09SDavid van Moolenbroek goto cleanup;
2058*00b67f09SDavid van Moolenbroek }
2059*00b67f09SDavid van Moolenbroek
2060*00b67f09SDavid van Moolenbroek if (client->message->rdclass == 0) {
2061*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2062*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
2063*00b67f09SDavid van Moolenbroek "message class could not be determined");
2064*00b67f09SDavid van Moolenbroek ns_client_dumpmessage(client,
2065*00b67f09SDavid van Moolenbroek "message class could not be determined");
2066*00b67f09SDavid van Moolenbroek ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
2067*00b67f09SDavid van Moolenbroek goto cleanup;
2068*00b67f09SDavid van Moolenbroek }
2069*00b67f09SDavid van Moolenbroek
2070*00b67f09SDavid van Moolenbroek /*
2071*00b67f09SDavid van Moolenbroek * Determine the destination address. If the receiving interface is
2072*00b67f09SDavid van Moolenbroek * bound to a specific address, we simply use it regardless of the
2073*00b67f09SDavid van Moolenbroek * address family. All IPv4 queries should fall into this case.
2074*00b67f09SDavid van Moolenbroek * Otherwise, if this is a TCP query, get the address from the
2075*00b67f09SDavid van Moolenbroek * receiving socket (this needs a system call and can be heavy).
2076*00b67f09SDavid van Moolenbroek * For IPv6 UDP queries, we get this from the pktinfo structure (if
2077*00b67f09SDavid van Moolenbroek * supported).
2078*00b67f09SDavid van Moolenbroek * If all the attempts fail (this can happen due to memory shortage,
2079*00b67f09SDavid van Moolenbroek * etc), we regard this as an error for safety.
2080*00b67f09SDavid van Moolenbroek */
2081*00b67f09SDavid van Moolenbroek if ((client->interface->flags & NS_INTERFACEFLAG_ANYADDR) == 0)
2082*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&client->destaddr,
2083*00b67f09SDavid van Moolenbroek &client->interface->addr);
2084*00b67f09SDavid van Moolenbroek else {
2085*00b67f09SDavid van Moolenbroek isc_sockaddr_t sockaddr;
2086*00b67f09SDavid van Moolenbroek result = ISC_R_FAILURE;
2087*00b67f09SDavid van Moolenbroek
2088*00b67f09SDavid van Moolenbroek if (TCP_CLIENT(client))
2089*00b67f09SDavid van Moolenbroek result = isc_socket_getsockname(client->tcpsocket,
2090*00b67f09SDavid van Moolenbroek &sockaddr);
2091*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
2092*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&client->destaddr, &sockaddr);
2093*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS &&
2094*00b67f09SDavid van Moolenbroek client->interface->addr.type.sa.sa_family == AF_INET6 &&
2095*00b67f09SDavid van Moolenbroek (client->attributes & NS_CLIENTATTR_PKTINFO) != 0) {
2096*00b67f09SDavid van Moolenbroek /*
2097*00b67f09SDavid van Moolenbroek * XXXJT technically, we should convert the receiving
2098*00b67f09SDavid van Moolenbroek * interface ID to a proper scope zone ID. However,
2099*00b67f09SDavid van Moolenbroek * due to the fact there is no standard API for this,
2100*00b67f09SDavid van Moolenbroek * we only handle link-local addresses and use the
2101*00b67f09SDavid van Moolenbroek * interface index as link ID. Despite the assumption,
2102*00b67f09SDavid van Moolenbroek * it should cover most typical cases.
2103*00b67f09SDavid van Moolenbroek */
2104*00b67f09SDavid van Moolenbroek isc_netaddr_fromin6(&client->destaddr,
2105*00b67f09SDavid van Moolenbroek &client->pktinfo.ipi6_addr);
2106*00b67f09SDavid van Moolenbroek if (IN6_IS_ADDR_LINKLOCAL(&client->pktinfo.ipi6_addr))
2107*00b67f09SDavid van Moolenbroek isc_netaddr_setzone(&client->destaddr,
2108*00b67f09SDavid van Moolenbroek client->pktinfo.ipi6_ifindex);
2109*00b67f09SDavid van Moolenbroek result = ISC_R_SUCCESS;
2110*00b67f09SDavid van Moolenbroek }
2111*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
2112*00b67f09SDavid van Moolenbroek UNEXPECTED_ERROR(__FILE__, __LINE__,
2113*00b67f09SDavid van Moolenbroek "failed to get request's "
2114*00b67f09SDavid van Moolenbroek "destination: %s",
2115*00b67f09SDavid van Moolenbroek isc_result_totext(result));
2116*00b67f09SDavid van Moolenbroek ns_client_next(client, ISC_R_SUCCESS);
2117*00b67f09SDavid van Moolenbroek goto cleanup;
2118*00b67f09SDavid van Moolenbroek }
2119*00b67f09SDavid van Moolenbroek }
2120*00b67f09SDavid van Moolenbroek
2121*00b67f09SDavid van Moolenbroek /*
2122*00b67f09SDavid van Moolenbroek * Find a view that matches the client's source address.
2123*00b67f09SDavid van Moolenbroek */
2124*00b67f09SDavid van Moolenbroek for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
2125*00b67f09SDavid van Moolenbroek view != NULL;
2126*00b67f09SDavid van Moolenbroek view = ISC_LIST_NEXT(view, link)) {
2127*00b67f09SDavid van Moolenbroek if (client->message->rdclass == view->rdclass ||
2128*00b67f09SDavid van Moolenbroek client->message->rdclass == dns_rdataclass_any)
2129*00b67f09SDavid van Moolenbroek {
2130*00b67f09SDavid van Moolenbroek dns_name_t *tsig = NULL;
2131*00b67f09SDavid van Moolenbroek
2132*00b67f09SDavid van Moolenbroek sigresult = dns_message_rechecksig(client->message,
2133*00b67f09SDavid van Moolenbroek view);
2134*00b67f09SDavid van Moolenbroek if (sigresult == ISC_R_SUCCESS)
2135*00b67f09SDavid van Moolenbroek tsig = dns_tsigkey_identity(client->message->tsigkey);
2136*00b67f09SDavid van Moolenbroek
2137*00b67f09SDavid van Moolenbroek if (allowed(&netaddr, tsig, view->matchclients) &&
2138*00b67f09SDavid van Moolenbroek allowed(&client->destaddr, tsig,
2139*00b67f09SDavid van Moolenbroek view->matchdestinations) &&
2140*00b67f09SDavid van Moolenbroek !((client->message->flags & DNS_MESSAGEFLAG_RD)
2141*00b67f09SDavid van Moolenbroek == 0 && view->matchrecursiveonly))
2142*00b67f09SDavid van Moolenbroek {
2143*00b67f09SDavid van Moolenbroek dns_view_attach(view, &client->view);
2144*00b67f09SDavid van Moolenbroek break;
2145*00b67f09SDavid van Moolenbroek }
2146*00b67f09SDavid van Moolenbroek }
2147*00b67f09SDavid van Moolenbroek }
2148*00b67f09SDavid van Moolenbroek
2149*00b67f09SDavid van Moolenbroek if (view == NULL) {
2150*00b67f09SDavid van Moolenbroek char classname[DNS_RDATACLASS_FORMATSIZE];
2151*00b67f09SDavid van Moolenbroek
2152*00b67f09SDavid van Moolenbroek /*
2153*00b67f09SDavid van Moolenbroek * Do a dummy TSIG verification attempt so that the
2154*00b67f09SDavid van Moolenbroek * response will have a TSIG if the query did, as
2155*00b67f09SDavid van Moolenbroek * required by RFC2845.
2156*00b67f09SDavid van Moolenbroek */
2157*00b67f09SDavid van Moolenbroek isc_buffer_t b;
2158*00b67f09SDavid van Moolenbroek isc_region_t *r;
2159*00b67f09SDavid van Moolenbroek
2160*00b67f09SDavid van Moolenbroek dns_message_resetsig(client->message);
2161*00b67f09SDavid van Moolenbroek
2162*00b67f09SDavid van Moolenbroek r = dns_message_getrawmessage(client->message);
2163*00b67f09SDavid van Moolenbroek isc_buffer_init(&b, r->base, r->length);
2164*00b67f09SDavid van Moolenbroek isc_buffer_add(&b, r->length);
2165*00b67f09SDavid van Moolenbroek (void)dns_tsig_verify(&b, client->message, NULL, NULL);
2166*00b67f09SDavid van Moolenbroek
2167*00b67f09SDavid van Moolenbroek dns_rdataclass_format(client->message->rdclass, classname,
2168*00b67f09SDavid van Moolenbroek sizeof(classname));
2169*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2170*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
2171*00b67f09SDavid van Moolenbroek "no matching view in class '%s'", classname);
2172*00b67f09SDavid van Moolenbroek ns_client_dumpmessage(client, "no matching view in class");
2173*00b67f09SDavid van Moolenbroek ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
2174*00b67f09SDavid van Moolenbroek goto cleanup;
2175*00b67f09SDavid van Moolenbroek }
2176*00b67f09SDavid van Moolenbroek
2177*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2178*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5),
2179*00b67f09SDavid van Moolenbroek "using view '%s'", view->name);
2180*00b67f09SDavid van Moolenbroek
2181*00b67f09SDavid van Moolenbroek /*
2182*00b67f09SDavid van Moolenbroek * Check for a signature. We log bad signatures regardless of
2183*00b67f09SDavid van Moolenbroek * whether they ultimately cause the request to be rejected or
2184*00b67f09SDavid van Moolenbroek * not. We do not log the lack of a signature unless we are
2185*00b67f09SDavid van Moolenbroek * debugging.
2186*00b67f09SDavid van Moolenbroek */
2187*00b67f09SDavid van Moolenbroek client->signer = NULL;
2188*00b67f09SDavid van Moolenbroek dns_name_init(&client->signername, NULL);
2189*00b67f09SDavid van Moolenbroek result = dns_message_signer(client->message, &client->signername);
2190*00b67f09SDavid van Moolenbroek if (result != ISC_R_NOTFOUND) {
2191*00b67f09SDavid van Moolenbroek signame = NULL;
2192*00b67f09SDavid van Moolenbroek if (dns_message_gettsig(client->message, &signame) != NULL) {
2193*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
2194*00b67f09SDavid van Moolenbroek dns_nsstatscounter_tsigin);
2195*00b67f09SDavid van Moolenbroek } else {
2196*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
2197*00b67f09SDavid van Moolenbroek dns_nsstatscounter_sig0in);
2198*00b67f09SDavid van Moolenbroek }
2199*00b67f09SDavid van Moolenbroek
2200*00b67f09SDavid van Moolenbroek }
2201*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS) {
2202*00b67f09SDavid van Moolenbroek char namebuf[DNS_NAME_FORMATSIZE];
2203*00b67f09SDavid van Moolenbroek dns_name_format(&client->signername, namebuf, sizeof(namebuf));
2204*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2205*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2206*00b67f09SDavid van Moolenbroek "request has valid signature: %s", namebuf);
2207*00b67f09SDavid van Moolenbroek client->signer = &client->signername;
2208*00b67f09SDavid van Moolenbroek } else if (result == ISC_R_NOTFOUND) {
2209*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2210*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2211*00b67f09SDavid van Moolenbroek "request is not signed");
2212*00b67f09SDavid van Moolenbroek } else if (result == DNS_R_NOIDENTITY) {
2213*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2214*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2215*00b67f09SDavid van Moolenbroek "request is signed by a nonauthoritative key");
2216*00b67f09SDavid van Moolenbroek } else {
2217*00b67f09SDavid van Moolenbroek char tsigrcode[64];
2218*00b67f09SDavid van Moolenbroek isc_buffer_t b;
2219*00b67f09SDavid van Moolenbroek dns_rcode_t status;
2220*00b67f09SDavid van Moolenbroek isc_result_t tresult;
2221*00b67f09SDavid van Moolenbroek
2222*00b67f09SDavid van Moolenbroek /* There is a signature, but it is bad. */
2223*00b67f09SDavid van Moolenbroek isc_stats_increment(ns_g_server->nsstats,
2224*00b67f09SDavid van Moolenbroek dns_nsstatscounter_invalidsig);
2225*00b67f09SDavid van Moolenbroek signame = NULL;
2226*00b67f09SDavid van Moolenbroek if (dns_message_gettsig(client->message, &signame) != NULL) {
2227*00b67f09SDavid van Moolenbroek char namebuf[DNS_NAME_FORMATSIZE];
2228*00b67f09SDavid van Moolenbroek char cnamebuf[DNS_NAME_FORMATSIZE];
2229*00b67f09SDavid van Moolenbroek dns_name_format(signame, namebuf, sizeof(namebuf));
2230*00b67f09SDavid van Moolenbroek status = client->message->tsigstatus;
2231*00b67f09SDavid van Moolenbroek isc_buffer_init(&b, tsigrcode, sizeof(tsigrcode) - 1);
2232*00b67f09SDavid van Moolenbroek tresult = dns_tsigrcode_totext(status, &b);
2233*00b67f09SDavid van Moolenbroek INSIST(tresult == ISC_R_SUCCESS);
2234*00b67f09SDavid van Moolenbroek tsigrcode[isc_buffer_usedlength(&b)] = '\0';
2235*00b67f09SDavid van Moolenbroek if (client->message->tsigkey->generated) {
2236*00b67f09SDavid van Moolenbroek dns_name_format(client->message->tsigkey->creator,
2237*00b67f09SDavid van Moolenbroek cnamebuf, sizeof(cnamebuf));
2238*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2239*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT,
2240*00b67f09SDavid van Moolenbroek ISC_LOG_ERROR,
2241*00b67f09SDavid van Moolenbroek "request has invalid signature: "
2242*00b67f09SDavid van Moolenbroek "TSIG %s (%s): %s (%s)", namebuf,
2243*00b67f09SDavid van Moolenbroek cnamebuf,
2244*00b67f09SDavid van Moolenbroek isc_result_totext(result),
2245*00b67f09SDavid van Moolenbroek tsigrcode);
2246*00b67f09SDavid van Moolenbroek } else {
2247*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2248*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT,
2249*00b67f09SDavid van Moolenbroek ISC_LOG_ERROR,
2250*00b67f09SDavid van Moolenbroek "request has invalid signature: "
2251*00b67f09SDavid van Moolenbroek "TSIG %s: %s (%s)", namebuf,
2252*00b67f09SDavid van Moolenbroek isc_result_totext(result),
2253*00b67f09SDavid van Moolenbroek tsigrcode);
2254*00b67f09SDavid van Moolenbroek }
2255*00b67f09SDavid van Moolenbroek } else {
2256*00b67f09SDavid van Moolenbroek status = client->message->sig0status;
2257*00b67f09SDavid van Moolenbroek isc_buffer_init(&b, tsigrcode, sizeof(tsigrcode) - 1);
2258*00b67f09SDavid van Moolenbroek tresult = dns_tsigrcode_totext(status, &b);
2259*00b67f09SDavid van Moolenbroek INSIST(tresult == ISC_R_SUCCESS);
2260*00b67f09SDavid van Moolenbroek tsigrcode[isc_buffer_usedlength(&b)] = '\0';
2261*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2262*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
2263*00b67f09SDavid van Moolenbroek "request has invalid signature: %s (%s)",
2264*00b67f09SDavid van Moolenbroek isc_result_totext(result), tsigrcode);
2265*00b67f09SDavid van Moolenbroek }
2266*00b67f09SDavid van Moolenbroek /*
2267*00b67f09SDavid van Moolenbroek * Accept update messages signed by unknown keys so that
2268*00b67f09SDavid van Moolenbroek * update forwarding works transparently through slaves
2269*00b67f09SDavid van Moolenbroek * that don't have all the same keys as the master.
2270*00b67f09SDavid van Moolenbroek */
2271*00b67f09SDavid van Moolenbroek if (!(client->message->tsigstatus == dns_tsigerror_badkey &&
2272*00b67f09SDavid van Moolenbroek client->message->opcode == dns_opcode_update)) {
2273*00b67f09SDavid van Moolenbroek ns_client_error(client, sigresult);
2274*00b67f09SDavid van Moolenbroek goto cleanup;
2275*00b67f09SDavid van Moolenbroek }
2276*00b67f09SDavid van Moolenbroek }
2277*00b67f09SDavid van Moolenbroek
2278*00b67f09SDavid van Moolenbroek /*
2279*00b67f09SDavid van Moolenbroek * Decide whether recursive service is available to this client.
2280*00b67f09SDavid van Moolenbroek * We do this here rather than in the query code so that we can
2281*00b67f09SDavid van Moolenbroek * set the RA bit correctly on all kinds of responses, not just
2282*00b67f09SDavid van Moolenbroek * responses to ordinary queries. Note if you can't query the
2283*00b67f09SDavid van Moolenbroek * cache there is no point in setting RA.
2284*00b67f09SDavid van Moolenbroek */
2285*00b67f09SDavid van Moolenbroek ra = ISC_FALSE;
2286*00b67f09SDavid van Moolenbroek if (client->view->resolver != NULL &&
2287*00b67f09SDavid van Moolenbroek client->view->recursion == ISC_TRUE &&
2288*00b67f09SDavid van Moolenbroek ns_client_checkaclsilent(client, NULL,
2289*00b67f09SDavid van Moolenbroek client->view->recursionacl,
2290*00b67f09SDavid van Moolenbroek ISC_TRUE) == ISC_R_SUCCESS &&
2291*00b67f09SDavid van Moolenbroek ns_client_checkaclsilent(client, NULL,
2292*00b67f09SDavid van Moolenbroek client->view->cacheacl,
2293*00b67f09SDavid van Moolenbroek ISC_TRUE) == ISC_R_SUCCESS &&
2294*00b67f09SDavid van Moolenbroek ns_client_checkaclsilent(client, &client->destaddr,
2295*00b67f09SDavid van Moolenbroek client->view->recursiononacl,
2296*00b67f09SDavid van Moolenbroek ISC_TRUE) == ISC_R_SUCCESS &&
2297*00b67f09SDavid van Moolenbroek ns_client_checkaclsilent(client, &client->destaddr,
2298*00b67f09SDavid van Moolenbroek client->view->cacheonacl,
2299*00b67f09SDavid van Moolenbroek ISC_TRUE) == ISC_R_SUCCESS)
2300*00b67f09SDavid van Moolenbroek ra = ISC_TRUE;
2301*00b67f09SDavid van Moolenbroek
2302*00b67f09SDavid van Moolenbroek if (ra == ISC_TRUE)
2303*00b67f09SDavid van Moolenbroek client->attributes |= NS_CLIENTATTR_RA;
2304*00b67f09SDavid van Moolenbroek
2305*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_CLIENT,
2306*00b67f09SDavid van Moolenbroek ISC_LOG_DEBUG(3), ra ? "recursion available" :
2307*00b67f09SDavid van Moolenbroek "recursion not available");
2308*00b67f09SDavid van Moolenbroek
2309*00b67f09SDavid van Moolenbroek /*
2310*00b67f09SDavid van Moolenbroek * Adjust maximum UDP response size for this client.
2311*00b67f09SDavid van Moolenbroek */
2312*00b67f09SDavid van Moolenbroek if (client->udpsize > 512) {
2313*00b67f09SDavid van Moolenbroek dns_peer_t *peer = NULL;
2314*00b67f09SDavid van Moolenbroek isc_uint16_t udpsize = view->maxudp;
2315*00b67f09SDavid van Moolenbroek (void) dns_peerlist_peerbyaddr(view->peers, &netaddr, &peer);
2316*00b67f09SDavid van Moolenbroek if (peer != NULL)
2317*00b67f09SDavid van Moolenbroek dns_peer_getmaxudp(peer, &udpsize);
2318*00b67f09SDavid van Moolenbroek if (client->udpsize > udpsize)
2319*00b67f09SDavid van Moolenbroek client->udpsize = udpsize;
2320*00b67f09SDavid van Moolenbroek }
2321*00b67f09SDavid van Moolenbroek
2322*00b67f09SDavid van Moolenbroek /*
2323*00b67f09SDavid van Moolenbroek * Dispatch the request.
2324*00b67f09SDavid van Moolenbroek */
2325*00b67f09SDavid van Moolenbroek switch (client->message->opcode) {
2326*00b67f09SDavid van Moolenbroek case dns_opcode_query:
2327*00b67f09SDavid van Moolenbroek CTRACE("query");
2328*00b67f09SDavid van Moolenbroek ns_query_start(client);
2329*00b67f09SDavid van Moolenbroek break;
2330*00b67f09SDavid van Moolenbroek case dns_opcode_update:
2331*00b67f09SDavid van Moolenbroek CTRACE("update");
2332*00b67f09SDavid van Moolenbroek ns_client_settimeout(client, 60);
2333*00b67f09SDavid van Moolenbroek ns_update_start(client, sigresult);
2334*00b67f09SDavid van Moolenbroek break;
2335*00b67f09SDavid van Moolenbroek case dns_opcode_notify:
2336*00b67f09SDavid van Moolenbroek CTRACE("notify");
2337*00b67f09SDavid van Moolenbroek ns_client_settimeout(client, 60);
2338*00b67f09SDavid van Moolenbroek ns_notify_start(client);
2339*00b67f09SDavid van Moolenbroek break;
2340*00b67f09SDavid van Moolenbroek case dns_opcode_iquery:
2341*00b67f09SDavid van Moolenbroek CTRACE("iquery");
2342*00b67f09SDavid van Moolenbroek ns_client_error(client, DNS_R_NOTIMP);
2343*00b67f09SDavid van Moolenbroek break;
2344*00b67f09SDavid van Moolenbroek default:
2345*00b67f09SDavid van Moolenbroek CTRACE("unknown opcode");
2346*00b67f09SDavid van Moolenbroek ns_client_error(client, DNS_R_NOTIMP);
2347*00b67f09SDavid van Moolenbroek }
2348*00b67f09SDavid van Moolenbroek
2349*00b67f09SDavid van Moolenbroek cleanup:
2350*00b67f09SDavid van Moolenbroek return;
2351*00b67f09SDavid van Moolenbroek }
2352*00b67f09SDavid van Moolenbroek
2353*00b67f09SDavid van Moolenbroek static void
client_timeout(isc_task_t * task,isc_event_t * event)2354*00b67f09SDavid van Moolenbroek client_timeout(isc_task_t *task, isc_event_t *event) {
2355*00b67f09SDavid van Moolenbroek ns_client_t *client;
2356*00b67f09SDavid van Moolenbroek
2357*00b67f09SDavid van Moolenbroek REQUIRE(event != NULL);
2358*00b67f09SDavid van Moolenbroek REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
2359*00b67f09SDavid van Moolenbroek event->ev_type == ISC_TIMEREVENT_IDLE);
2360*00b67f09SDavid van Moolenbroek client = event->ev_arg;
2361*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
2362*00b67f09SDavid van Moolenbroek REQUIRE(task == client->task);
2363*00b67f09SDavid van Moolenbroek REQUIRE(client->timer != NULL);
2364*00b67f09SDavid van Moolenbroek
2365*00b67f09SDavid van Moolenbroek UNUSED(task);
2366*00b67f09SDavid van Moolenbroek
2367*00b67f09SDavid van Moolenbroek CTRACE("timeout");
2368*00b67f09SDavid van Moolenbroek
2369*00b67f09SDavid van Moolenbroek isc_event_free(&event);
2370*00b67f09SDavid van Moolenbroek
2371*00b67f09SDavid van Moolenbroek if (client->shutdown != NULL) {
2372*00b67f09SDavid van Moolenbroek (client->shutdown)(client->shutdown_arg, ISC_R_TIMEDOUT);
2373*00b67f09SDavid van Moolenbroek client->shutdown = NULL;
2374*00b67f09SDavid van Moolenbroek client->shutdown_arg = NULL;
2375*00b67f09SDavid van Moolenbroek }
2376*00b67f09SDavid van Moolenbroek
2377*00b67f09SDavid van Moolenbroek if (client->newstate > NS_CLIENTSTATE_READY)
2378*00b67f09SDavid van Moolenbroek client->newstate = NS_CLIENTSTATE_READY;
2379*00b67f09SDavid van Moolenbroek (void)exit_check(client);
2380*00b67f09SDavid van Moolenbroek }
2381*00b67f09SDavid van Moolenbroek
2382*00b67f09SDavid van Moolenbroek static isc_result_t
get_clientmctx(ns_clientmgr_t * manager,isc_mem_t ** mctxp)2383*00b67f09SDavid van Moolenbroek get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp) {
2384*00b67f09SDavid van Moolenbroek isc_mem_t *clientmctx;
2385*00b67f09SDavid van Moolenbroek isc_result_t result;
2386*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2387*00b67f09SDavid van Moolenbroek unsigned int nextmctx;
2388*00b67f09SDavid van Moolenbroek #endif
2389*00b67f09SDavid van Moolenbroek
2390*00b67f09SDavid van Moolenbroek MTRACE("clientmctx");
2391*00b67f09SDavid van Moolenbroek
2392*00b67f09SDavid van Moolenbroek /*
2393*00b67f09SDavid van Moolenbroek * Caller must be holding the manager lock.
2394*00b67f09SDavid van Moolenbroek */
2395*00b67f09SDavid van Moolenbroek if (ns_g_clienttest) {
2396*00b67f09SDavid van Moolenbroek result = isc_mem_create(0, 0, mctxp);
2397*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
2398*00b67f09SDavid van Moolenbroek isc_mem_setname(*mctxp, "client", NULL);
2399*00b67f09SDavid van Moolenbroek return (result);
2400*00b67f09SDavid van Moolenbroek }
2401*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2402*00b67f09SDavid van Moolenbroek nextmctx = manager->nextmctx++;
2403*00b67f09SDavid van Moolenbroek if (manager->nextmctx == NMCTXS)
2404*00b67f09SDavid van Moolenbroek manager->nextmctx = 0;
2405*00b67f09SDavid van Moolenbroek
2406*00b67f09SDavid van Moolenbroek INSIST(nextmctx < NMCTXS);
2407*00b67f09SDavid van Moolenbroek
2408*00b67f09SDavid van Moolenbroek clientmctx = manager->mctxpool[nextmctx];
2409*00b67f09SDavid van Moolenbroek if (clientmctx == NULL) {
2410*00b67f09SDavid van Moolenbroek result = isc_mem_create(0, 0, &clientmctx);
2411*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2412*00b67f09SDavid van Moolenbroek return (result);
2413*00b67f09SDavid van Moolenbroek isc_mem_setname(clientmctx, "client", NULL);
2414*00b67f09SDavid van Moolenbroek
2415*00b67f09SDavid van Moolenbroek manager->mctxpool[nextmctx] = clientmctx;
2416*00b67f09SDavid van Moolenbroek }
2417*00b67f09SDavid van Moolenbroek #else
2418*00b67f09SDavid van Moolenbroek clientmctx = manager->mctx;
2419*00b67f09SDavid van Moolenbroek #endif
2420*00b67f09SDavid van Moolenbroek
2421*00b67f09SDavid van Moolenbroek isc_mem_attach(clientmctx, mctxp);
2422*00b67f09SDavid van Moolenbroek
2423*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
2424*00b67f09SDavid van Moolenbroek }
2425*00b67f09SDavid van Moolenbroek
2426*00b67f09SDavid van Moolenbroek static isc_result_t
client_create(ns_clientmgr_t * manager,ns_client_t ** clientp)2427*00b67f09SDavid van Moolenbroek client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
2428*00b67f09SDavid van Moolenbroek ns_client_t *client;
2429*00b67f09SDavid van Moolenbroek isc_result_t result;
2430*00b67f09SDavid van Moolenbroek isc_mem_t *mctx = NULL;
2431*00b67f09SDavid van Moolenbroek
2432*00b67f09SDavid van Moolenbroek /*
2433*00b67f09SDavid van Moolenbroek * Caller must be holding the manager lock.
2434*00b67f09SDavid van Moolenbroek *
2435*00b67f09SDavid van Moolenbroek * Note: creating a client does not add the client to the
2436*00b67f09SDavid van Moolenbroek * manager's client list or set the client's manager pointer.
2437*00b67f09SDavid van Moolenbroek * The caller is responsible for that.
2438*00b67f09SDavid van Moolenbroek */
2439*00b67f09SDavid van Moolenbroek
2440*00b67f09SDavid van Moolenbroek REQUIRE(clientp != NULL && *clientp == NULL);
2441*00b67f09SDavid van Moolenbroek
2442*00b67f09SDavid van Moolenbroek result = get_clientmctx(manager, &mctx);
2443*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2444*00b67f09SDavid van Moolenbroek return (result);
2445*00b67f09SDavid van Moolenbroek
2446*00b67f09SDavid van Moolenbroek client = isc_mem_get(mctx, sizeof(*client));
2447*00b67f09SDavid van Moolenbroek if (client == NULL) {
2448*00b67f09SDavid van Moolenbroek isc_mem_detach(&mctx);
2449*00b67f09SDavid van Moolenbroek return (ISC_R_NOMEMORY);
2450*00b67f09SDavid van Moolenbroek }
2451*00b67f09SDavid van Moolenbroek client->mctx = mctx;
2452*00b67f09SDavid van Moolenbroek
2453*00b67f09SDavid van Moolenbroek client->task = NULL;
2454*00b67f09SDavid van Moolenbroek result = isc_task_create(manager->taskmgr, 0, &client->task);
2455*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2456*00b67f09SDavid van Moolenbroek goto cleanup_client;
2457*00b67f09SDavid van Moolenbroek isc_task_setname(client->task, "client", client);
2458*00b67f09SDavid van Moolenbroek
2459*00b67f09SDavid van Moolenbroek client->timer = NULL;
2460*00b67f09SDavid van Moolenbroek result = isc_timer_create(manager->timermgr, isc_timertype_inactive,
2461*00b67f09SDavid van Moolenbroek NULL, NULL, client->task, client_timeout,
2462*00b67f09SDavid van Moolenbroek client, &client->timer);
2463*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2464*00b67f09SDavid van Moolenbroek goto cleanup_task;
2465*00b67f09SDavid van Moolenbroek client->timerset = ISC_FALSE;
2466*00b67f09SDavid van Moolenbroek
2467*00b67f09SDavid van Moolenbroek client->delaytimer = NULL;
2468*00b67f09SDavid van Moolenbroek
2469*00b67f09SDavid van Moolenbroek client->message = NULL;
2470*00b67f09SDavid van Moolenbroek result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
2471*00b67f09SDavid van Moolenbroek &client->message);
2472*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2473*00b67f09SDavid van Moolenbroek goto cleanup_timer;
2474*00b67f09SDavid van Moolenbroek
2475*00b67f09SDavid van Moolenbroek /* XXXRTH Hardwired constants */
2476*00b67f09SDavid van Moolenbroek
2477*00b67f09SDavid van Moolenbroek client->sendevent = isc_socket_socketevent(client->mctx, client,
2478*00b67f09SDavid van Moolenbroek ISC_SOCKEVENT_SENDDONE,
2479*00b67f09SDavid van Moolenbroek client_senddone, client);
2480*00b67f09SDavid van Moolenbroek if (client->sendevent == NULL) {
2481*00b67f09SDavid van Moolenbroek result = ISC_R_NOMEMORY;
2482*00b67f09SDavid van Moolenbroek goto cleanup_message;
2483*00b67f09SDavid van Moolenbroek }
2484*00b67f09SDavid van Moolenbroek
2485*00b67f09SDavid van Moolenbroek client->recvbuf = isc_mem_get(client->mctx, RECV_BUFFER_SIZE);
2486*00b67f09SDavid van Moolenbroek if (client->recvbuf == NULL) {
2487*00b67f09SDavid van Moolenbroek result = ISC_R_NOMEMORY;
2488*00b67f09SDavid van Moolenbroek goto cleanup_sendevent;
2489*00b67f09SDavid van Moolenbroek }
2490*00b67f09SDavid van Moolenbroek
2491*00b67f09SDavid van Moolenbroek client->recvevent = isc_socket_socketevent(client->mctx, client,
2492*00b67f09SDavid van Moolenbroek ISC_SOCKEVENT_RECVDONE,
2493*00b67f09SDavid van Moolenbroek client_request, client);
2494*00b67f09SDavid van Moolenbroek if (client->recvevent == NULL) {
2495*00b67f09SDavid van Moolenbroek result = ISC_R_NOMEMORY;
2496*00b67f09SDavid van Moolenbroek goto cleanup_recvbuf;
2497*00b67f09SDavid van Moolenbroek }
2498*00b67f09SDavid van Moolenbroek
2499*00b67f09SDavid van Moolenbroek client->magic = NS_CLIENT_MAGIC;
2500*00b67f09SDavid van Moolenbroek client->manager = NULL;
2501*00b67f09SDavid van Moolenbroek client->state = NS_CLIENTSTATE_INACTIVE;
2502*00b67f09SDavid van Moolenbroek client->newstate = NS_CLIENTSTATE_MAX;
2503*00b67f09SDavid van Moolenbroek client->naccepts = 0;
2504*00b67f09SDavid van Moolenbroek client->nreads = 0;
2505*00b67f09SDavid van Moolenbroek client->nsends = 0;
2506*00b67f09SDavid van Moolenbroek client->nrecvs = 0;
2507*00b67f09SDavid van Moolenbroek client->nupdates = 0;
2508*00b67f09SDavid van Moolenbroek client->nctls = 0;
2509*00b67f09SDavid van Moolenbroek client->references = 0;
2510*00b67f09SDavid van Moolenbroek client->attributes = 0;
2511*00b67f09SDavid van Moolenbroek client->view = NULL;
2512*00b67f09SDavid van Moolenbroek client->dispatch = NULL;
2513*00b67f09SDavid van Moolenbroek client->udpsocket = NULL;
2514*00b67f09SDavid van Moolenbroek client->tcplistener = NULL;
2515*00b67f09SDavid van Moolenbroek client->tcpsocket = NULL;
2516*00b67f09SDavid van Moolenbroek client->tcpmsg_valid = ISC_FALSE;
2517*00b67f09SDavid van Moolenbroek client->tcpbuf = NULL;
2518*00b67f09SDavid van Moolenbroek client->opt = NULL;
2519*00b67f09SDavid van Moolenbroek client->udpsize = 512;
2520*00b67f09SDavid van Moolenbroek client->dscp = -1;
2521*00b67f09SDavid van Moolenbroek client->extflags = 0;
2522*00b67f09SDavid van Moolenbroek client->ednsversion = -1;
2523*00b67f09SDavid van Moolenbroek client->next = NULL;
2524*00b67f09SDavid van Moolenbroek client->shutdown = NULL;
2525*00b67f09SDavid van Moolenbroek client->shutdown_arg = NULL;
2526*00b67f09SDavid van Moolenbroek client->signer = NULL;
2527*00b67f09SDavid van Moolenbroek dns_name_init(&client->signername, NULL);
2528*00b67f09SDavid van Moolenbroek client->mortal = ISC_FALSE;
2529*00b67f09SDavid van Moolenbroek client->tcpquota = NULL;
2530*00b67f09SDavid van Moolenbroek client->recursionquota = NULL;
2531*00b67f09SDavid van Moolenbroek client->interface = NULL;
2532*00b67f09SDavid van Moolenbroek client->peeraddr_valid = ISC_FALSE;
2533*00b67f09SDavid van Moolenbroek #ifdef ALLOW_FILTER_AAAA
2534*00b67f09SDavid van Moolenbroek client->filter_aaaa = dns_aaaa_ok;
2535*00b67f09SDavid van Moolenbroek #endif
2536*00b67f09SDavid van Moolenbroek client->needshutdown = ns_g_clienttest;
2537*00b67f09SDavid van Moolenbroek
2538*00b67f09SDavid van Moolenbroek ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL,
2539*00b67f09SDavid van Moolenbroek NS_EVENT_CLIENTCONTROL, client_start, client, client,
2540*00b67f09SDavid van Moolenbroek NULL, NULL);
2541*00b67f09SDavid van Moolenbroek /*
2542*00b67f09SDavid van Moolenbroek * Initialize FORMERR cache to sentinel value that will not match
2543*00b67f09SDavid van Moolenbroek * any actual FORMERR response.
2544*00b67f09SDavid van Moolenbroek */
2545*00b67f09SDavid van Moolenbroek isc_sockaddr_any(&client->formerrcache.addr);
2546*00b67f09SDavid van Moolenbroek client->formerrcache.time = 0;
2547*00b67f09SDavid van Moolenbroek client->formerrcache.id = 0;
2548*00b67f09SDavid van Moolenbroek ISC_LINK_INIT(client, link);
2549*00b67f09SDavid van Moolenbroek ISC_LINK_INIT(client, rlink);
2550*00b67f09SDavid van Moolenbroek ISC_QLINK_INIT(client, ilink);
2551*00b67f09SDavid van Moolenbroek
2552*00b67f09SDavid van Moolenbroek /*
2553*00b67f09SDavid van Moolenbroek * We call the init routines for the various kinds of client here,
2554*00b67f09SDavid van Moolenbroek * after we have created an otherwise valid client, because some
2555*00b67f09SDavid van Moolenbroek * of them call routines that REQUIRE(NS_CLIENT_VALID(client)).
2556*00b67f09SDavid van Moolenbroek */
2557*00b67f09SDavid van Moolenbroek result = ns_query_init(client);
2558*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2559*00b67f09SDavid van Moolenbroek goto cleanup_recvevent;
2560*00b67f09SDavid van Moolenbroek
2561*00b67f09SDavid van Moolenbroek result = isc_task_onshutdown(client->task, client_shutdown, client);
2562*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2563*00b67f09SDavid van Moolenbroek goto cleanup_query;
2564*00b67f09SDavid van Moolenbroek
2565*00b67f09SDavid van Moolenbroek CTRACE("create");
2566*00b67f09SDavid van Moolenbroek
2567*00b67f09SDavid van Moolenbroek *clientp = client;
2568*00b67f09SDavid van Moolenbroek
2569*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
2570*00b67f09SDavid van Moolenbroek
2571*00b67f09SDavid van Moolenbroek cleanup_query:
2572*00b67f09SDavid van Moolenbroek ns_query_free(client);
2573*00b67f09SDavid van Moolenbroek
2574*00b67f09SDavid van Moolenbroek cleanup_recvevent:
2575*00b67f09SDavid van Moolenbroek isc_event_free((isc_event_t **)&client->recvevent);
2576*00b67f09SDavid van Moolenbroek
2577*00b67f09SDavid van Moolenbroek cleanup_recvbuf:
2578*00b67f09SDavid van Moolenbroek isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
2579*00b67f09SDavid van Moolenbroek
2580*00b67f09SDavid van Moolenbroek cleanup_sendevent:
2581*00b67f09SDavid van Moolenbroek isc_event_free((isc_event_t **)&client->sendevent);
2582*00b67f09SDavid van Moolenbroek
2583*00b67f09SDavid van Moolenbroek client->magic = 0;
2584*00b67f09SDavid van Moolenbroek
2585*00b67f09SDavid van Moolenbroek cleanup_message:
2586*00b67f09SDavid van Moolenbroek dns_message_destroy(&client->message);
2587*00b67f09SDavid van Moolenbroek
2588*00b67f09SDavid van Moolenbroek cleanup_timer:
2589*00b67f09SDavid van Moolenbroek isc_timer_detach(&client->timer);
2590*00b67f09SDavid van Moolenbroek
2591*00b67f09SDavid van Moolenbroek cleanup_task:
2592*00b67f09SDavid van Moolenbroek isc_task_detach(&client->task);
2593*00b67f09SDavid van Moolenbroek
2594*00b67f09SDavid van Moolenbroek cleanup_client:
2595*00b67f09SDavid van Moolenbroek isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
2596*00b67f09SDavid van Moolenbroek
2597*00b67f09SDavid van Moolenbroek return (result);
2598*00b67f09SDavid van Moolenbroek }
2599*00b67f09SDavid van Moolenbroek
2600*00b67f09SDavid van Moolenbroek static void
client_read(ns_client_t * client)2601*00b67f09SDavid van Moolenbroek client_read(ns_client_t *client) {
2602*00b67f09SDavid van Moolenbroek isc_result_t result;
2603*00b67f09SDavid van Moolenbroek
2604*00b67f09SDavid van Moolenbroek CTRACE("read");
2605*00b67f09SDavid van Moolenbroek
2606*00b67f09SDavid van Moolenbroek result = dns_tcpmsg_readmessage(&client->tcpmsg, client->task,
2607*00b67f09SDavid van Moolenbroek client_request, client);
2608*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2609*00b67f09SDavid van Moolenbroek goto fail;
2610*00b67f09SDavid van Moolenbroek
2611*00b67f09SDavid van Moolenbroek /*
2612*00b67f09SDavid van Moolenbroek * Set a timeout to limit the amount of time we will wait
2613*00b67f09SDavid van Moolenbroek * for a request on this TCP connection.
2614*00b67f09SDavid van Moolenbroek */
2615*00b67f09SDavid van Moolenbroek ns_client_settimeout(client, 30);
2616*00b67f09SDavid van Moolenbroek
2617*00b67f09SDavid van Moolenbroek client->state = client->newstate = NS_CLIENTSTATE_READING;
2618*00b67f09SDavid van Moolenbroek INSIST(client->nreads == 0);
2619*00b67f09SDavid van Moolenbroek INSIST(client->recursionquota == NULL);
2620*00b67f09SDavid van Moolenbroek client->nreads++;
2621*00b67f09SDavid van Moolenbroek
2622*00b67f09SDavid van Moolenbroek return;
2623*00b67f09SDavid van Moolenbroek fail:
2624*00b67f09SDavid van Moolenbroek ns_client_next(client, result);
2625*00b67f09SDavid van Moolenbroek }
2626*00b67f09SDavid van Moolenbroek
2627*00b67f09SDavid van Moolenbroek static void
client_newconn(isc_task_t * task,isc_event_t * event)2628*00b67f09SDavid van Moolenbroek client_newconn(isc_task_t *task, isc_event_t *event) {
2629*00b67f09SDavid van Moolenbroek ns_client_t *client = event->ev_arg;
2630*00b67f09SDavid van Moolenbroek isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
2631*00b67f09SDavid van Moolenbroek isc_result_t result;
2632*00b67f09SDavid van Moolenbroek
2633*00b67f09SDavid van Moolenbroek REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN);
2634*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
2635*00b67f09SDavid van Moolenbroek REQUIRE(client->task == task);
2636*00b67f09SDavid van Moolenbroek
2637*00b67f09SDavid van Moolenbroek UNUSED(task);
2638*00b67f09SDavid van Moolenbroek
2639*00b67f09SDavid van Moolenbroek INSIST(client->state == NS_CLIENTSTATE_READY);
2640*00b67f09SDavid van Moolenbroek
2641*00b67f09SDavid van Moolenbroek INSIST(client->naccepts == 1);
2642*00b67f09SDavid van Moolenbroek client->naccepts--;
2643*00b67f09SDavid van Moolenbroek
2644*00b67f09SDavid van Moolenbroek LOCK(&client->interface->lock);
2645*00b67f09SDavid van Moolenbroek INSIST(client->interface->ntcpcurrent > 0);
2646*00b67f09SDavid van Moolenbroek client->interface->ntcpcurrent--;
2647*00b67f09SDavid van Moolenbroek UNLOCK(&client->interface->lock);
2648*00b67f09SDavid van Moolenbroek
2649*00b67f09SDavid van Moolenbroek /*
2650*00b67f09SDavid van Moolenbroek * We must take ownership of the new socket before the exit
2651*00b67f09SDavid van Moolenbroek * check to make sure it gets destroyed if we decide to exit.
2652*00b67f09SDavid van Moolenbroek */
2653*00b67f09SDavid van Moolenbroek if (nevent->result == ISC_R_SUCCESS) {
2654*00b67f09SDavid van Moolenbroek client->tcpsocket = nevent->newsocket;
2655*00b67f09SDavid van Moolenbroek isc_socket_setname(client->tcpsocket, "client-tcp", NULL);
2656*00b67f09SDavid van Moolenbroek client->state = NS_CLIENTSTATE_READING;
2657*00b67f09SDavid van Moolenbroek INSIST(client->recursionquota == NULL);
2658*00b67f09SDavid van Moolenbroek
2659*00b67f09SDavid van Moolenbroek (void)isc_socket_getpeername(client->tcpsocket,
2660*00b67f09SDavid van Moolenbroek &client->peeraddr);
2661*00b67f09SDavid van Moolenbroek client->peeraddr_valid = ISC_TRUE;
2662*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2663*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2664*00b67f09SDavid van Moolenbroek "new TCP connection");
2665*00b67f09SDavid van Moolenbroek } else {
2666*00b67f09SDavid van Moolenbroek /*
2667*00b67f09SDavid van Moolenbroek * XXXRTH What should we do? We're trying to accept but
2668*00b67f09SDavid van Moolenbroek * it didn't work. If we just give up, then TCP
2669*00b67f09SDavid van Moolenbroek * service may eventually stop.
2670*00b67f09SDavid van Moolenbroek *
2671*00b67f09SDavid van Moolenbroek * For now, we just go idle.
2672*00b67f09SDavid van Moolenbroek *
2673*00b67f09SDavid van Moolenbroek * Going idle is probably the right thing if the
2674*00b67f09SDavid van Moolenbroek * I/O was canceled.
2675*00b67f09SDavid van Moolenbroek */
2676*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2677*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2678*00b67f09SDavid van Moolenbroek "accept failed: %s",
2679*00b67f09SDavid van Moolenbroek isc_result_totext(nevent->result));
2680*00b67f09SDavid van Moolenbroek }
2681*00b67f09SDavid van Moolenbroek
2682*00b67f09SDavid van Moolenbroek if (exit_check(client))
2683*00b67f09SDavid van Moolenbroek goto freeevent;
2684*00b67f09SDavid van Moolenbroek
2685*00b67f09SDavid van Moolenbroek if (nevent->result == ISC_R_SUCCESS) {
2686*00b67f09SDavid van Moolenbroek int match;
2687*00b67f09SDavid van Moolenbroek isc_netaddr_t netaddr;
2688*00b67f09SDavid van Moolenbroek
2689*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
2690*00b67f09SDavid van Moolenbroek
2691*00b67f09SDavid van Moolenbroek if (ns_g_server->blackholeacl != NULL &&
2692*00b67f09SDavid van Moolenbroek dns_acl_match(&netaddr, NULL,
2693*00b67f09SDavid van Moolenbroek ns_g_server->blackholeacl,
2694*00b67f09SDavid van Moolenbroek &ns_g_server->aclenv,
2695*00b67f09SDavid van Moolenbroek &match, NULL) == ISC_R_SUCCESS &&
2696*00b67f09SDavid van Moolenbroek match > 0)
2697*00b67f09SDavid van Moolenbroek {
2698*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2699*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
2700*00b67f09SDavid van Moolenbroek "blackholed connection attempt");
2701*00b67f09SDavid van Moolenbroek client->newstate = NS_CLIENTSTATE_READY;
2702*00b67f09SDavid van Moolenbroek (void)exit_check(client);
2703*00b67f09SDavid van Moolenbroek goto freeevent;
2704*00b67f09SDavid van Moolenbroek }
2705*00b67f09SDavid van Moolenbroek
2706*00b67f09SDavid van Moolenbroek INSIST(client->tcpmsg_valid == ISC_FALSE);
2707*00b67f09SDavid van Moolenbroek dns_tcpmsg_init(client->mctx, client->tcpsocket,
2708*00b67f09SDavid van Moolenbroek &client->tcpmsg);
2709*00b67f09SDavid van Moolenbroek client->tcpmsg_valid = ISC_TRUE;
2710*00b67f09SDavid van Moolenbroek
2711*00b67f09SDavid van Moolenbroek /*
2712*00b67f09SDavid van Moolenbroek * Let a new client take our place immediately, before
2713*00b67f09SDavid van Moolenbroek * we wait for a request packet. If we don't,
2714*00b67f09SDavid van Moolenbroek * telnetting to port 53 (once per CPU) will
2715*00b67f09SDavid van Moolenbroek * deny service to legitimate TCP clients.
2716*00b67f09SDavid van Moolenbroek */
2717*00b67f09SDavid van Moolenbroek result = isc_quota_attach(&ns_g_server->tcpquota,
2718*00b67f09SDavid van Moolenbroek &client->tcpquota);
2719*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
2720*00b67f09SDavid van Moolenbroek result = ns_client_replace(client);
2721*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
2722*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2723*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
2724*00b67f09SDavid van Moolenbroek "no more TCP clients: %s",
2725*00b67f09SDavid van Moolenbroek isc_result_totext(result));
2726*00b67f09SDavid van Moolenbroek }
2727*00b67f09SDavid van Moolenbroek
2728*00b67f09SDavid van Moolenbroek client_read(client);
2729*00b67f09SDavid van Moolenbroek }
2730*00b67f09SDavid van Moolenbroek
2731*00b67f09SDavid van Moolenbroek freeevent:
2732*00b67f09SDavid van Moolenbroek isc_event_free(&event);
2733*00b67f09SDavid van Moolenbroek }
2734*00b67f09SDavid van Moolenbroek
2735*00b67f09SDavid van Moolenbroek static void
client_accept(ns_client_t * client)2736*00b67f09SDavid van Moolenbroek client_accept(ns_client_t *client) {
2737*00b67f09SDavid van Moolenbroek isc_result_t result;
2738*00b67f09SDavid van Moolenbroek
2739*00b67f09SDavid van Moolenbroek CTRACE("accept");
2740*00b67f09SDavid van Moolenbroek
2741*00b67f09SDavid van Moolenbroek result = isc_socket_accept(client->tcplistener, client->task,
2742*00b67f09SDavid van Moolenbroek client_newconn, client);
2743*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
2744*00b67f09SDavid van Moolenbroek UNEXPECTED_ERROR(__FILE__, __LINE__,
2745*00b67f09SDavid van Moolenbroek "isc_socket_accept() failed: %s",
2746*00b67f09SDavid van Moolenbroek isc_result_totext(result));
2747*00b67f09SDavid van Moolenbroek /*
2748*00b67f09SDavid van Moolenbroek * XXXRTH What should we do? We're trying to accept but
2749*00b67f09SDavid van Moolenbroek * it didn't work. If we just give up, then TCP
2750*00b67f09SDavid van Moolenbroek * service may eventually stop.
2751*00b67f09SDavid van Moolenbroek *
2752*00b67f09SDavid van Moolenbroek * For now, we just go idle.
2753*00b67f09SDavid van Moolenbroek */
2754*00b67f09SDavid van Moolenbroek return;
2755*00b67f09SDavid van Moolenbroek }
2756*00b67f09SDavid van Moolenbroek INSIST(client->naccepts == 0);
2757*00b67f09SDavid van Moolenbroek client->naccepts++;
2758*00b67f09SDavid van Moolenbroek LOCK(&client->interface->lock);
2759*00b67f09SDavid van Moolenbroek client->interface->ntcpcurrent++;
2760*00b67f09SDavid van Moolenbroek UNLOCK(&client->interface->lock);
2761*00b67f09SDavid van Moolenbroek }
2762*00b67f09SDavid van Moolenbroek
2763*00b67f09SDavid van Moolenbroek static void
client_udprecv(ns_client_t * client)2764*00b67f09SDavid van Moolenbroek client_udprecv(ns_client_t *client) {
2765*00b67f09SDavid van Moolenbroek isc_result_t result;
2766*00b67f09SDavid van Moolenbroek isc_region_t r;
2767*00b67f09SDavid van Moolenbroek
2768*00b67f09SDavid van Moolenbroek CTRACE("udprecv");
2769*00b67f09SDavid van Moolenbroek
2770*00b67f09SDavid van Moolenbroek r.base = client->recvbuf;
2771*00b67f09SDavid van Moolenbroek r.length = RECV_BUFFER_SIZE;
2772*00b67f09SDavid van Moolenbroek result = isc_socket_recv2(client->udpsocket, &r, 1,
2773*00b67f09SDavid van Moolenbroek client->task, client->recvevent, 0);
2774*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS) {
2775*00b67f09SDavid van Moolenbroek UNEXPECTED_ERROR(__FILE__, __LINE__,
2776*00b67f09SDavid van Moolenbroek "isc_socket_recv2() failed: %s",
2777*00b67f09SDavid van Moolenbroek isc_result_totext(result));
2778*00b67f09SDavid van Moolenbroek /*
2779*00b67f09SDavid van Moolenbroek * This cannot happen in the current implementation, since
2780*00b67f09SDavid van Moolenbroek * isc_socket_recv2() cannot fail if flags == 0.
2781*00b67f09SDavid van Moolenbroek *
2782*00b67f09SDavid van Moolenbroek * If this does fail, we just go idle.
2783*00b67f09SDavid van Moolenbroek */
2784*00b67f09SDavid van Moolenbroek return;
2785*00b67f09SDavid van Moolenbroek }
2786*00b67f09SDavid van Moolenbroek INSIST(client->nrecvs == 0);
2787*00b67f09SDavid van Moolenbroek client->nrecvs++;
2788*00b67f09SDavid van Moolenbroek }
2789*00b67f09SDavid van Moolenbroek
2790*00b67f09SDavid van Moolenbroek void
ns_client_attach(ns_client_t * source,ns_client_t ** targetp)2791*00b67f09SDavid van Moolenbroek ns_client_attach(ns_client_t *source, ns_client_t **targetp) {
2792*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(source));
2793*00b67f09SDavid van Moolenbroek REQUIRE(targetp != NULL && *targetp == NULL);
2794*00b67f09SDavid van Moolenbroek
2795*00b67f09SDavid van Moolenbroek source->references++;
2796*00b67f09SDavid van Moolenbroek ns_client_log(source, NS_LOGCATEGORY_CLIENT,
2797*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
2798*00b67f09SDavid van Moolenbroek "ns_client_attach: ref = %d", source->references);
2799*00b67f09SDavid van Moolenbroek *targetp = source;
2800*00b67f09SDavid van Moolenbroek }
2801*00b67f09SDavid van Moolenbroek
2802*00b67f09SDavid van Moolenbroek void
ns_client_detach(ns_client_t ** clientp)2803*00b67f09SDavid van Moolenbroek ns_client_detach(ns_client_t **clientp) {
2804*00b67f09SDavid van Moolenbroek ns_client_t *client = *clientp;
2805*00b67f09SDavid van Moolenbroek
2806*00b67f09SDavid van Moolenbroek client->references--;
2807*00b67f09SDavid van Moolenbroek INSIST(client->references >= 0);
2808*00b67f09SDavid van Moolenbroek *clientp = NULL;
2809*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2810*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
2811*00b67f09SDavid van Moolenbroek "ns_client_detach: ref = %d", client->references);
2812*00b67f09SDavid van Moolenbroek (void)exit_check(client);
2813*00b67f09SDavid van Moolenbroek }
2814*00b67f09SDavid van Moolenbroek
2815*00b67f09SDavid van Moolenbroek isc_boolean_t
ns_client_shuttingdown(ns_client_t * client)2816*00b67f09SDavid van Moolenbroek ns_client_shuttingdown(ns_client_t *client) {
2817*00b67f09SDavid van Moolenbroek return (ISC_TF(client->newstate == NS_CLIENTSTATE_FREED));
2818*00b67f09SDavid van Moolenbroek }
2819*00b67f09SDavid van Moolenbroek
2820*00b67f09SDavid van Moolenbroek isc_result_t
ns_client_replace(ns_client_t * client)2821*00b67f09SDavid van Moolenbroek ns_client_replace(ns_client_t *client) {
2822*00b67f09SDavid van Moolenbroek isc_result_t result;
2823*00b67f09SDavid van Moolenbroek
2824*00b67f09SDavid van Moolenbroek CTRACE("replace");
2825*00b67f09SDavid van Moolenbroek
2826*00b67f09SDavid van Moolenbroek REQUIRE(client != NULL);
2827*00b67f09SDavid van Moolenbroek REQUIRE(client->manager != NULL);
2828*00b67f09SDavid van Moolenbroek
2829*00b67f09SDavid van Moolenbroek result = get_client(client->manager, client->interface,
2830*00b67f09SDavid van Moolenbroek client->dispatch, TCP_CLIENT(client));
2831*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2832*00b67f09SDavid van Moolenbroek return (result);
2833*00b67f09SDavid van Moolenbroek
2834*00b67f09SDavid van Moolenbroek /*
2835*00b67f09SDavid van Moolenbroek * The responsibility for listening for new requests is hereby
2836*00b67f09SDavid van Moolenbroek * transferred to the new client. Therefore, the old client
2837*00b67f09SDavid van Moolenbroek * should refrain from listening for any more requests.
2838*00b67f09SDavid van Moolenbroek */
2839*00b67f09SDavid van Moolenbroek client->mortal = ISC_TRUE;
2840*00b67f09SDavid van Moolenbroek
2841*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
2842*00b67f09SDavid van Moolenbroek }
2843*00b67f09SDavid van Moolenbroek
2844*00b67f09SDavid van Moolenbroek /***
2845*00b67f09SDavid van Moolenbroek *** Client Manager
2846*00b67f09SDavid van Moolenbroek ***/
2847*00b67f09SDavid van Moolenbroek
2848*00b67f09SDavid van Moolenbroek static void
clientmgr_destroy(ns_clientmgr_t * manager)2849*00b67f09SDavid van Moolenbroek clientmgr_destroy(ns_clientmgr_t *manager) {
2850*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2851*00b67f09SDavid van Moolenbroek int i;
2852*00b67f09SDavid van Moolenbroek #endif
2853*00b67f09SDavid van Moolenbroek
2854*00b67f09SDavid van Moolenbroek REQUIRE(ISC_LIST_EMPTY(manager->clients));
2855*00b67f09SDavid van Moolenbroek
2856*00b67f09SDavid van Moolenbroek MTRACE("clientmgr_destroy");
2857*00b67f09SDavid van Moolenbroek
2858*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2859*00b67f09SDavid van Moolenbroek for (i = 0; i < NMCTXS; i++) {
2860*00b67f09SDavid van Moolenbroek if (manager->mctxpool[i] != NULL)
2861*00b67f09SDavid van Moolenbroek isc_mem_detach(&manager->mctxpool[i]);
2862*00b67f09SDavid van Moolenbroek }
2863*00b67f09SDavid van Moolenbroek #endif
2864*00b67f09SDavid van Moolenbroek
2865*00b67f09SDavid van Moolenbroek ISC_QUEUE_DESTROY(manager->inactive);
2866*00b67f09SDavid van Moolenbroek DESTROYLOCK(&manager->lock);
2867*00b67f09SDavid van Moolenbroek DESTROYLOCK(&manager->listlock);
2868*00b67f09SDavid van Moolenbroek DESTROYLOCK(&manager->reclock);
2869*00b67f09SDavid van Moolenbroek manager->magic = 0;
2870*00b67f09SDavid van Moolenbroek isc_mem_put(manager->mctx, manager, sizeof(*manager));
2871*00b67f09SDavid van Moolenbroek }
2872*00b67f09SDavid van Moolenbroek
2873*00b67f09SDavid van Moolenbroek isc_result_t
ns_clientmgr_create(isc_mem_t * mctx,isc_taskmgr_t * taskmgr,isc_timermgr_t * timermgr,ns_clientmgr_t ** managerp)2874*00b67f09SDavid van Moolenbroek ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
2875*00b67f09SDavid van Moolenbroek isc_timermgr_t *timermgr, ns_clientmgr_t **managerp)
2876*00b67f09SDavid van Moolenbroek {
2877*00b67f09SDavid van Moolenbroek ns_clientmgr_t *manager;
2878*00b67f09SDavid van Moolenbroek isc_result_t result;
2879*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2880*00b67f09SDavid van Moolenbroek int i;
2881*00b67f09SDavid van Moolenbroek #endif
2882*00b67f09SDavid van Moolenbroek
2883*00b67f09SDavid van Moolenbroek manager = isc_mem_get(mctx, sizeof(*manager));
2884*00b67f09SDavid van Moolenbroek if (manager == NULL)
2885*00b67f09SDavid van Moolenbroek return (ISC_R_NOMEMORY);
2886*00b67f09SDavid van Moolenbroek
2887*00b67f09SDavid van Moolenbroek result = isc_mutex_init(&manager->lock);
2888*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2889*00b67f09SDavid van Moolenbroek goto cleanup_manager;
2890*00b67f09SDavid van Moolenbroek
2891*00b67f09SDavid van Moolenbroek result = isc_mutex_init(&manager->listlock);
2892*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2893*00b67f09SDavid van Moolenbroek goto cleanup_lock;
2894*00b67f09SDavid van Moolenbroek
2895*00b67f09SDavid van Moolenbroek result = isc_mutex_init(&manager->reclock);
2896*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
2897*00b67f09SDavid van Moolenbroek goto cleanup_listlock;
2898*00b67f09SDavid van Moolenbroek
2899*00b67f09SDavid van Moolenbroek manager->mctx = mctx;
2900*00b67f09SDavid van Moolenbroek manager->taskmgr = taskmgr;
2901*00b67f09SDavid van Moolenbroek manager->timermgr = timermgr;
2902*00b67f09SDavid van Moolenbroek manager->exiting = ISC_FALSE;
2903*00b67f09SDavid van Moolenbroek ISC_LIST_INIT(manager->clients);
2904*00b67f09SDavid van Moolenbroek ISC_LIST_INIT(manager->recursing);
2905*00b67f09SDavid van Moolenbroek ISC_QUEUE_INIT(manager->inactive, ilink);
2906*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2907*00b67f09SDavid van Moolenbroek manager->nextmctx = 0;
2908*00b67f09SDavid van Moolenbroek for (i = 0; i < NMCTXS; i++)
2909*00b67f09SDavid van Moolenbroek manager->mctxpool[i] = NULL; /* will be created on-demand */
2910*00b67f09SDavid van Moolenbroek #endif
2911*00b67f09SDavid van Moolenbroek manager->magic = MANAGER_MAGIC;
2912*00b67f09SDavid van Moolenbroek
2913*00b67f09SDavid van Moolenbroek MTRACE("create");
2914*00b67f09SDavid van Moolenbroek
2915*00b67f09SDavid van Moolenbroek *managerp = manager;
2916*00b67f09SDavid van Moolenbroek
2917*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
2918*00b67f09SDavid van Moolenbroek
2919*00b67f09SDavid van Moolenbroek cleanup_listlock:
2920*00b67f09SDavid van Moolenbroek (void) isc_mutex_destroy(&manager->listlock);
2921*00b67f09SDavid van Moolenbroek
2922*00b67f09SDavid van Moolenbroek cleanup_lock:
2923*00b67f09SDavid van Moolenbroek (void) isc_mutex_destroy(&manager->lock);
2924*00b67f09SDavid van Moolenbroek
2925*00b67f09SDavid van Moolenbroek cleanup_manager:
2926*00b67f09SDavid van Moolenbroek isc_mem_put(manager->mctx, manager, sizeof(*manager));
2927*00b67f09SDavid van Moolenbroek
2928*00b67f09SDavid van Moolenbroek return (result);
2929*00b67f09SDavid van Moolenbroek }
2930*00b67f09SDavid van Moolenbroek
2931*00b67f09SDavid van Moolenbroek void
ns_clientmgr_destroy(ns_clientmgr_t ** managerp)2932*00b67f09SDavid van Moolenbroek ns_clientmgr_destroy(ns_clientmgr_t **managerp) {
2933*00b67f09SDavid van Moolenbroek isc_result_t result;
2934*00b67f09SDavid van Moolenbroek ns_clientmgr_t *manager;
2935*00b67f09SDavid van Moolenbroek ns_client_t *client;
2936*00b67f09SDavid van Moolenbroek isc_boolean_t need_destroy = ISC_FALSE, unlock = ISC_FALSE;
2937*00b67f09SDavid van Moolenbroek
2938*00b67f09SDavid van Moolenbroek REQUIRE(managerp != NULL);
2939*00b67f09SDavid van Moolenbroek manager = *managerp;
2940*00b67f09SDavid van Moolenbroek REQUIRE(VALID_MANAGER(manager));
2941*00b67f09SDavid van Moolenbroek
2942*00b67f09SDavid van Moolenbroek MTRACE("destroy");
2943*00b67f09SDavid van Moolenbroek
2944*00b67f09SDavid van Moolenbroek /*
2945*00b67f09SDavid van Moolenbroek * Check for success because we may already be task-exclusive
2946*00b67f09SDavid van Moolenbroek * at this point. Only if we succeed at obtaining an exclusive
2947*00b67f09SDavid van Moolenbroek * lock now will we need to relinquish it later.
2948*00b67f09SDavid van Moolenbroek */
2949*00b67f09SDavid van Moolenbroek result = isc_task_beginexclusive(ns_g_server->task);
2950*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
2951*00b67f09SDavid van Moolenbroek unlock = ISC_TRUE;
2952*00b67f09SDavid van Moolenbroek
2953*00b67f09SDavid van Moolenbroek manager->exiting = ISC_TRUE;
2954*00b67f09SDavid van Moolenbroek
2955*00b67f09SDavid van Moolenbroek for (client = ISC_LIST_HEAD(manager->clients);
2956*00b67f09SDavid van Moolenbroek client != NULL;
2957*00b67f09SDavid van Moolenbroek client = ISC_LIST_NEXT(client, link))
2958*00b67f09SDavid van Moolenbroek isc_task_shutdown(client->task);
2959*00b67f09SDavid van Moolenbroek
2960*00b67f09SDavid van Moolenbroek if (ISC_LIST_EMPTY(manager->clients))
2961*00b67f09SDavid van Moolenbroek need_destroy = ISC_TRUE;
2962*00b67f09SDavid van Moolenbroek
2963*00b67f09SDavid van Moolenbroek if (unlock)
2964*00b67f09SDavid van Moolenbroek isc_task_endexclusive(ns_g_server->task);
2965*00b67f09SDavid van Moolenbroek
2966*00b67f09SDavid van Moolenbroek if (need_destroy)
2967*00b67f09SDavid van Moolenbroek clientmgr_destroy(manager);
2968*00b67f09SDavid van Moolenbroek
2969*00b67f09SDavid van Moolenbroek *managerp = NULL;
2970*00b67f09SDavid van Moolenbroek }
2971*00b67f09SDavid van Moolenbroek
2972*00b67f09SDavid van Moolenbroek static isc_result_t
get_client(ns_clientmgr_t * manager,ns_interface_t * ifp,dns_dispatch_t * disp,isc_boolean_t tcp)2973*00b67f09SDavid van Moolenbroek get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
2974*00b67f09SDavid van Moolenbroek dns_dispatch_t *disp, isc_boolean_t tcp)
2975*00b67f09SDavid van Moolenbroek {
2976*00b67f09SDavid van Moolenbroek isc_result_t result = ISC_R_SUCCESS;
2977*00b67f09SDavid van Moolenbroek isc_event_t *ev;
2978*00b67f09SDavid van Moolenbroek ns_client_t *client;
2979*00b67f09SDavid van Moolenbroek MTRACE("get client");
2980*00b67f09SDavid van Moolenbroek
2981*00b67f09SDavid van Moolenbroek REQUIRE(manager != NULL);
2982*00b67f09SDavid van Moolenbroek
2983*00b67f09SDavid van Moolenbroek if (manager->exiting)
2984*00b67f09SDavid van Moolenbroek return (ISC_R_SHUTTINGDOWN);
2985*00b67f09SDavid van Moolenbroek
2986*00b67f09SDavid van Moolenbroek /*
2987*00b67f09SDavid van Moolenbroek * Allocate a client. First try to get a recycled one;
2988*00b67f09SDavid van Moolenbroek * if that fails, make a new one.
2989*00b67f09SDavid van Moolenbroek */
2990*00b67f09SDavid van Moolenbroek client = NULL;
2991*00b67f09SDavid van Moolenbroek if (!ns_g_clienttest)
2992*00b67f09SDavid van Moolenbroek ISC_QUEUE_POP(manager->inactive, ilink, client);
2993*00b67f09SDavid van Moolenbroek
2994*00b67f09SDavid van Moolenbroek if (client != NULL)
2995*00b67f09SDavid van Moolenbroek MTRACE("recycle");
2996*00b67f09SDavid van Moolenbroek else {
2997*00b67f09SDavid van Moolenbroek MTRACE("create new");
2998*00b67f09SDavid van Moolenbroek
2999*00b67f09SDavid van Moolenbroek LOCK(&manager->lock);
3000*00b67f09SDavid van Moolenbroek result = client_create(manager, &client);
3001*00b67f09SDavid van Moolenbroek UNLOCK(&manager->lock);
3002*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
3003*00b67f09SDavid van Moolenbroek return (result);
3004*00b67f09SDavid van Moolenbroek
3005*00b67f09SDavid van Moolenbroek LOCK(&manager->listlock);
3006*00b67f09SDavid van Moolenbroek ISC_LIST_APPEND(manager->clients, client, link);
3007*00b67f09SDavid van Moolenbroek UNLOCK(&manager->listlock);
3008*00b67f09SDavid van Moolenbroek }
3009*00b67f09SDavid van Moolenbroek
3010*00b67f09SDavid van Moolenbroek client->manager = manager;
3011*00b67f09SDavid van Moolenbroek ns_interface_attach(ifp, &client->interface);
3012*00b67f09SDavid van Moolenbroek client->state = NS_CLIENTSTATE_READY;
3013*00b67f09SDavid van Moolenbroek INSIST(client->recursionquota == NULL);
3014*00b67f09SDavid van Moolenbroek
3015*00b67f09SDavid van Moolenbroek client->dscp = ifp->dscp;
3016*00b67f09SDavid van Moolenbroek
3017*00b67f09SDavid van Moolenbroek if (tcp) {
3018*00b67f09SDavid van Moolenbroek client->attributes |= NS_CLIENTATTR_TCP;
3019*00b67f09SDavid van Moolenbroek isc_socket_attach(ifp->tcpsocket,
3020*00b67f09SDavid van Moolenbroek &client->tcplistener);
3021*00b67f09SDavid van Moolenbroek } else {
3022*00b67f09SDavid van Moolenbroek isc_socket_t *sock;
3023*00b67f09SDavid van Moolenbroek
3024*00b67f09SDavid van Moolenbroek dns_dispatch_attach(disp, &client->dispatch);
3025*00b67f09SDavid van Moolenbroek sock = dns_dispatch_getsocket(client->dispatch);
3026*00b67f09SDavid van Moolenbroek isc_socket_attach(sock, &client->udpsocket);
3027*00b67f09SDavid van Moolenbroek }
3028*00b67f09SDavid van Moolenbroek
3029*00b67f09SDavid van Moolenbroek INSIST(client->nctls == 0);
3030*00b67f09SDavid van Moolenbroek client->nctls++;
3031*00b67f09SDavid van Moolenbroek ev = &client->ctlevent;
3032*00b67f09SDavid van Moolenbroek isc_task_send(client->task, &ev);
3033*00b67f09SDavid van Moolenbroek
3034*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
3035*00b67f09SDavid van Moolenbroek }
3036*00b67f09SDavid van Moolenbroek
3037*00b67f09SDavid van Moolenbroek isc_result_t
ns_clientmgr_createclients(ns_clientmgr_t * manager,unsigned int n,ns_interface_t * ifp,isc_boolean_t tcp)3038*00b67f09SDavid van Moolenbroek ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
3039*00b67f09SDavid van Moolenbroek ns_interface_t *ifp, isc_boolean_t tcp)
3040*00b67f09SDavid van Moolenbroek {
3041*00b67f09SDavid van Moolenbroek isc_result_t result = ISC_R_SUCCESS;
3042*00b67f09SDavid van Moolenbroek unsigned int disp;
3043*00b67f09SDavid van Moolenbroek
3044*00b67f09SDavid van Moolenbroek REQUIRE(VALID_MANAGER(manager));
3045*00b67f09SDavid van Moolenbroek REQUIRE(n > 0);
3046*00b67f09SDavid van Moolenbroek
3047*00b67f09SDavid van Moolenbroek MTRACE("createclients");
3048*00b67f09SDavid van Moolenbroek
3049*00b67f09SDavid van Moolenbroek for (disp = 0; disp < n; disp++) {
3050*00b67f09SDavid van Moolenbroek result = get_client(manager, ifp, ifp->udpdispatch[disp], tcp);
3051*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
3052*00b67f09SDavid van Moolenbroek break;
3053*00b67f09SDavid van Moolenbroek }
3054*00b67f09SDavid van Moolenbroek
3055*00b67f09SDavid van Moolenbroek return (result);
3056*00b67f09SDavid van Moolenbroek }
3057*00b67f09SDavid van Moolenbroek
3058*00b67f09SDavid van Moolenbroek isc_sockaddr_t *
ns_client_getsockaddr(ns_client_t * client)3059*00b67f09SDavid van Moolenbroek ns_client_getsockaddr(ns_client_t *client) {
3060*00b67f09SDavid van Moolenbroek return (&client->peeraddr);
3061*00b67f09SDavid van Moolenbroek }
3062*00b67f09SDavid van Moolenbroek
3063*00b67f09SDavid van Moolenbroek isc_result_t
ns_client_checkaclsilent(ns_client_t * client,isc_netaddr_t * netaddr,dns_acl_t * acl,isc_boolean_t default_allow)3064*00b67f09SDavid van Moolenbroek ns_client_checkaclsilent(ns_client_t *client, isc_netaddr_t *netaddr,
3065*00b67f09SDavid van Moolenbroek dns_acl_t *acl, isc_boolean_t default_allow)
3066*00b67f09SDavid van Moolenbroek {
3067*00b67f09SDavid van Moolenbroek isc_result_t result;
3068*00b67f09SDavid van Moolenbroek isc_netaddr_t tmpnetaddr;
3069*00b67f09SDavid van Moolenbroek int match;
3070*00b67f09SDavid van Moolenbroek
3071*00b67f09SDavid van Moolenbroek if (acl == NULL) {
3072*00b67f09SDavid van Moolenbroek if (default_allow)
3073*00b67f09SDavid van Moolenbroek goto allow;
3074*00b67f09SDavid van Moolenbroek else
3075*00b67f09SDavid van Moolenbroek goto deny;
3076*00b67f09SDavid van Moolenbroek }
3077*00b67f09SDavid van Moolenbroek
3078*00b67f09SDavid van Moolenbroek if (netaddr == NULL) {
3079*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&tmpnetaddr, &client->peeraddr);
3080*00b67f09SDavid van Moolenbroek netaddr = &tmpnetaddr;
3081*00b67f09SDavid van Moolenbroek }
3082*00b67f09SDavid van Moolenbroek
3083*00b67f09SDavid van Moolenbroek result = dns_acl_match(netaddr, client->signer, acl,
3084*00b67f09SDavid van Moolenbroek &ns_g_server->aclenv, &match, NULL);
3085*00b67f09SDavid van Moolenbroek
3086*00b67f09SDavid van Moolenbroek if (result != ISC_R_SUCCESS)
3087*00b67f09SDavid van Moolenbroek goto deny; /* Internal error, already logged. */
3088*00b67f09SDavid van Moolenbroek if (match > 0)
3089*00b67f09SDavid van Moolenbroek goto allow;
3090*00b67f09SDavid van Moolenbroek goto deny; /* Negative match or no match. */
3091*00b67f09SDavid van Moolenbroek
3092*00b67f09SDavid van Moolenbroek allow:
3093*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
3094*00b67f09SDavid van Moolenbroek
3095*00b67f09SDavid van Moolenbroek deny:
3096*00b67f09SDavid van Moolenbroek return (DNS_R_REFUSED);
3097*00b67f09SDavid van Moolenbroek }
3098*00b67f09SDavid van Moolenbroek
3099*00b67f09SDavid van Moolenbroek isc_result_t
ns_client_checkacl(ns_client_t * client,isc_sockaddr_t * sockaddr,const char * opname,dns_acl_t * acl,isc_boolean_t default_allow,int log_level)3100*00b67f09SDavid van Moolenbroek ns_client_checkacl(ns_client_t *client, isc_sockaddr_t *sockaddr,
3101*00b67f09SDavid van Moolenbroek const char *opname, dns_acl_t *acl,
3102*00b67f09SDavid van Moolenbroek isc_boolean_t default_allow, int log_level)
3103*00b67f09SDavid van Moolenbroek {
3104*00b67f09SDavid van Moolenbroek isc_result_t result;
3105*00b67f09SDavid van Moolenbroek isc_netaddr_t netaddr;
3106*00b67f09SDavid van Moolenbroek
3107*00b67f09SDavid van Moolenbroek if (sockaddr != NULL)
3108*00b67f09SDavid van Moolenbroek isc_netaddr_fromsockaddr(&netaddr, sockaddr);
3109*00b67f09SDavid van Moolenbroek
3110*00b67f09SDavid van Moolenbroek result = ns_client_checkaclsilent(client, sockaddr ? &netaddr : NULL,
3111*00b67f09SDavid van Moolenbroek acl, default_allow);
3112*00b67f09SDavid van Moolenbroek
3113*00b67f09SDavid van Moolenbroek pfilter_notify(result, client, opname);
3114*00b67f09SDavid van Moolenbroek if (result == ISC_R_SUCCESS)
3115*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
3116*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
3117*00b67f09SDavid van Moolenbroek "%s approved", opname);
3118*00b67f09SDavid van Moolenbroek else
3119*00b67f09SDavid van Moolenbroek ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
3120*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT,
3121*00b67f09SDavid van Moolenbroek log_level, "%s denied", opname);
3122*00b67f09SDavid van Moolenbroek return (result);
3123*00b67f09SDavid van Moolenbroek }
3124*00b67f09SDavid van Moolenbroek
3125*00b67f09SDavid van Moolenbroek static void
ns_client_name(ns_client_t * client,char * peerbuf,size_t len)3126*00b67f09SDavid van Moolenbroek ns_client_name(ns_client_t *client, char *peerbuf, size_t len) {
3127*00b67f09SDavid van Moolenbroek if (client->peeraddr_valid)
3128*00b67f09SDavid van Moolenbroek isc_sockaddr_format(&client->peeraddr, peerbuf,
3129*00b67f09SDavid van Moolenbroek (unsigned int)len);
3130*00b67f09SDavid van Moolenbroek else
3131*00b67f09SDavid van Moolenbroek snprintf(peerbuf, len, "@%p", client);
3132*00b67f09SDavid van Moolenbroek }
3133*00b67f09SDavid van Moolenbroek
3134*00b67f09SDavid van Moolenbroek void
ns_client_logv(ns_client_t * client,isc_logcategory_t * category,isc_logmodule_t * module,int level,const char * fmt,va_list ap)3135*00b67f09SDavid van Moolenbroek ns_client_logv(ns_client_t *client, isc_logcategory_t *category,
3136*00b67f09SDavid van Moolenbroek isc_logmodule_t *module, int level, const char *fmt, va_list ap)
3137*00b67f09SDavid van Moolenbroek {
3138*00b67f09SDavid van Moolenbroek char msgbuf[4096];
3139*00b67f09SDavid van Moolenbroek char peerbuf[ISC_SOCKADDR_FORMATSIZE];
3140*00b67f09SDavid van Moolenbroek char signerbuf[DNS_NAME_FORMATSIZE], qnamebuf[DNS_NAME_FORMATSIZE];
3141*00b67f09SDavid van Moolenbroek const char *viewname = "";
3142*00b67f09SDavid van Moolenbroek const char *sep1 = "", *sep2 = "", *sep3 = "", *sep4 = "";
3143*00b67f09SDavid van Moolenbroek const char *signer = "", *qname = "";
3144*00b67f09SDavid van Moolenbroek dns_name_t *q = NULL;
3145*00b67f09SDavid van Moolenbroek
3146*00b67f09SDavid van Moolenbroek vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
3147*00b67f09SDavid van Moolenbroek
3148*00b67f09SDavid van Moolenbroek ns_client_name(client, peerbuf, sizeof(peerbuf));
3149*00b67f09SDavid van Moolenbroek
3150*00b67f09SDavid van Moolenbroek if (client->signer != NULL) {
3151*00b67f09SDavid van Moolenbroek dns_name_format(client->signer, signerbuf, sizeof(signerbuf));
3152*00b67f09SDavid van Moolenbroek sep1 = "/key ";
3153*00b67f09SDavid van Moolenbroek signer = signerbuf;
3154*00b67f09SDavid van Moolenbroek }
3155*00b67f09SDavid van Moolenbroek
3156*00b67f09SDavid van Moolenbroek q = client->query.origqname != NULL
3157*00b67f09SDavid van Moolenbroek ? client->query.origqname : client->query.qname;
3158*00b67f09SDavid van Moolenbroek if (q != NULL) {
3159*00b67f09SDavid van Moolenbroek dns_name_format(q, qnamebuf, sizeof(qnamebuf));
3160*00b67f09SDavid van Moolenbroek sep2 = " (";
3161*00b67f09SDavid van Moolenbroek sep3 = ")";
3162*00b67f09SDavid van Moolenbroek qname = qnamebuf;
3163*00b67f09SDavid van Moolenbroek }
3164*00b67f09SDavid van Moolenbroek
3165*00b67f09SDavid van Moolenbroek if (client->view != NULL && strcmp(client->view->name, "_bind") != 0 &&
3166*00b67f09SDavid van Moolenbroek strcmp(client->view->name, "_default") != 0) {
3167*00b67f09SDavid van Moolenbroek sep4 = ": view ";
3168*00b67f09SDavid van Moolenbroek viewname = client->view->name;
3169*00b67f09SDavid van Moolenbroek }
3170*00b67f09SDavid van Moolenbroek
3171*00b67f09SDavid van Moolenbroek isc_log_write(ns_g_lctx, category, module, level,
3172*00b67f09SDavid van Moolenbroek "client %s%s%s%s%s%s%s%s: %s",
3173*00b67f09SDavid van Moolenbroek peerbuf, sep1, signer, sep2, qname, sep3,
3174*00b67f09SDavid van Moolenbroek sep4, viewname, msgbuf);
3175*00b67f09SDavid van Moolenbroek }
3176*00b67f09SDavid van Moolenbroek
3177*00b67f09SDavid van Moolenbroek void
ns_client_log(ns_client_t * client,isc_logcategory_t * category,isc_logmodule_t * module,int level,const char * fmt,...)3178*00b67f09SDavid van Moolenbroek ns_client_log(ns_client_t *client, isc_logcategory_t *category,
3179*00b67f09SDavid van Moolenbroek isc_logmodule_t *module, int level, const char *fmt, ...)
3180*00b67f09SDavid van Moolenbroek {
3181*00b67f09SDavid van Moolenbroek va_list ap;
3182*00b67f09SDavid van Moolenbroek
3183*00b67f09SDavid van Moolenbroek if (! isc_log_wouldlog(ns_g_lctx, level))
3184*00b67f09SDavid van Moolenbroek return;
3185*00b67f09SDavid van Moolenbroek
3186*00b67f09SDavid van Moolenbroek va_start(ap, fmt);
3187*00b67f09SDavid van Moolenbroek ns_client_logv(client, category, module, level, fmt, ap);
3188*00b67f09SDavid van Moolenbroek va_end(ap);
3189*00b67f09SDavid van Moolenbroek }
3190*00b67f09SDavid van Moolenbroek
3191*00b67f09SDavid van Moolenbroek void
ns_client_aclmsg(const char * msg,dns_name_t * name,dns_rdatatype_t type,dns_rdataclass_t rdclass,char * buf,size_t len)3192*00b67f09SDavid van Moolenbroek ns_client_aclmsg(const char *msg, dns_name_t *name, dns_rdatatype_t type,
3193*00b67f09SDavid van Moolenbroek dns_rdataclass_t rdclass, char *buf, size_t len)
3194*00b67f09SDavid van Moolenbroek {
3195*00b67f09SDavid van Moolenbroek char namebuf[DNS_NAME_FORMATSIZE];
3196*00b67f09SDavid van Moolenbroek char typebuf[DNS_RDATATYPE_FORMATSIZE];
3197*00b67f09SDavid van Moolenbroek char classbuf[DNS_RDATACLASS_FORMATSIZE];
3198*00b67f09SDavid van Moolenbroek
3199*00b67f09SDavid van Moolenbroek dns_name_format(name, namebuf, sizeof(namebuf));
3200*00b67f09SDavid van Moolenbroek dns_rdatatype_format(type, typebuf, sizeof(typebuf));
3201*00b67f09SDavid van Moolenbroek dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
3202*00b67f09SDavid van Moolenbroek (void)snprintf(buf, len, "%s '%s/%s/%s'", msg, namebuf, typebuf,
3203*00b67f09SDavid van Moolenbroek classbuf);
3204*00b67f09SDavid van Moolenbroek }
3205*00b67f09SDavid van Moolenbroek
3206*00b67f09SDavid van Moolenbroek static void
ns_client_dumpmessage(ns_client_t * client,const char * reason)3207*00b67f09SDavid van Moolenbroek ns_client_dumpmessage(ns_client_t *client, const char *reason) {
3208*00b67f09SDavid van Moolenbroek isc_buffer_t buffer;
3209*00b67f09SDavid van Moolenbroek char *buf = NULL;
3210*00b67f09SDavid van Moolenbroek int len = 1024;
3211*00b67f09SDavid van Moolenbroek isc_result_t result;
3212*00b67f09SDavid van Moolenbroek
3213*00b67f09SDavid van Moolenbroek if (!isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(1)))
3214*00b67f09SDavid van Moolenbroek return;
3215*00b67f09SDavid van Moolenbroek
3216*00b67f09SDavid van Moolenbroek /*
3217*00b67f09SDavid van Moolenbroek * Note that these are multiline debug messages. We want a newline
3218*00b67f09SDavid van Moolenbroek * to appear in the log after each message.
3219*00b67f09SDavid van Moolenbroek */
3220*00b67f09SDavid van Moolenbroek
3221*00b67f09SDavid van Moolenbroek do {
3222*00b67f09SDavid van Moolenbroek buf = isc_mem_get(client->mctx, len);
3223*00b67f09SDavid van Moolenbroek if (buf == NULL)
3224*00b67f09SDavid van Moolenbroek break;
3225*00b67f09SDavid van Moolenbroek isc_buffer_init(&buffer, buf, len);
3226*00b67f09SDavid van Moolenbroek result = dns_message_totext(client->message,
3227*00b67f09SDavid van Moolenbroek &dns_master_style_debug,
3228*00b67f09SDavid van Moolenbroek 0, &buffer);
3229*00b67f09SDavid van Moolenbroek if (result == ISC_R_NOSPACE) {
3230*00b67f09SDavid van Moolenbroek isc_mem_put(client->mctx, buf, len);
3231*00b67f09SDavid van Moolenbroek len += 1024;
3232*00b67f09SDavid van Moolenbroek } else if (result == ISC_R_SUCCESS)
3233*00b67f09SDavid van Moolenbroek ns_client_log(client, NS_LOGCATEGORY_UNMATCHED,
3234*00b67f09SDavid van Moolenbroek NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
3235*00b67f09SDavid van Moolenbroek "%s\n%.*s", reason,
3236*00b67f09SDavid van Moolenbroek (int)isc_buffer_usedlength(&buffer),
3237*00b67f09SDavid van Moolenbroek buf);
3238*00b67f09SDavid van Moolenbroek } while (result == ISC_R_NOSPACE);
3239*00b67f09SDavid van Moolenbroek
3240*00b67f09SDavid van Moolenbroek if (buf != NULL)
3241*00b67f09SDavid van Moolenbroek isc_mem_put(client->mctx, buf, len);
3242*00b67f09SDavid van Moolenbroek }
3243*00b67f09SDavid van Moolenbroek
3244*00b67f09SDavid van Moolenbroek void
ns_client_dumprecursing(FILE * f,ns_clientmgr_t * manager)3245*00b67f09SDavid van Moolenbroek ns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager) {
3246*00b67f09SDavid van Moolenbroek ns_client_t *client;
3247*00b67f09SDavid van Moolenbroek char namebuf[DNS_NAME_FORMATSIZE];
3248*00b67f09SDavid van Moolenbroek char original[DNS_NAME_FORMATSIZE];
3249*00b67f09SDavid van Moolenbroek char peerbuf[ISC_SOCKADDR_FORMATSIZE];
3250*00b67f09SDavid van Moolenbroek char typebuf[DNS_RDATATYPE_FORMATSIZE];
3251*00b67f09SDavid van Moolenbroek char classbuf[DNS_RDATACLASS_FORMATSIZE];
3252*00b67f09SDavid van Moolenbroek const char *name;
3253*00b67f09SDavid van Moolenbroek const char *sep;
3254*00b67f09SDavid van Moolenbroek const char *origfor;
3255*00b67f09SDavid van Moolenbroek dns_rdataset_t *rdataset;
3256*00b67f09SDavid van Moolenbroek
3257*00b67f09SDavid van Moolenbroek REQUIRE(VALID_MANAGER(manager));
3258*00b67f09SDavid van Moolenbroek
3259*00b67f09SDavid van Moolenbroek LOCK(&manager->reclock);
3260*00b67f09SDavid van Moolenbroek client = ISC_LIST_HEAD(manager->recursing);
3261*00b67f09SDavid van Moolenbroek while (client != NULL) {
3262*00b67f09SDavid van Moolenbroek INSIST(client->state == NS_CLIENTSTATE_RECURSING);
3263*00b67f09SDavid van Moolenbroek
3264*00b67f09SDavid van Moolenbroek ns_client_name(client, peerbuf, sizeof(peerbuf));
3265*00b67f09SDavid van Moolenbroek if (client->view != NULL &&
3266*00b67f09SDavid van Moolenbroek strcmp(client->view->name, "_bind") != 0 &&
3267*00b67f09SDavid van Moolenbroek strcmp(client->view->name, "_default") != 0) {
3268*00b67f09SDavid van Moolenbroek name = client->view->name;
3269*00b67f09SDavid van Moolenbroek sep = ": view ";
3270*00b67f09SDavid van Moolenbroek } else {
3271*00b67f09SDavid van Moolenbroek name = "";
3272*00b67f09SDavid van Moolenbroek sep = "";
3273*00b67f09SDavid van Moolenbroek }
3274*00b67f09SDavid van Moolenbroek
3275*00b67f09SDavid van Moolenbroek LOCK(&client->query.fetchlock);
3276*00b67f09SDavid van Moolenbroek INSIST(client->query.qname != NULL);
3277*00b67f09SDavid van Moolenbroek dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
3278*00b67f09SDavid van Moolenbroek if (client->query.qname != client->query.origqname &&
3279*00b67f09SDavid van Moolenbroek client->query.origqname != NULL) {
3280*00b67f09SDavid van Moolenbroek origfor = " for ";
3281*00b67f09SDavid van Moolenbroek dns_name_format(client->query.origqname, original,
3282*00b67f09SDavid van Moolenbroek sizeof(original));
3283*00b67f09SDavid van Moolenbroek } else {
3284*00b67f09SDavid van Moolenbroek origfor = "";
3285*00b67f09SDavid van Moolenbroek original[0] = '\0';
3286*00b67f09SDavid van Moolenbroek }
3287*00b67f09SDavid van Moolenbroek rdataset = ISC_LIST_HEAD(client->query.qname->list);
3288*00b67f09SDavid van Moolenbroek if (rdataset == NULL && client->query.origqname != NULL)
3289*00b67f09SDavid van Moolenbroek rdataset = ISC_LIST_HEAD(client->query.origqname->list);
3290*00b67f09SDavid van Moolenbroek if (rdataset != NULL) {
3291*00b67f09SDavid van Moolenbroek dns_rdatatype_format(rdataset->type, typebuf,
3292*00b67f09SDavid van Moolenbroek sizeof(typebuf));
3293*00b67f09SDavid van Moolenbroek dns_rdataclass_format(rdataset->rdclass, classbuf,
3294*00b67f09SDavid van Moolenbroek sizeof(classbuf));
3295*00b67f09SDavid van Moolenbroek } else {
3296*00b67f09SDavid van Moolenbroek strcpy(typebuf, "-");
3297*00b67f09SDavid van Moolenbroek strcpy(classbuf, "-");
3298*00b67f09SDavid van Moolenbroek }
3299*00b67f09SDavid van Moolenbroek UNLOCK(&client->query.fetchlock);
3300*00b67f09SDavid van Moolenbroek fprintf(f, "; client %s%s%s: id %u '%s/%s/%s'%s%s "
3301*00b67f09SDavid van Moolenbroek "requesttime %d\n", peerbuf, sep, name,
3302*00b67f09SDavid van Moolenbroek client->message->id, namebuf, typebuf, classbuf,
3303*00b67f09SDavid van Moolenbroek origfor, original, client->requesttime);
3304*00b67f09SDavid van Moolenbroek client = ISC_LIST_NEXT(client, rlink);
3305*00b67f09SDavid van Moolenbroek }
3306*00b67f09SDavid van Moolenbroek UNLOCK(&manager->reclock);
3307*00b67f09SDavid van Moolenbroek }
3308*00b67f09SDavid van Moolenbroek
3309*00b67f09SDavid van Moolenbroek void
ns_client_qnamereplace(ns_client_t * client,dns_name_t * name)3310*00b67f09SDavid van Moolenbroek ns_client_qnamereplace(ns_client_t *client, dns_name_t *name) {
3311*00b67f09SDavid van Moolenbroek LOCK(&client->query.fetchlock);
3312*00b67f09SDavid van Moolenbroek if (client->query.restarts > 0) {
3313*00b67f09SDavid van Moolenbroek /*
3314*00b67f09SDavid van Moolenbroek * client->query.qname was dynamically allocated.
3315*00b67f09SDavid van Moolenbroek */
3316*00b67f09SDavid van Moolenbroek dns_message_puttempname(client->message,
3317*00b67f09SDavid van Moolenbroek &client->query.qname);
3318*00b67f09SDavid van Moolenbroek }
3319*00b67f09SDavid van Moolenbroek client->query.qname = name;
3320*00b67f09SDavid van Moolenbroek UNLOCK(&client->query.fetchlock);
3321*00b67f09SDavid van Moolenbroek }
3322*00b67f09SDavid van Moolenbroek
3323*00b67f09SDavid van Moolenbroek isc_result_t
ns_client_sourceip(dns_clientinfo_t * ci,isc_sockaddr_t ** addrp)3324*00b67f09SDavid van Moolenbroek ns_client_sourceip(dns_clientinfo_t *ci, isc_sockaddr_t **addrp) {
3325*00b67f09SDavid van Moolenbroek ns_client_t *client = (ns_client_t *) ci->data;
3326*00b67f09SDavid van Moolenbroek
3327*00b67f09SDavid van Moolenbroek REQUIRE(NS_CLIENT_VALID(client));
3328*00b67f09SDavid van Moolenbroek REQUIRE(addrp != NULL);
3329*00b67f09SDavid van Moolenbroek
3330*00b67f09SDavid van Moolenbroek *addrp = &client->peeraddr;
3331*00b67f09SDavid van Moolenbroek return (ISC_R_SUCCESS);
3332*00b67f09SDavid van Moolenbroek }
3333