annotate src/io/rx_packet.c @ 0:d39e1d0d75b6

initial add
author paulo@hit-nxdomain.opendns.com
date Sat, 20 Feb 2010 21:18:28 -0800
parents
children
rev   line source
paulo@0 1 /*
paulo@0 2 * $Id: rx_packet.c,v 1.7 2004/02/15 04:53:38 hipnod Exp $
paulo@0 3 *
paulo@0 4 * Copyright (C) 2003 giFT project (gift.sourceforge.net)
paulo@0 5 *
paulo@0 6 * This program is free software; you can redistribute it and/or modify it
paulo@0 7 * under the terms of the GNU General Public License as published by the
paulo@0 8 * Free Software Foundation; either version 2, or (at your option) any
paulo@0 9 * later version.
paulo@0 10 *
paulo@0 11 * This program is distributed in the hope that it will be useful, but
paulo@0 12 * WITHOUT ANY WARRANTY; without even the implied warranty of
paulo@0 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
paulo@0 14 * General Public License for more details.
paulo@0 15 */
paulo@0 16
paulo@0 17 #include "gt_gnutella.h"
paulo@0 18
paulo@0 19 #include "rx_stack.h"
paulo@0 20 #include "rx_layer.h"
paulo@0 21
paulo@0 22 #include "gt_packet.h"
paulo@0 23 #include "rx_packet.h"
paulo@0 24
paulo@0 25 /*****************************************************************************/
paulo@0 26
paulo@0 27 struct rx_packet
paulo@0 28 {
paulo@0 29 struct io_buf *partial;
paulo@0 30 rx_packet_handler_t handler;
paulo@0 31 void *udata;
paulo@0 32 };
paulo@0 33
paulo@0 34 /*****************************************************************************/
paulo@0 35
paulo@0 36 #define RX_PACKET(rx) \
paulo@0 37 ((struct rx_packet *) ((rx)->udata))
paulo@0 38
paulo@0 39 /*****************************************************************************/
paulo@0 40
paulo@0 41 static void null_packet_handler (void *udata, GtPacket *packet)
paulo@0 42 {
paulo@0 43 gt_packet_free (packet);
paulo@0 44 }
paulo@0 45
paulo@0 46 static BOOL rx_packet_init (struct rx_layer *rx, void *udata)
paulo@0 47 {
paulo@0 48 struct rx_packet *rx_packet;
paulo@0 49
paulo@0 50 if (!(rx_packet = NEW (struct rx_packet)))
paulo@0 51 return FALSE;
paulo@0 52
paulo@0 53 rx->udata = rx_packet;
paulo@0 54
paulo@0 55 /* discard the packets received */
paulo@0 56 rx_packet->handler = null_packet_handler;
paulo@0 57 return TRUE;
paulo@0 58 }
paulo@0 59
paulo@0 60 static void rx_packet_destroy (struct rx_layer *rx)
paulo@0 61 {
paulo@0 62 struct rx_packet *rx_packet = RX_PACKET(rx);
paulo@0 63
paulo@0 64 io_buf_free (rx_packet->partial);
paulo@0 65 FREE (rx_packet);
paulo@0 66 }
paulo@0 67
paulo@0 68 static void rx_packet_disable (struct rx_layer *rx)
paulo@0 69 {
paulo@0 70 /* NOP: lower layer will stop sending us data -- it'd better
paulo@0 71 * stop now */
paulo@0 72 }
paulo@0 73
paulo@0 74 static void rx_packet_enable (struct rx_layer *rx)
paulo@0 75 {
paulo@0 76 /* NOP: the lower layer will start sending us data again */
paulo@0 77 }
paulo@0 78
paulo@0 79 /*****************************************************************************/
paulo@0 80
paulo@0 81 static GtPacket *make_packet (struct rx_layer *rx, struct rx_packet *rx_packet,
paulo@0 82 size_t packet_size)
paulo@0 83 {
paulo@0 84 GtPacket *packet;
paulo@0 85 struct io_buf *pbuf = rx_packet->partial;
paulo@0 86
paulo@0 87 assert (io_buf_len (pbuf) == packet_size);
paulo@0 88 assert (packet_size < GT_PACKET_MAX);
paulo@0 89
paulo@0 90 /* construct a complete packet from the data in the binary buf */
paulo@0 91 packet = gt_packet_unserialize (pbuf->data, packet_size);
paulo@0 92
paulo@0 93 /*
paulo@0 94 * TODO: gt_packet_unserialize() currently copies the data, but should
paulo@0 95 * really take ownership of the data in place. That would also allow the
paulo@0 96 * null terminator in the io_buf to terminate the last string in the
paulo@0 97 * packet, if any.
paulo@0 98 */
paulo@0 99 io_buf_free (pbuf);
paulo@0 100 rx_packet->partial = NULL;
paulo@0 101
paulo@0 102 if (!packet)
paulo@0 103 {
paulo@0 104 gt_rx_stack_abort (rx->stack);
paulo@0 105 return NULL;
paulo@0 106 }
paulo@0 107
paulo@0 108 return packet;
paulo@0 109 }
paulo@0 110
paulo@0 111 static BOOL fill_up_to (struct rx_layer *rx, struct io_buf *dst,
paulo@0 112 struct io_buf *src, size_t fill_size)
paulo@0 113 {
paulo@0 114 size_t new_len;
paulo@0 115 size_t old_len;
paulo@0 116 size_t len_to_read;
paulo@0 117
paulo@0 118 old_len = io_buf_len (dst);
paulo@0 119 new_len = io_buf_len (src);
paulo@0 120
paulo@0 121 /* skip if we already have enough */
paulo@0 122 if (old_len >= fill_size)
paulo@0 123 return TRUE;
paulo@0 124
paulo@0 125 len_to_read = MIN (fill_size - old_len, new_len);
paulo@0 126
paulo@0 127 /* ensure the packet has enough room */
paulo@0 128 if (!io_buf_resize (dst, old_len + len_to_read))
paulo@0 129 {
paulo@0 130 gt_rx_stack_abort (rx->stack);
paulo@0 131 return FALSE;
paulo@0 132 }
paulo@0 133
paulo@0 134 io_buf_copy (dst, src, len_to_read);
paulo@0 135
paulo@0 136 if (io_buf_len (dst) >= fill_size)
paulo@0 137 return TRUE;
paulo@0 138
paulo@0 139 return FALSE;
paulo@0 140 }
paulo@0 141
paulo@0 142 static BOOL fill_header (struct rx_layer *rx, struct rx_packet *rx_packet,
paulo@0 143 struct io_buf *io_buf)
paulo@0 144 {
paulo@0 145 struct io_buf *dst = rx_packet->partial;
paulo@0 146 struct io_buf *src = io_buf;
paulo@0 147
paulo@0 148 if (!fill_up_to (rx, dst, src, GNUTELLA_HDR_LEN))
paulo@0 149 {
paulo@0 150 /* this would fail if there was an alloc failure */
paulo@0 151 assert (io_buf_read_avail (io_buf) == 0);
paulo@0 152 return FALSE;
paulo@0 153 }
paulo@0 154
paulo@0 155 return TRUE;
paulo@0 156 }
paulo@0 157
paulo@0 158 /*
paulo@0 159 * We must read all the data the lower layer has sent and buffer
paulo@0 160 * partial packets, because otherwise we would poll the CPU if the
paulo@0 161 * packet were bigger than the buffer size of one of the layer
paulo@0 162 * layers under this one.
paulo@0 163 */
paulo@0 164 static BOOL read_packet (struct rx_layer *rx, struct rx_packet *rx_packet,
paulo@0 165 struct io_buf *io_buf, GtPacket **ret)
paulo@0 166 {
paulo@0 167 uint32_t payload_len;
paulo@0 168 size_t partial_len;
paulo@0 169 uint32_t packet_size;
paulo@0 170 struct io_buf *partial = rx_packet->partial;
paulo@0 171 GtPacket *pkt;
paulo@0 172
paulo@0 173 *ret = NULL;
paulo@0 174
paulo@0 175 partial_len = io_buf_len (partial);
paulo@0 176 assert (partial_len >= GNUTELLA_HDR_LEN);
paulo@0 177
paulo@0 178 /*
paulo@0 179 * The partial packet is now at least 23 bytes. Look in the header for
paulo@0 180 * the payload length so we know how much we need to read before the
paulo@0 181 * packet is complete.
paulo@0 182 */
paulo@0 183 payload_len = get_payload_len (partial->data);
paulo@0 184 packet_size = payload_len + GNUTELLA_HDR_LEN;
paulo@0 185
paulo@0 186 /*
paulo@0 187 * Check for wraparound, and reset the packet size to its payload len.
paulo@0 188 * Its likely we've experienced a protocol de-sync here. Set the size so
paulo@0 189 * the connection will be closed.
paulo@0 190 */
paulo@0 191 if (packet_size < GNUTELLA_HDR_LEN)
paulo@0 192 packet_size = GT_PACKET_MAX;
paulo@0 193
paulo@0 194 if (packet_size >= GT_PACKET_MAX)
paulo@0 195 {
paulo@0 196 if (IO_DEBUG)
paulo@0 197 GT->dbg (GT, "received too large packet(%d)", packet_size);
paulo@0 198
paulo@0 199 /* TODO: should send a BYE message here */
paulo@0 200 gt_rx_stack_abort (rx->stack);
paulo@0 201 return FALSE;
paulo@0 202 }
paulo@0 203
paulo@0 204 if (!fill_up_to (rx, partial, io_buf, packet_size))
paulo@0 205 {
paulo@0 206 /* this would fail if there was an alloc failure */
paulo@0 207 assert (io_buf_read_avail (io_buf) == 0);
paulo@0 208 return FALSE;
paulo@0 209 }
paulo@0 210
paulo@0 211 /* yay, read a packet */
paulo@0 212 pkt = make_packet (rx, rx_packet, packet_size);
paulo@0 213 *ret = pkt;
paulo@0 214
paulo@0 215 return (pkt == NULL ? FALSE : TRUE);
paulo@0 216 }
paulo@0 217
paulo@0 218 /*
paulo@0 219 * Receive a message buffer from the lower layer, parse it into as many
paulo@0 220 * packets as it contains, and pass those to our handler function.
paulo@0 221 *
paulo@0 222 * This is meant to be the top layer of the rx stack only.
paulo@0 223 *
paulo@0 224 * TODO: A handler could be implemented as another layer, should think
paulo@0 225 * about this approach instead.
paulo@0 226 */
paulo@0 227 static void rx_packet_recv (struct rx_layer *rx, struct io_buf *io_buf)
paulo@0 228 {
paulo@0 229 GtPacket *packet = NULL;
paulo@0 230 struct rx_packet *rx_packet;
paulo@0 231
paulo@0 232 rx_packet = RX_PACKET(rx);
paulo@0 233
paulo@0 234 while (rx->enabled && io_buf_read_avail (io_buf) > 0)
paulo@0 235 {
paulo@0 236 /* allocate a new partial buffer, if one is not present yet */
paulo@0 237 if (!rx_packet->partial &&
paulo@0 238 !(rx_packet->partial = io_buf_new (GNUTELLA_HDR_LEN)))
paulo@0 239 {
paulo@0 240 gt_rx_stack_abort (rx->stack);
paulo@0 241 break;
paulo@0 242 }
paulo@0 243
paulo@0 244 /* try to read the first 23 bytes */
paulo@0 245 if (!fill_header (rx, rx_packet, io_buf))
paulo@0 246 break;
paulo@0 247
paulo@0 248 /*
paulo@0 249 * Read the payload. If there arent enough bytes to complete the
paulo@0 250 * packet, we finish it later.
paulo@0 251 */
paulo@0 252 if (!read_packet (rx, rx_packet, io_buf, &packet))
paulo@0 253 {
paulo@0 254 assert (packet == NULL);
paulo@0 255 break;
paulo@0 256 }
paulo@0 257
paulo@0 258 assert (packet != NULL);
paulo@0 259 (*rx_packet->handler) (rx_packet->udata, packet);
paulo@0 260
paulo@0 261 /* freeing the packet here means the callback must make its own
paulo@0 262 * provisions for storing the packet's data */
paulo@0 263 gt_packet_free (packet);
paulo@0 264 packet = NULL;
paulo@0 265 }
paulo@0 266
paulo@0 267 io_buf_free (io_buf);
paulo@0 268 }
paulo@0 269
paulo@0 270 /*****************************************************************************/
paulo@0 271
paulo@0 272 struct rx_layer_ops gt_rx_packet_ops =
paulo@0 273 {
paulo@0 274 rx_packet_init,
paulo@0 275 rx_packet_destroy,
paulo@0 276 rx_packet_enable,
paulo@0 277 rx_packet_disable,
paulo@0 278 rx_packet_recv,
paulo@0 279 };
paulo@0 280
paulo@0 281 void gt_rx_packet_set_handler (struct rx_layer *rx,
paulo@0 282 rx_packet_handler_t handler,
paulo@0 283 void *udata)
paulo@0 284 {
paulo@0 285 struct rx_packet *rx_packet = RX_PACKET(rx);
paulo@0 286
paulo@0 287 rx_packet->handler = handler;
paulo@0 288 rx_packet->udata = udata;
paulo@0 289 }