fixing compiler warnings
[oweals/gnunet.git] / src / fs / gnunet-service-fs_lc.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011 GNUnet e.V.
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file fs/gnunet-service-fs_lc.c
22  * @brief API to handle 'local clients'
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet-service-fs.h"
27 #include "gnunet-service-fs_lc.h"
28 #include "gnunet-service-fs_cp.h"
29 #include "gnunet-service-fs_pr.h"
30
31
32 /**
33  * Doubly-linked list of requests we are performing
34  * on behalf of the same client.
35  */
36 struct ClientRequest
37 {
38
39   /**
40    * This is a doubly-linked list.
41    */
42   struct ClientRequest *next;
43
44   /**
45    * This is a doubly-linked list.
46    */
47   struct ClientRequest *prev;
48
49   /**
50    * Request this entry represents.
51    */
52   struct GSF_PendingRequest *pr;
53
54   /**
55    * Client list this request belongs to.
56    */
57   struct GSF_LocalClient *lc;
58
59   /**
60    * Task scheduled to destroy the request.
61    */
62   struct GNUNET_SCHEDULER_Task * kill_task;
63
64 };
65
66
67 /**
68  * Replies to be transmitted to the client.  The actual
69  * response message is allocated after this struct.
70  */
71 struct ClientResponse
72 {
73   /**
74    * This is a doubly-linked list.
75    */
76   struct ClientResponse *next;
77
78   /**
79    * This is a doubly-linked list.
80    */
81   struct ClientResponse *prev;
82
83   /**
84    * Client list entry this response belongs to.
85    */
86   struct GSF_LocalClient *lc;
87
88   /**
89    * Number of bytes in the response.
90    */
91   size_t msize;
92 };
93
94
95 /**
96  * A local client.
97  */
98 struct GSF_LocalClient
99 {
100
101   /**
102    * We keep clients in a DLL.
103    */
104   struct GSF_LocalClient *next;
105
106   /**
107    * We keep clients in a DLL.
108    */
109   struct GSF_LocalClient *prev;
110
111   /**
112    * ID of the client.
113    */
114   struct GNUNET_SERVER_Client *client;
115
116   /**
117    * Head of list of requests performed on behalf
118    * of this client right now.
119    */
120   struct ClientRequest *cr_head;
121
122   /**
123    * Tail of list of requests performed on behalf
124    * of this client right now.
125    */
126   struct ClientRequest *cr_tail;
127
128   /**
129    * Head of linked list of responses.
130    */
131   struct ClientResponse *res_head;
132
133   /**
134    * Tail of linked list of responses.
135    */
136   struct ClientResponse *res_tail;
137
138   /**
139    * Context for sending replies.
140    */
141   struct GNUNET_SERVER_TransmitHandle *th;
142
143 };
144
145
146 /**
147  * Head of linked list of our local clients.
148  */
149 static struct GSF_LocalClient *client_head;
150
151 /**
152  * Head of linked list of our local clients.
153  */
154 static struct GSF_LocalClient *client_tail;
155
156
157 /**
158  * Look up a local client record or create one if it
159  * doesn't exist yet.
160  *
161  * @param client handle of the client
162  * @return handle to local client entry
163  */
164 struct GSF_LocalClient *
165 GSF_local_client_lookup_ (struct GNUNET_SERVER_Client *client)
166 {
167   struct GSF_LocalClient *pos;
168
169   pos = client_head;
170   while ((NULL != pos) && (pos->client != client))
171     pos = pos->next;
172   if (NULL != pos)
173     return pos;
174   pos = GNUNET_new (struct GSF_LocalClient);
175   pos->client = client;
176   GNUNET_CONTAINER_DLL_insert (client_head,
177                                client_tail,
178                                pos);
179   return pos;
180 }
181
182
183 /**
184  * Free the given client request.
185  *
186  * @param cls the client request to free
187  */
188 static void
189 client_request_destroy (void *cls)
190 {
191   struct ClientRequest *cr = cls;
192   struct GSF_LocalClient *lc;
193
194   cr->kill_task = NULL;
195   lc = cr->lc;
196   GNUNET_CONTAINER_DLL_remove (lc->cr_head,
197                                lc->cr_tail,
198                                cr);
199   GSF_pending_request_cancel_ (cr->pr,
200                                GNUNET_YES);
201   GNUNET_STATISTICS_update (GSF_stats,
202                             gettext_noop ("# client searches active"),
203                             -1,
204                             GNUNET_NO);
205   GNUNET_free (cr);
206 }
207
208
209 /**
210  * Handle a reply to a pending request.  Also called if a request
211  * expires (then with data == NULL).  The handler may be called
212  * many times (depending on the request type), but will not be
213  * called during or after a call to #GSF_pending_request_cancel()
214  * and will also not be called anymore after a call signalling
215  * expiration.
216  *
217  * @param cls user-specified closure
218  * @param eval evaluation of the result
219  * @param pr handle to the original pending request
220  * @param reply_anonymity_level anonymity level for the reply, UINT32_MAX for "unknown"
221  * @param expiration when does @a data expire?
222  * @param last_transmission when was the last time we've tried to download this block? (FOREVER if unknown)
223  * @param type type of the block
224  * @param data response data, NULL on request expiration
225  * @param data_len number of bytes in @a data
226  */
227 static void
228 client_response_handler (void *cls,
229                          enum GNUNET_BLOCK_EvaluationResult eval,
230                          struct GSF_PendingRequest *pr,
231                          uint32_t reply_anonymity_level,
232                          struct GNUNET_TIME_Absolute expiration,
233                          struct GNUNET_TIME_Absolute last_transmission,
234                          enum GNUNET_BLOCK_Type type,
235                          const void *data,
236                          size_t data_len)
237 {
238   struct ClientRequest *cr = cls;
239   struct GSF_LocalClient *lc;
240   struct ClientPutMessage *pm;
241   const struct GSF_PendingRequestData *prd;
242   size_t msize;
243
244   if (NULL == data)
245   {
246     /* local-only request, with no result, clean up. */
247     if (NULL == cr->kill_task)
248       cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy,
249                                                 cr);
250     return;
251   }
252   prd = GSF_pending_request_get_data_ (pr);
253   GNUNET_break (type != GNUNET_BLOCK_TYPE_ANY);
254   if ((prd->type != type) && (prd->type != GNUNET_BLOCK_TYPE_ANY))
255   {
256     GNUNET_break (0);
257     return;
258   }
259   GNUNET_STATISTICS_update (GSF_stats,
260                             gettext_noop
261                             ("# replies received for local clients"), 1,
262                             GNUNET_NO);
263   GNUNET_assert (pr == cr->pr);
264   lc = cr->lc;
265   msize = sizeof (struct ClientPutMessage) + data_len;
266   {
267     char buf[msize] GNUNET_ALIGN;
268
269     pm = (struct ClientPutMessage *) buf;
270     pm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_PUT);
271     pm->header.size = htons (msize);
272     pm->type = htonl (type);
273     pm->expiration = GNUNET_TIME_absolute_hton (expiration);
274     pm->last_transmission = GNUNET_TIME_absolute_hton (last_transmission);
275     pm->num_transmissions = htonl (prd->num_transmissions);
276     pm->respect_offered = htonl (prd->respect_offered);
277     memcpy (&pm[1], data, data_len);
278     GSF_local_client_transmit_ (lc, &pm->header);
279   }
280   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281               "Queued reply to query `%s' for local client\n",
282               GNUNET_h2s (&prd->query));
283   if (GNUNET_BLOCK_EVALUATION_OK_LAST != eval)
284   {
285     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
286                 "Evaluation %d - keeping query alive\n",
287                 (int) eval);
288     return;
289   }
290   if (NULL == cr->kill_task)
291     cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy, cr);
292 }
293
294
295 /**
296  * Handle START_SEARCH-message (search request from local client).
297  * Only responsible for creating the request entry itself and setting
298  * up reply callback and cancellation on client disconnect.  Does NOT
299  * execute the actual request strategy (planning).
300  *
301  * @param client identification of the client
302  * @param message the actual message
303  * @param prptr where to store the pending request handle for the request
304  * @return #GNUNET_YES to start local processing,
305  *         #GNUNET_NO to not (yet) start local processing,
306  *         #GNUNET_SYSERR on error
307  */
308 int
309 GSF_local_client_start_search_handler_ (struct GNUNET_SERVER_Client *client,
310                                         const struct GNUNET_MessageHeader *message,
311                                         struct GSF_PendingRequest **prptr)
312 {
313   static struct GNUNET_PeerIdentity all_zeros;
314   const struct SearchMessage *sm;
315   struct GSF_LocalClient *lc;
316   struct ClientRequest *cr;
317   struct GSF_PendingRequestData *prd;
318   uint16_t msize;
319   unsigned int sc;
320   enum GNUNET_BLOCK_Type type;
321   enum GSF_PendingRequestOptions options;
322
323   msize = ntohs (message->size);
324   if ((msize < sizeof (struct SearchMessage)) ||
325       (0 != (msize - sizeof (struct SearchMessage)) % sizeof (struct GNUNET_HashCode)))
326   {
327     GNUNET_break (0);
328     *prptr = NULL;
329     return GNUNET_SYSERR;
330   }
331   GNUNET_STATISTICS_update (GSF_stats,
332                             gettext_noop ("# client searches received"),
333                             1,
334                             GNUNET_NO);
335   sc = (msize - sizeof (struct SearchMessage)) / sizeof (struct GNUNET_HashCode);
336   sm = (const struct SearchMessage *) message;
337   type = ntohl (sm->type);
338   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
339               "Received request for `%s' of type %u from local client\n",
340               GNUNET_h2s (&sm->query), (unsigned int) type);
341   lc = GSF_local_client_lookup_ (client);
342   cr = NULL;
343   /* detect duplicate UBLOCK requests */
344   if ((type == GNUNET_BLOCK_TYPE_FS_UBLOCK) ||
345       (type == GNUNET_BLOCK_TYPE_ANY))
346   {
347     cr = lc->cr_head;
348     while (NULL != cr)
349     {
350       prd = GSF_pending_request_get_data_ (cr->pr);
351       /* only unify with queries that hae not yet started local processing
352          (SEARCH_MESSAGE_OPTION_CONTINUED was always set) and that have a
353          matching query and type */
354       if ((GNUNET_YES != prd->has_started) &&
355           (0 != memcmp (&prd->query,
356                         &sm->query,
357                         sizeof (struct GNUNET_HashCode))) &&
358           (prd->type == type))
359         break;
360       cr = cr->next;
361     }
362   }
363   if (NULL != cr)
364   {
365     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
366                 "Have existing request, merging content-seen lists.\n");
367     GSF_pending_request_update_ (cr->pr,
368                                  (const struct GNUNET_HashCode *) &sm[1],
369                                  sc);
370     GNUNET_STATISTICS_update (GSF_stats,
371                               gettext_noop
372                               ("# client searches updated (merged content seen list)"),
373                               1, GNUNET_NO);
374   }
375   else
376   {
377     GNUNET_STATISTICS_update (GSF_stats,
378                               gettext_noop ("# client searches active"), 1,
379                               GNUNET_NO);
380     cr = GNUNET_new (struct ClientRequest);
381     cr->lc = lc;
382     GNUNET_CONTAINER_DLL_insert (lc->cr_head,
383                                  lc->cr_tail,
384                                  cr);
385     options = GSF_PRO_LOCAL_REQUEST;
386     if (0 != (SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY & ntohl (sm->options)))
387       options |= GSF_PRO_LOCAL_ONLY;
388     cr->pr = GSF_pending_request_create_ (options, type,
389                                           &sm->query,
390                                           (0 !=
391                                            memcmp (&sm->target, &all_zeros,
392                                                    sizeof (struct GNUNET_PeerIdentity)))
393                                           ? &sm->target : NULL, NULL, 0,
394                                           0 /* bf */ ,
395                                           ntohl (sm->anonymity_level),
396                                           0 /* priority */ ,
397                                           0 /* ttl */ ,
398                                           0 /* sender PID */ ,
399                                           0 /* origin PID */ ,
400                                           (const struct GNUNET_HashCode *) &sm[1], sc,
401                                           &client_response_handler, cr);
402   }
403   *prptr = cr->pr;
404   return (0 !=
405           (SEARCH_MESSAGE_OPTION_CONTINUED & ntohl (sm->options))) ? GNUNET_NO :
406       GNUNET_YES;
407 }
408
409
410 /**
411  * Transmit the given message by copying it to the target buffer
412  * "buf".  "buf" will be NULL and "size" zero if the socket was closed
413  * for writing in the meantime.  In that case, do nothing
414  * (the disconnect or shutdown handler will take care of the rest).
415  * If we were able to transmit messages and there are still more
416  * pending, ask core again for further calls to this function.
417  *
418  * @param cls closure, pointer to the `struct GSF_LocalClient`
419  * @param size number of bytes available in @a buf
420  * @param buf where the callee should write the message
421  * @return number of bytes written to @a buf
422  */
423 static size_t
424 transmit_to_client (void *cls,
425                     size_t size,
426                     void *buf)
427 {
428   struct GSF_LocalClient *lc = cls;
429   char *cbuf = buf;
430   struct ClientResponse *res;
431   size_t msize;
432
433   lc->th = NULL;
434   if (NULL == buf)
435     return 0;
436   msize = 0;
437   while ((NULL != (res = lc->res_head)) && (res->msize <= size))
438   {
439     memcpy (&cbuf[msize], &res[1], res->msize);
440     msize += res->msize;
441     size -= res->msize;
442     GNUNET_CONTAINER_DLL_remove (lc->res_head, lc->res_tail, res);
443     GNUNET_free (res);
444   }
445   if (NULL != res)
446     lc->th =
447         GNUNET_SERVER_notify_transmit_ready (lc->client, res->msize,
448                                              GNUNET_TIME_UNIT_FOREVER_REL,
449                                              &transmit_to_client, lc);
450   return msize;
451 }
452
453
454 /**
455  * Transmit a message to the given local client as soon as possible.
456  * If the client disconnects before transmission, the message is
457  * simply discarded.
458  *
459  * @param lc recipient
460  * @param msg message to transmit to client
461  */
462 void
463 GSF_local_client_transmit_ (struct GSF_LocalClient *lc,
464                             const struct GNUNET_MessageHeader *msg)
465 {
466   struct ClientResponse *res;
467   size_t msize;
468
469   msize = ntohs (msg->size);
470   res = GNUNET_malloc (sizeof (struct ClientResponse) + msize);
471   res->lc = lc;
472   res->msize = msize;
473   memcpy (&res[1],
474           msg,
475           msize);
476   GNUNET_CONTAINER_DLL_insert_tail (lc->res_head,
477                                     lc->res_tail,
478                                     res);
479   if (NULL == lc->th)
480     lc->th =
481         GNUNET_SERVER_notify_transmit_ready (lc->client, msize,
482                                              GNUNET_TIME_UNIT_FOREVER_REL,
483                                              &transmit_to_client, lc);
484 }
485
486
487 /**
488  * A client disconnected from us.  Tear down the local client
489  * record.
490  *
491  * @param cls unused
492  * @param client handle of the client
493  */
494 void
495 GSF_client_disconnect_handler_ (void *cls,
496                                 struct GNUNET_SERVER_Client *client)
497 {
498   struct GSF_LocalClient *pos;
499   struct ClientRequest *cr;
500   struct ClientResponse *res;
501
502   pos = client_head;
503   while ((pos != NULL) && (pos->client != client))
504     pos = pos->next;
505   if (NULL == pos)
506     return;
507   while (NULL != (cr = pos->cr_head))
508   {
509     if (NULL != cr->kill_task)
510       GNUNET_SCHEDULER_cancel (cr->kill_task);
511     client_request_destroy (cr);
512   }
513   while (NULL != (res = pos->res_head))
514   {
515     GNUNET_CONTAINER_DLL_remove (pos->res_head, pos->res_tail, res);
516     GNUNET_free (res);
517   }
518   if (NULL != pos->th)
519   {
520     GNUNET_SERVER_notify_transmit_ready_cancel (pos->th);
521     pos->th = NULL;
522   }
523   GSF_handle_local_client_disconnect_ (pos);
524   GNUNET_CONTAINER_DLL_remove (client_head, client_tail, pos);
525   GNUNET_free (pos);
526 }
527
528
529 /* end of gnunet-service-fs_lc.c */