Import bind 9.5.2 vendor sources.
[dragonfly.git] / contrib / bind-9.5.2 / lib / dns / tcpmsg.c
1 /*
2  * Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2001  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: tcpmsg.c,v 1.31 2007/06/19 23:47:16 tbox Exp $ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <isc/mem.h>
25 #include <isc/task.h>
26 #include <isc/util.h>
27
28 #include <dns/events.h>
29 #include <dns/result.h>
30 #include <dns/tcpmsg.h>
31
32 #ifdef TCPMSG_DEBUG
33 #include <stdio.h>              /* Required for printf. */
34 #define XDEBUG(x) printf x
35 #else
36 #define XDEBUG(x)
37 #endif
38
39 #define TCPMSG_MAGIC            ISC_MAGIC('T', 'C', 'P', 'm')
40 #define VALID_TCPMSG(foo)       ISC_MAGIC_VALID(foo, TCPMSG_MAGIC)
41
42 static void recv_length(isc_task_t *, isc_event_t *);
43 static void recv_message(isc_task_t *, isc_event_t *);
44
45
46 static void
47 recv_length(isc_task_t *task, isc_event_t *ev_in) {
48         isc_socketevent_t *ev = (isc_socketevent_t *)ev_in;
49         isc_event_t *dev;
50         dns_tcpmsg_t *tcpmsg = ev_in->ev_arg;
51         isc_region_t region;
52         isc_result_t result;
53
54         INSIST(VALID_TCPMSG(tcpmsg));
55
56         dev = &tcpmsg->event;
57         tcpmsg->address = ev->address;
58
59         if (ev->result != ISC_R_SUCCESS) {
60                 tcpmsg->result = ev->result;
61                 goto send_and_free;
62         }
63
64         /*
65          * Success.
66          */
67         tcpmsg->size = ntohs(tcpmsg->size);
68         if (tcpmsg->size == 0) {
69                 tcpmsg->result = ISC_R_UNEXPECTEDEND;
70                 goto send_and_free;
71         }
72         if (tcpmsg->size > tcpmsg->maxsize) {
73                 tcpmsg->result = ISC_R_RANGE;
74                 goto send_and_free;
75         }
76
77         region.base = isc_mem_get(tcpmsg->mctx, tcpmsg->size);
78         region.length = tcpmsg->size;
79         if (region.base == NULL) {
80                 tcpmsg->result = ISC_R_NOMEMORY;
81                 goto send_and_free;
82         }
83         XDEBUG(("Allocated %d bytes\n", tcpmsg->size));
84
85         isc_buffer_init(&tcpmsg->buffer, region.base, region.length);
86         result = isc_socket_recv(tcpmsg->sock, &region, 0,
87                                  task, recv_message, tcpmsg);
88         if (result != ISC_R_SUCCESS) {
89                 tcpmsg->result = result;
90                 goto send_and_free;
91         }
92
93         isc_event_free(&ev_in);
94         return;
95
96  send_and_free:
97         isc_task_send(tcpmsg->task, &dev);
98         tcpmsg->task = NULL;
99         isc_event_free(&ev_in);
100         return;
101 }
102
103 static void
104 recv_message(isc_task_t *task, isc_event_t *ev_in) {
105         isc_socketevent_t *ev = (isc_socketevent_t *)ev_in;
106         isc_event_t *dev;
107         dns_tcpmsg_t *tcpmsg = ev_in->ev_arg;
108
109         (void)task;
110
111         INSIST(VALID_TCPMSG(tcpmsg));
112
113         dev = &tcpmsg->event;
114         tcpmsg->address = ev->address;
115
116         if (ev->result != ISC_R_SUCCESS) {
117                 tcpmsg->result = ev->result;
118                 goto send_and_free;
119         }
120
121         tcpmsg->result = ISC_R_SUCCESS;
122         isc_buffer_add(&tcpmsg->buffer, ev->n);
123
124         XDEBUG(("Received %d bytes (of %d)\n", ev->n, tcpmsg->size));
125
126  send_and_free:
127         isc_task_send(tcpmsg->task, &dev);
128         tcpmsg->task = NULL;
129         isc_event_free(&ev_in);
130 }
131
132 void
133 dns_tcpmsg_init(isc_mem_t *mctx, isc_socket_t *sock, dns_tcpmsg_t *tcpmsg) {
134         REQUIRE(mctx != NULL);
135         REQUIRE(sock != NULL);
136         REQUIRE(tcpmsg != NULL);
137
138         tcpmsg->magic = TCPMSG_MAGIC;
139         tcpmsg->size = 0;
140         tcpmsg->buffer.base = NULL;
141         tcpmsg->buffer.length = 0;
142         tcpmsg->maxsize = 65535;                /* Largest message possible. */
143         tcpmsg->mctx = mctx;
144         tcpmsg->sock = sock;
145         tcpmsg->task = NULL;                    /* None yet. */
146         tcpmsg->result = ISC_R_UNEXPECTED;      /* None yet. */
147         /*
148          * Should probably initialize the event here, but it can wait.
149          */
150 }
151
152
153 void
154 dns_tcpmsg_setmaxsize(dns_tcpmsg_t *tcpmsg, unsigned int maxsize) {
155         REQUIRE(VALID_TCPMSG(tcpmsg));
156         REQUIRE(maxsize < 65536);
157
158         tcpmsg->maxsize = maxsize;
159 }
160
161
162 isc_result_t
163 dns_tcpmsg_readmessage(dns_tcpmsg_t *tcpmsg,
164                        isc_task_t *task, isc_taskaction_t action, void *arg)
165 {
166         isc_result_t result;
167         isc_region_t region;
168
169         REQUIRE(VALID_TCPMSG(tcpmsg));
170         REQUIRE(task != NULL);
171         REQUIRE(tcpmsg->task == NULL);  /* not currently in use */
172
173         if (tcpmsg->buffer.base != NULL) {
174                 isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base,
175                             tcpmsg->buffer.length);
176                 tcpmsg->buffer.base = NULL;
177                 tcpmsg->buffer.length = 0;
178         }
179
180         tcpmsg->task = task;
181         tcpmsg->action = action;
182         tcpmsg->arg = arg;
183         tcpmsg->result = ISC_R_UNEXPECTED;  /* unknown right now */
184
185         ISC_EVENT_INIT(&tcpmsg->event, sizeof(isc_event_t), 0, 0,
186                        DNS_EVENT_TCPMSG, action, arg, tcpmsg,
187                        NULL, NULL);
188
189         region.base = (unsigned char *)&tcpmsg->size;
190         region.length = 2;  /* isc_uint16_t */
191         result = isc_socket_recv(tcpmsg->sock, &region, 0,
192                                  tcpmsg->task, recv_length, tcpmsg);
193
194         if (result != ISC_R_SUCCESS)
195                 tcpmsg->task = NULL;
196
197         return (result);
198 }
199
200 void
201 dns_tcpmsg_cancelread(dns_tcpmsg_t *tcpmsg) {
202         REQUIRE(VALID_TCPMSG(tcpmsg));
203
204         isc_socket_cancel(tcpmsg->sock, NULL, ISC_SOCKCANCEL_RECV);
205 }
206
207 void
208 dns_tcpmsg_keepbuffer(dns_tcpmsg_t *tcpmsg, isc_buffer_t *buffer) {
209         REQUIRE(VALID_TCPMSG(tcpmsg));
210         REQUIRE(buffer != NULL);
211
212         *buffer = tcpmsg->buffer;
213         tcpmsg->buffer.base = NULL;
214         tcpmsg->buffer.length = 0;
215 }
216
217 #if 0
218 void
219 dns_tcpmsg_freebuffer(dns_tcpmsg_t *tcpmsg) {
220         REQUIRE(VALID_TCPMSG(tcpmsg));
221
222         if (tcpmsg->buffer.base == NULL)
223                 return;
224
225         isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base, tcpmsg->buffer.length);
226         tcpmsg->buffer.base = NULL;
227         tcpmsg->buffer.length = 0;
228 }
229 #endif
230
231 void
232 dns_tcpmsg_invalidate(dns_tcpmsg_t *tcpmsg) {
233         REQUIRE(VALID_TCPMSG(tcpmsg));
234
235         tcpmsg->magic = 0;
236
237         if (tcpmsg->buffer.base != NULL) {
238                 isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base,
239                             tcpmsg->buffer.length);
240                 tcpmsg->buffer.base = NULL;
241                 tcpmsg->buffer.length = 0;
242         }
243 }