fix div by zero
[oweals/gnunet.git] / src / transport / tcp_server_mst_legacy.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file util/server_mst.c
21  * @brief convenience functions for handling inbound message buffers
22  * @author Christian Grothoff
23  */
24
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27
28
29 #if HAVE_UNALIGNED_64_ACCESS
30 #define ALIGN_FACTOR 4
31 #else
32 #define ALIGN_FACTOR 8
33 #endif
34
35
36 /**
37  * Handle to a message stream tokenizer.
38  */
39 struct GNUNET_SERVER_MessageStreamTokenizer
40 {
41
42   /**
43    * Function to call on completed messages.
44    */
45   GNUNET_SERVER_MessageTokenizerCallback cb;
46
47   /**
48    * Closure for @e cb.
49    */
50   void *cb_cls;
51
52   /**
53    * Size of the buffer (starting at @e hdr).
54    */
55   size_t curr_buf;
56
57   /**
58    * How many bytes in buffer have we already processed?
59    */
60   size_t off;
61
62   /**
63    * How many bytes in buffer are valid right now?
64    */
65   size_t pos;
66
67   /**
68    * Beginning of the buffer.  Typed like this to force alignment.
69    */
70   struct GNUNET_MessageHeader *hdr;
71
72 };
73
74
75
76 /**
77  * Create a message stream tokenizer.
78  *
79  * @param cb function to call on completed messages
80  * @param cb_cls closure for @a cb
81  * @return handle to tokenizer
82  */
83 struct GNUNET_SERVER_MessageStreamTokenizer *
84 GNUNET_SERVER_mst_create (GNUNET_SERVER_MessageTokenizerCallback cb,
85                           void *cb_cls)
86 {
87   struct GNUNET_SERVER_MessageStreamTokenizer *ret;
88
89   ret = GNUNET_new (struct GNUNET_SERVER_MessageStreamTokenizer);
90   ret->hdr = GNUNET_malloc (GNUNET_MIN_MESSAGE_SIZE);
91   ret->curr_buf = GNUNET_MIN_MESSAGE_SIZE;
92   ret->cb = cb;
93   ret->cb_cls = cb_cls;
94   return ret;
95 }
96
97
98 /**
99  * Add incoming data to the receive buffer and call the
100  * callback for all complete messages.
101  *
102  * @param mst tokenizer to use
103  * @param client_identity ID of client for which this is a buffer
104  * @param buf input data to add
105  * @param size number of bytes in @a buf
106  * @param purge should any excess bytes in the buffer be discarded
107  *       (i.e. for packet-based services like UDP)
108  * @param one_shot only call callback once, keep rest of message in buffer
109  * @return #GNUNET_OK if we are done processing (need more data)
110  *         #GNUNET_NO if @a one_shot was set and we have another message ready
111  *         #GNUNET_SYSERR if the data stream is corrupt
112  */
113 int
114 GNUNET_SERVER_mst_receive (struct GNUNET_SERVER_MessageStreamTokenizer *mst,
115                            void *client_identity,
116                            const char *buf, size_t size,
117                            int purge, int one_shot)
118 {
119   const struct GNUNET_MessageHeader *hdr;
120   size_t delta;
121   uint16_t want;
122   char *ibuf;
123   int need_align;
124   unsigned long offset;
125   int ret;
126
127   GNUNET_assert (mst->off <= mst->pos);
128   GNUNET_assert (mst->pos <= mst->curr_buf);
129   LOG (GNUNET_ERROR_TYPE_DEBUG,
130        "Server-mst receives %u bytes with %u bytes already in private buffer\n",
131        (unsigned int) size, (unsigned int) (mst->pos - mst->off));
132   ret = GNUNET_OK;
133   ibuf = (char *) mst->hdr;
134   while (mst->pos > 0)
135   {
136 do_align:
137     GNUNET_assert (mst->pos >= mst->off);
138     if ((mst->curr_buf - mst->off < sizeof (struct GNUNET_MessageHeader)) ||
139         (0 != (mst->off % ALIGN_FACTOR)))
140     {
141       /* need to align or need more space */
142       mst->pos -= mst->off;
143       memmove (ibuf, &ibuf[mst->off], mst->pos);
144       mst->off = 0;
145     }
146     if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
147     {
148       delta =
149           GNUNET_MIN (sizeof (struct GNUNET_MessageHeader) -
150                       (mst->pos - mst->off), size);
151       GNUNET_memcpy (&ibuf[mst->pos], buf, delta);
152       mst->pos += delta;
153       buf += delta;
154       size -= delta;
155     }
156     if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
157     {
158       if (purge)
159       {
160         mst->off = 0;
161         mst->pos = 0;
162       }
163       return GNUNET_OK;
164     }
165     hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
166     want = ntohs (hdr->size);
167     if (want < sizeof (struct GNUNET_MessageHeader))
168     {
169       GNUNET_break_op (0);
170       return GNUNET_SYSERR;
171     }
172     if ( (mst->curr_buf - mst->off < want) &&
173          (mst->off > 0) )
174     {
175       /* can get more space by moving */
176       mst->pos -= mst->off;
177       memmove (ibuf, &ibuf[mst->off], mst->pos);
178       mst->off = 0;
179     }
180     if (mst->curr_buf < want)
181     {
182       /* need to get more space by growing buffer */
183       GNUNET_assert (0 == mst->off);
184       mst->hdr = GNUNET_realloc (mst->hdr, want);
185       ibuf = (char *) mst->hdr;
186       mst->curr_buf = want;
187     }
188     hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
189     if (mst->pos - mst->off < want)
190     {
191       delta = GNUNET_MIN (want - (mst->pos - mst->off), size);
192       GNUNET_assert (mst->pos + delta <= mst->curr_buf);
193       GNUNET_memcpy (&ibuf[mst->pos], buf, delta);
194       mst->pos += delta;
195       buf += delta;
196       size -= delta;
197     }
198     if (mst->pos - mst->off < want)
199     {
200       if (purge)
201       {
202         mst->off = 0;
203         mst->pos = 0;
204       }
205       return GNUNET_OK;
206     }
207     if (one_shot == GNUNET_SYSERR)
208     {
209       /* cannot call callback again, but return value saying that
210        * we have another full message in the buffer */
211       ret = GNUNET_NO;
212       goto copy;
213     }
214     if (one_shot == GNUNET_YES)
215       one_shot = GNUNET_SYSERR;
216     mst->off += want;
217     if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr))
218       return GNUNET_SYSERR;
219     if (mst->off == mst->pos)
220     {
221       /* reset to beginning of buffer, it's free right now! */
222       mst->off = 0;
223       mst->pos = 0;
224     }
225   }
226   GNUNET_assert (0 == mst->pos);
227   while (size > 0)
228   {
229     LOG (GNUNET_ERROR_TYPE_DEBUG,
230          "Server-mst has %u bytes left in inbound buffer\n",
231          (unsigned int) size);
232     if (size < sizeof (struct GNUNET_MessageHeader))
233       break;
234     offset = (unsigned long) buf;
235     need_align = (0 != (offset % ALIGN_FACTOR)) ? GNUNET_YES : GNUNET_NO;
236     if (GNUNET_NO == need_align)
237     {
238       /* can try to do zero-copy and process directly from original buffer */
239       hdr = (const struct GNUNET_MessageHeader *) buf;
240       want = ntohs (hdr->size);
241       if (want < sizeof (struct GNUNET_MessageHeader))
242       {
243         GNUNET_break_op (0);
244         mst->off = 0;
245         return GNUNET_SYSERR;
246       }
247       if (size < want)
248         break;                  /* or not: buffer incomplete, so copy to private buffer... */
249       if (one_shot == GNUNET_SYSERR)
250       {
251         /* cannot call callback again, but return value saying that
252          * we have another full message in the buffer */
253         ret = GNUNET_NO;
254         goto copy;
255       }
256       if (one_shot == GNUNET_YES)
257         one_shot = GNUNET_SYSERR;
258       if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr))
259         return GNUNET_SYSERR;
260       buf += want;
261       size -= want;
262     }
263     else
264     {
265       /* need to copy to private buffer to align;
266        * yes, we go a bit more spagetti than usual here */
267       goto do_align;
268     }
269   }
270 copy:
271   if ((size > 0) && (!purge))
272   {
273     if (size + mst->pos > mst->curr_buf)
274     {
275       mst->hdr = GNUNET_realloc (mst->hdr, size + mst->pos);
276       ibuf = (char *) mst->hdr;
277       mst->curr_buf = size + mst->pos;
278     }
279     GNUNET_assert (size + mst->pos <= mst->curr_buf);
280     GNUNET_memcpy (&ibuf[mst->pos], buf, size);
281     mst->pos += size;
282   }
283   if (purge)
284   {
285     mst->off = 0;
286     mst->pos = 0;
287   }
288   LOG (GNUNET_ERROR_TYPE_DEBUG,
289        "Server-mst leaves %u bytes in private buffer\n",
290        (unsigned int) (mst->pos - mst->off));
291   return ret;
292 }
293
294
295 /**
296  * Destroys a tokenizer.
297  *
298  * @param mst tokenizer to destroy
299  */
300 void
301 GNUNET_SERVER_mst_destroy (struct GNUNET_SERVER_MessageStreamTokenizer *mst)
302 {
303   GNUNET_free (mst->hdr);
304   GNUNET_free (mst);
305 }
306
307
308
309 /* end of server_mst.c */