Import of bind-9.3.2-P1
[dragonfly.git] / contrib / bind-9.3 / lib / dns / tcpmsg.c
1 /*
2  * Copyright (C) 2004  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.1 2004/03/06 08:13:46 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
56         if (ev->result != ISC_R_SUCCESS) {
57                 tcpmsg->result = ev->result;
58                 goto send_and_free;
59         }
60
61         /*
62          * Success.
63          */
64         tcpmsg->size = ntohs(tcpmsg->size);
65         if (tcpmsg->size == 0) {
66                 tcpmsg->result = ISC_R_UNEXPECTEDEND;
67                 goto send_and_free;
68         }
69         if (tcpmsg->size > tcpmsg->maxsize) {
70                 tcpmsg->result = ISC_R_RANGE;
71                 goto send_and_free;
72         }
73
74         region.base = isc_mem_get(tcpmsg->mctx, tcpmsg->size);
75         region.length = tcpmsg->size;
76         if (region.base == NULL) {
77                 tcpmsg->result = ISC_R_NOMEMORY;
78                 goto send_and_free;
79         }
80         XDEBUG(("Allocated %d bytes\n", tcpmsg->size));
81
82         isc_buffer_init(&tcpmsg->buffer, region.base, region.length);
83         result = isc_socket_recv(tcpmsg->sock, &region, 0,
84                                  task, recv_message, tcpmsg);
85         if (result != ISC_R_SUCCESS) {
86                 tcpmsg->result = result;
87                 goto send_and_free;
88         }
89
90         isc_event_free(&ev_in);
91         return;
92
93  send_and_free:
94         isc_task_send(tcpmsg->task, &dev);
95         tcpmsg->task = NULL;
96         isc_event_free(&ev_in);
97         return;
98 }
99
100 static void
101 recv_message(isc_task_t *task, isc_event_t *ev_in) {
102         isc_socketevent_t *ev = (isc_socketevent_t *)ev_in;
103         isc_event_t *dev;
104         dns_tcpmsg_t *tcpmsg = ev_in->ev_arg;
105
106         (void)task;
107
108         INSIST(VALID_TCPMSG(tcpmsg));
109
110         dev = &tcpmsg->event;
111
112         if (ev->result != ISC_R_SUCCESS) {
113                 tcpmsg->result = ev->result;
114                 goto send_and_free;
115         }
116
117         tcpmsg->result = ISC_R_SUCCESS;
118         isc_buffer_add(&tcpmsg->buffer, ev->n);
119         tcpmsg->address = ev->address;
120
121         XDEBUG(("Received %d bytes (of %d)\n", ev->n, tcpmsg->size));
122
123  send_and_free:
124         isc_task_send(tcpmsg->task, &dev);
125         tcpmsg->task = NULL;
126         isc_event_free(&ev_in);
127 }
128
129 void
130 dns_tcpmsg_init(isc_mem_t *mctx, isc_socket_t *sock, dns_tcpmsg_t *tcpmsg) {
131         REQUIRE(mctx != NULL);
132         REQUIRE(sock != NULL);
133         REQUIRE(tcpmsg != NULL);
134
135         tcpmsg->magic = TCPMSG_MAGIC;
136         tcpmsg->size = 0;
137         tcpmsg->buffer.base = NULL;
138         tcpmsg->buffer.length = 0;
139         tcpmsg->maxsize = 65535;                /* Largest message possible. */
140         tcpmsg->mctx = mctx;
141         tcpmsg->sock = sock;
142         tcpmsg->task = NULL;                    /* None yet. */
143         tcpmsg->result = ISC_R_UNEXPECTED;      /* None yet. */
144         /*
145          * Should probably initialize the event here, but it can wait.
146          */
147 }
148
149
150 void
151 dns_tcpmsg_setmaxsize(dns_tcpmsg_t *tcpmsg, unsigned int maxsize) {
152         REQUIRE(VALID_TCPMSG(tcpmsg));
153         REQUIRE(maxsize < 65536);
154
155         tcpmsg->maxsize = maxsize;
156 }
157
158
159 isc_result_t
160 dns_tcpmsg_readmessage(dns_tcpmsg_t *tcpmsg,
161                        isc_task_t *task, isc_taskaction_t action, void *arg)
162 {
163         isc_result_t result;
164         isc_region_t region;
165
166         REQUIRE(VALID_TCPMSG(tcpmsg));
167         REQUIRE(task != NULL);
168         REQUIRE(tcpmsg->task == NULL);  /* not currently in use */
169
170         if (tcpmsg->buffer.base != NULL) {
171                 isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base,
172                             tcpmsg->buffer.length);
173                 tcpmsg->buffer.base = NULL;
174                 tcpmsg->buffer.length = 0;
175         }
176
177         tcpmsg->task = task;
178         tcpmsg->action = action;
179         tcpmsg->arg = arg;
180         tcpmsg->result = ISC_R_UNEXPECTED;  /* unknown right now */
181
182         ISC_EVENT_INIT(&tcpmsg->event, sizeof(isc_event_t), 0, 0,
183                        DNS_EVENT_TCPMSG, action, arg, tcpmsg,
184                        NULL, NULL);
185
186         region.base = (unsigned char *)&tcpmsg->size;
187         region.length = 2;  /* isc_uint16_t */
188         result = isc_socket_recv(tcpmsg->sock, &region, 0,
189                                  tcpmsg->task, recv_length, tcpmsg);
190
191         if (result != ISC_R_SUCCESS)
192                 tcpmsg->task = NULL;
193
194         return (result);
195 }
196
197 void
198 dns_tcpmsg_cancelread(dns_tcpmsg_t *tcpmsg) {
199         REQUIRE(VALID_TCPMSG(tcpmsg));
200
201         isc_socket_cancel(tcpmsg->sock, NULL, ISC_SOCKCANCEL_RECV);
202 }
203
204 void
205 dns_tcpmsg_keepbuffer(dns_tcpmsg_t *tcpmsg, isc_buffer_t *buffer) {
206         REQUIRE(VALID_TCPMSG(tcpmsg));
207         REQUIRE(buffer != NULL);
208
209         *buffer = tcpmsg->buffer;
210         tcpmsg->buffer.base = NULL;
211         tcpmsg->buffer.length = 0;
212 }
213
214 #if 0
215 void
216 dns_tcpmsg_freebuffer(dns_tcpmsg_t *tcpmsg) {
217         REQUIRE(VALID_TCPMSG(tcpmsg));
218
219         if (tcpmsg->buffer.base == NULL)
220                 return;
221
222         isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base, tcpmsg->buffer.length);
223         tcpmsg->buffer.base = NULL;
224         tcpmsg->buffer.length = 0;
225 }
226 #endif
227
228 void
229 dns_tcpmsg_invalidate(dns_tcpmsg_t *tcpmsg) {
230         REQUIRE(VALID_TCPMSG(tcpmsg));
231
232         tcpmsg->magic = 0;
233
234         if (tcpmsg->buffer.base != NULL) {
235                 isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base,
236                             tcpmsg->buffer.length);
237                 tcpmsg->buffer.base = NULL;
238                 tcpmsg->buffer.length = 0;
239         }
240 }