Import bind-9.3.4
[dragonfly.git] / contrib / bind-9.3 / lib / dns / tcpmsg.c
1 /*
2  * Copyright (C) 2004, 2006  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2001  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and 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.24.206.3 2006/08/10 23:59:28 marka Exp $ */
19
20 #include <config.h>
21
22 #include <isc/mem.h>
23 #include <isc/task.h>
24 #include <isc/util.h>
25
26 #include <dns/events.h>
27 #include <dns/result.h>
28 #include <dns/tcpmsg.h>
29
30 #ifdef TCPMSG_DEBUG
31 #include <stdio.h>              /* Required for printf. */
32 #define XDEBUG(x) printf x
33 #else
34 #define XDEBUG(x)
35 #endif
36
37 #define TCPMSG_MAGIC            ISC_MAGIC('T', 'C', 'P', 'm')
38 #define VALID_TCPMSG(foo)       ISC_MAGIC_VALID(foo, TCPMSG_MAGIC)
39
40 static void recv_length(isc_task_t *, isc_event_t *);
41 static void recv_message(isc_task_t *, isc_event_t *);
42
43
44 static void
45 recv_length(isc_task_t *task, isc_event_t *ev_in) {
46         isc_socketevent_t *ev = (isc_socketevent_t *)ev_in;
47         isc_event_t *dev;
48         dns_tcpmsg_t *tcpmsg = ev_in->ev_arg;
49         isc_region_t region;
50         isc_result_t result;
51
52         INSIST(VALID_TCPMSG(tcpmsg));
53
54         dev = &tcpmsg->event;
55         tcpmsg->address = ev->address;
56
57         if (ev->result != ISC_R_SUCCESS) {
58                 tcpmsg->result = ev->result;
59                 goto send_and_free;
60         }
61
62         /*
63          * Success.
64          */
65         tcpmsg->size = ntohs(tcpmsg->size);
66         if (tcpmsg->size == 0) {
67                 tcpmsg->result = ISC_R_UNEXPECTEDEND;
68                 goto send_and_free;
69         }
70         if (tcpmsg->size > tcpmsg->maxsize) {
71                 tcpmsg->result = ISC_R_RANGE;
72                 goto send_and_free;
73         }
74
75         region.base = isc_mem_get(tcpmsg->mctx, tcpmsg->size);
76         region.length = tcpmsg->size;
77         if (region.base == NULL) {
78                 tcpmsg->result = ISC_R_NOMEMORY;
79                 goto send_and_free;
80         }
81         XDEBUG(("Allocated %d bytes\n", tcpmsg->size));
82
83         isc_buffer_init(&tcpmsg->buffer, region.base, region.length);
84         result = isc_socket_recv(tcpmsg->sock, &region, 0,
85                                  task, recv_message, tcpmsg);
86         if (result != ISC_R_SUCCESS) {
87                 tcpmsg->result = result;
88                 goto send_and_free;
89         }
90
91         isc_event_free(&ev_in);
92         return;
93
94  send_and_free:
95         isc_task_send(tcpmsg->task, &dev);
96         tcpmsg->task = NULL;
97         isc_event_free(&ev_in);
98         return;
99 }
100
101 static void
102 recv_message(isc_task_t *task, isc_event_t *ev_in) {
103         isc_socketevent_t *ev = (isc_socketevent_t *)ev_in;
104         isc_event_t *dev;
105         dns_tcpmsg_t *tcpmsg = ev_in->ev_arg;
106
107         (void)task;
108
109         INSIST(VALID_TCPMSG(tcpmsg));
110
111         dev = &tcpmsg->event;
112         tcpmsg->address = ev->address;
113
114         if (ev->result != ISC_R_SUCCESS) {
115                 tcpmsg->result = ev->result;
116                 goto send_and_free;
117         }
118
119         tcpmsg->result = ISC_R_SUCCESS;
120         isc_buffer_add(&tcpmsg->buffer, ev->n);
121
122         XDEBUG(("Received %d bytes (of %d)\n", ev->n, tcpmsg->size));
123
124  send_and_free:
125         isc_task_send(tcpmsg->task, &dev);
126         tcpmsg->task = NULL;
127         isc_event_free(&ev_in);
128 }
129
130 void
131 dns_tcpmsg_init(isc_mem_t *mctx, isc_socket_t *sock, dns_tcpmsg_t *tcpmsg) {
132         REQUIRE(mctx != NULL);
133         REQUIRE(sock != NULL);
134         REQUIRE(tcpmsg != NULL);
135
136         tcpmsg->magic = TCPMSG_MAGIC;
137         tcpmsg->size = 0;
138         tcpmsg->buffer.base = NULL;
139         tcpmsg->buffer.length = 0;
140         tcpmsg->maxsize = 65535;                /* Largest message possible. */
141         tcpmsg->mctx = mctx;
142         tcpmsg->sock = sock;
143         tcpmsg->task = NULL;                    /* None yet. */
144         tcpmsg->result = ISC_R_UNEXPECTED;      /* None yet. */
145         /*
146          * Should probably initialize the event here, but it can wait.
147          */
148 }
149
150
151 void
152 dns_tcpmsg_setmaxsize(dns_tcpmsg_t *tcpmsg, unsigned int maxsize) {
153         REQUIRE(VALID_TCPMSG(tcpmsg));
154         REQUIRE(maxsize < 65536);
155
156         tcpmsg->maxsize = maxsize;
157 }
158
159
160 isc_result_t
161 dns_tcpmsg_readmessage(dns_tcpmsg_t *tcpmsg,
162                        isc_task_t *task, isc_taskaction_t action, void *arg)
163 {
164         isc_result_t result;
165         isc_region_t region;
166
167         REQUIRE(VALID_TCPMSG(tcpmsg));
168         REQUIRE(task != NULL);
169         REQUIRE(tcpmsg->task == NULL);  /* not currently in use */
170
171         if (tcpmsg->buffer.base != NULL) {
172                 isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base,
173                             tcpmsg->buffer.length);
174                 tcpmsg->buffer.base = NULL;
175                 tcpmsg->buffer.length = 0;
176         }
177
178         tcpmsg->task = task;
179         tcpmsg->action = action;
180         tcpmsg->arg = arg;
181         tcpmsg->result = ISC_R_UNEXPECTED;  /* unknown right now */
182
183         ISC_EVENT_INIT(&tcpmsg->event, sizeof(isc_event_t), 0, 0,
184                        DNS_EVENT_TCPMSG, action, arg, tcpmsg,
185                        NULL, NULL);
186
187         region.base = (unsigned char *)&tcpmsg->size;
188         region.length = 2;  /* isc_uint16_t */
189         result = isc_socket_recv(tcpmsg->sock, &region, 0,
190                                  tcpmsg->task, recv_length, tcpmsg);
191
192         if (result != ISC_R_SUCCESS)
193                 tcpmsg->task = NULL;
194
195         return (result);
196 }
197
198 void
199 dns_tcpmsg_cancelread(dns_tcpmsg_t *tcpmsg) {
200         REQUIRE(VALID_TCPMSG(tcpmsg));
201
202         isc_socket_cancel(tcpmsg->sock, NULL, ISC_SOCKCANCEL_RECV);
203 }
204
205 void
206 dns_tcpmsg_keepbuffer(dns_tcpmsg_t *tcpmsg, isc_buffer_t *buffer) {
207         REQUIRE(VALID_TCPMSG(tcpmsg));
208         REQUIRE(buffer != NULL);
209
210         *buffer = tcpmsg->buffer;
211         tcpmsg->buffer.base = NULL;
212         tcpmsg->buffer.length = 0;
213 }
214
215 #if 0
216 void
217 dns_tcpmsg_freebuffer(dns_tcpmsg_t *tcpmsg) {
218         REQUIRE(VALID_TCPMSG(tcpmsg));
219
220         if (tcpmsg->buffer.base == NULL)
221                 return;
222
223         isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base, tcpmsg->buffer.length);
224         tcpmsg->buffer.base = NULL;
225         tcpmsg->buffer.length = 0;
226 }
227 #endif
228
229 void
230 dns_tcpmsg_invalidate(dns_tcpmsg_t *tcpmsg) {
231         REQUIRE(VALID_TCPMSG(tcpmsg));
232
233         tcpmsg->magic = 0;
234
235         if (tcpmsg->buffer.base != NULL) {
236                 isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base,
237                             tcpmsg->buffer.length);
238                 tcpmsg->buffer.base = NULL;
239                 tcpmsg->buffer.length = 0;
240         }
241 }