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