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 42static void recv_length(isc_task_t *, isc_event_t *); 43static void recv_message(isc_task_t *, isc_event_t *); 44 45 46static void 47recv_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, ®ion, 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 103static void 104recv_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 132void 133dns_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 153void 154dns_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 162isc_result_t 163dns_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, ®ion, 0, 192 tcpmsg->task, recv_length, tcpmsg); 193 194 if (result != ISC_R_SUCCESS) 195 tcpmsg->task = NULL; 196 197 return (result); 198} 199 200void 201dns_tcpmsg_cancelread(dns_tcpmsg_t *tcpmsg) { 202 REQUIRE(VALID_TCPMSG(tcpmsg)); 203 204 isc_socket_cancel(tcpmsg->sock, NULL, ISC_SOCKCANCEL_RECV); 205} 206 207void 208dns_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 218void 219dns_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 231void 232dns_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} 244