- const
[oweals/gnunet.git] / src / fs / gnunet-service-fs_lc.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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  * @param tc task context
188  */
189 static void
190 client_request_destroy (void *cls,
191                         const struct GNUNET_SCHEDULER_TaskContext *tc)
192 {
193   struct ClientRequest *cr = cls;
194   struct GSF_LocalClient *lc;
195
196   cr->kill_task = NULL;
197   lc = cr->lc;
198   GNUNET_CONTAINER_DLL_remove (lc->cr_head, lc->cr_tail, cr);
199   GSF_pending_request_cancel_ (cr->pr, GNUNET_YES);
200   GNUNET_STATISTICS_update (GSF_stats,
201                             gettext_noop ("# client searches active"), -1,
202                             GNUNET_NO);
203   GNUNET_free (cr);
204 }
205
206
207 /**
208  * Handle a reply to a pending request.  Also called if a request
209  * expires (then with data == NULL).  The handler may be called
210  * many times (depending on the request type), but will not be
211  * called during or after a call to #GSF_pending_request_cancel()
212  * and will also not be called anymore after a call signalling
213  * expiration.
214  *
215  * @param cls user-specified closure
216  * @param eval evaluation of the result
217  * @param pr handle to the original pending request
218  * @param reply_anonymity_level anonymity level for the reply, UINT32_MAX for "unknown"
219  * @param expiration when does @a data expire?
220  * @param last_transmission when was the last time we've tried to download this block? (FOREVER if unknown)
221  * @param type type of the block
222  * @param data response data, NULL on request expiration
223  * @param data_len number of bytes in @a data
224  */
225 static void
226 client_response_handler (void *cls,
227                          enum GNUNET_BLOCK_EvaluationResult eval,
228                          struct GSF_PendingRequest *pr,
229                          uint32_t reply_anonymity_level,
230                          struct GNUNET_TIME_Absolute expiration,
231                          struct GNUNET_TIME_Absolute last_transmission,
232                          enum GNUNET_BLOCK_Type type,
233                          const void *data,
234                          size_t data_len)
235 {
236   struct ClientRequest *cr = cls;
237   struct GSF_LocalClient *lc;
238   struct ClientPutMessage *pm;
239   const struct GSF_PendingRequestData *prd;
240   size_t msize;
241
242   if (NULL == data)
243   {
244     /* local-only request, with no result, clean up. */
245     if (NULL == cr->kill_task)
246       cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy,
247                                                 cr);
248     return;
249   }
250   prd = GSF_pending_request_get_data_ (pr);
251   GNUNET_break (type != GNUNET_BLOCK_TYPE_ANY);
252   if ((prd->type != type) && (prd->type != GNUNET_BLOCK_TYPE_ANY))
253   {
254     GNUNET_break (0);
255     return;
256   }
257   GNUNET_STATISTICS_update (GSF_stats,
258                             gettext_noop
259                             ("# replies received for local clients"), 1,
260                             GNUNET_NO);
261   GNUNET_assert (pr == cr->pr);
262   lc = cr->lc;
263   msize = sizeof (struct ClientPutMessage) + data_len;
264   {
265     char buf[msize] GNUNET_ALIGN;
266
267     pm = (struct ClientPutMessage *) buf;
268     pm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_PUT);
269     pm->header.size = htons (msize);
270     pm->type = htonl (type);
271     pm->expiration = GNUNET_TIME_absolute_hton (expiration);
272     pm->last_transmission = GNUNET_TIME_absolute_hton (last_transmission);
273     pm->num_transmissions = htonl (prd->num_transmissions);
274     pm->respect_offered = htonl (prd->respect_offered);
275     memcpy (&pm[1], data, data_len);
276     GSF_local_client_transmit_ (lc, &pm->header);
277   }
278   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
279               "Queued reply to query `%s' for local client\n",
280               GNUNET_h2s (&prd->query), (unsigned int) prd->type);
281   if (GNUNET_BLOCK_EVALUATION_OK_LAST != eval)
282   {
283     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284                 "Evaluation %d - keeping query alive\n",
285                 (int) eval);
286     return;
287   }
288   if (NULL == cr->kill_task)
289     cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy, cr);
290 }
291
292
293 /**
294  * Handle START_SEARCH-message (search request from local client).
295  * Only responsible for creating the request entry itself and setting
296  * up reply callback and cancellation on client disconnect.  Does NOT
297  * execute the actual request strategy (planning).
298  *
299  * @param client identification of the client
300  * @param message the actual message
301  * @param prptr where to store the pending request handle for the request
302  * @return #GNUNET_YES to start local processing,
303  *         #GNUNET_NO to not (yet) start local processing,
304  *         #GNUNET_SYSERR on error
305  */
306 int
307 GSF_local_client_start_search_handler_ (struct GNUNET_SERVER_Client *client,
308                                         const struct GNUNET_MessageHeader *message,
309                                         struct GSF_PendingRequest **prptr)
310 {
311   static struct GNUNET_PeerIdentity all_zeros;
312   const struct SearchMessage *sm;
313   struct GSF_LocalClient *lc;
314   struct ClientRequest *cr;
315   struct GSF_PendingRequestData *prd;
316   uint16_t msize;
317   unsigned int sc;
318   enum GNUNET_BLOCK_Type type;
319   enum GSF_PendingRequestOptions options;
320
321   msize = ntohs (message->size);
322   if ((msize < sizeof (struct SearchMessage)) ||
323       (0 != (msize - sizeof (struct SearchMessage)) % sizeof (struct GNUNET_HashCode)))
324   {
325     GNUNET_break (0);
326     *prptr = NULL;
327     return GNUNET_SYSERR;
328   }
329   GNUNET_STATISTICS_update (GSF_stats,
330                             gettext_noop ("# client searches received"),
331                             1,
332                             GNUNET_NO);
333   sc = (msize - sizeof (struct SearchMessage)) / sizeof (struct GNUNET_HashCode);
334   sm = (const struct SearchMessage *) message;
335   type = ntohl (sm->type);
336   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337               "Received request for `%s' of type %u from local client\n",
338               GNUNET_h2s (&sm->query), (unsigned int) type);
339   lc = GSF_local_client_lookup_ (client);
340   cr = NULL;
341   /* detect duplicate UBLOCK requests */
342   if ((type == GNUNET_BLOCK_TYPE_FS_UBLOCK) ||
343       (type == GNUNET_BLOCK_TYPE_ANY))
344   {
345     cr = lc->cr_head;
346     while (NULL != cr)
347     {
348       prd = GSF_pending_request_get_data_ (cr->pr);
349       /* only unify with queries that hae not yet started local processing
350          (SEARCH_MESSAGE_OPTION_CONTINUED was always set) and that have a
351          matching query and type */
352       if ((GNUNET_YES != prd->has_started) &&
353           (0 != memcmp (&prd->query,
354                         &sm->query,
355                         sizeof (struct GNUNET_HashCode))) &&
356           (prd->type == type))
357         break;
358       cr = cr->next;
359     }
360   }
361   if (NULL != cr)
362   {
363     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364                 "Have existing request, merging content-seen lists.\n");
365     GSF_pending_request_update_ (cr->pr,
366                                  (const struct GNUNET_HashCode *) &sm[1],
367                                  sc);
368     GNUNET_STATISTICS_update (GSF_stats,
369                               gettext_noop
370                               ("# client searches updated (merged content seen list)"),
371                               1, GNUNET_NO);
372   }
373   else
374   {
375     GNUNET_STATISTICS_update (GSF_stats,
376                               gettext_noop ("# client searches active"), 1,
377                               GNUNET_NO);
378     cr = GNUNET_new (struct ClientRequest);
379     cr->lc = lc;
380     GNUNET_CONTAINER_DLL_insert (lc->cr_head,
381                                  lc->cr_tail,
382                                  cr);
383     options = GSF_PRO_LOCAL_REQUEST;
384     if (0 != (SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY & ntohl (sm->options)))
385       options |= GSF_PRO_LOCAL_ONLY;
386     cr->pr = GSF_pending_request_create_ (options, type,
387                                           &sm->query,
388                                           (0 !=
389                                            memcmp (&sm->target, &all_zeros,
390                                                    sizeof (struct GNUNET_PeerIdentity)))
391                                           ? &sm->target : NULL, NULL, 0,
392                                           0 /* bf */ ,
393                                           ntohl (sm->anonymity_level),
394                                           0 /* priority */ ,
395                                           0 /* ttl */ ,
396                                           0 /* sender PID */ ,
397                                           0 /* origin PID */ ,
398                                           (const struct GNUNET_HashCode *) &sm[1], sc,
399                                           &client_response_handler, cr);
400   }
401   *prptr = cr->pr;
402   return (0 !=
403           (SEARCH_MESSAGE_OPTION_CONTINUED & ntohl (sm->options))) ? GNUNET_NO :
404       GNUNET_YES;
405 }
406
407
408 /**
409  * Transmit the given message by copying it to the target buffer
410  * "buf".  "buf" will be NULL and "size" zero if the socket was closed
411  * for writing in the meantime.  In that case, do nothing
412  * (the disconnect or shutdown handler will take care of the rest).
413  * If we were able to transmit messages and there are still more
414  * pending, ask core again for further calls to this function.
415  *
416  * @param cls closure, pointer to the `struct GSF_LocalClient`
417  * @param size number of bytes available in @a buf
418  * @param buf where the callee should write the message
419  * @return number of bytes written to @a buf
420  */
421 static size_t
422 transmit_to_client (void *cls,
423                     size_t size,
424                     void *buf)
425 {
426   struct GSF_LocalClient *lc = cls;
427   char *cbuf = buf;
428   struct ClientResponse *res;
429   size_t msize;
430
431   lc->th = NULL;
432   if (NULL == buf)
433     return 0;
434   msize = 0;
435   while ((NULL != (res = lc->res_head)) && (res->msize <= size))
436   {
437     memcpy (&cbuf[msize], &res[1], res->msize);
438     msize += res->msize;
439     size -= res->msize;
440     GNUNET_CONTAINER_DLL_remove (lc->res_head, lc->res_tail, res);
441     GNUNET_free (res);
442   }
443   if (NULL != res)
444     lc->th =
445         GNUNET_SERVER_notify_transmit_ready (lc->client, res->msize,
446                                              GNUNET_TIME_UNIT_FOREVER_REL,
447                                              &transmit_to_client, lc);
448   return msize;
449 }
450
451
452 /**
453  * Transmit a message to the given local client as soon as possible.
454  * If the client disconnects before transmission, the message is
455  * simply discarded.
456  *
457  * @param lc recipient
458  * @param msg message to transmit to client
459  */
460 void
461 GSF_local_client_transmit_ (struct GSF_LocalClient *lc,
462                             const struct GNUNET_MessageHeader *msg)
463 {
464   struct ClientResponse *res;
465   size_t msize;
466
467   msize = ntohs (msg->size);
468   res = GNUNET_malloc (sizeof (struct ClientResponse) + msize);
469   res->lc = lc;
470   res->msize = msize;
471   memcpy (&res[1],
472           msg,
473           msize);
474   GNUNET_CONTAINER_DLL_insert_tail (lc->res_head,
475                                     lc->res_tail,
476                                     res);
477   if (NULL == lc->th)
478     lc->th =
479         GNUNET_SERVER_notify_transmit_ready (lc->client, msize,
480                                              GNUNET_TIME_UNIT_FOREVER_REL,
481                                              &transmit_to_client, lc);
482 }
483
484
485 /**
486  * A client disconnected from us.  Tear down the local client
487  * record.
488  *
489  * @param cls unused
490  * @param client handle of the client
491  */
492 void
493 GSF_client_disconnect_handler_ (void *cls,
494                                 struct GNUNET_SERVER_Client *client)
495 {
496   struct GSF_LocalClient *pos;
497   struct ClientRequest *cr;
498   struct ClientResponse *res;
499
500   pos = client_head;
501   while ((pos != NULL) && (pos->client != client))
502     pos = pos->next;
503   if (NULL == pos)
504     return;
505   while (NULL != (cr = pos->cr_head))
506   {
507     if (NULL != cr->kill_task)
508       GNUNET_SCHEDULER_cancel (cr->kill_task);
509     client_request_destroy (cr, NULL);
510   }
511   while (NULL != (res = pos->res_head))
512   {
513     GNUNET_CONTAINER_DLL_remove (pos->res_head, pos->res_tail, res);
514     GNUNET_free (res);
515   }
516   if (NULL != pos->th)
517   {
518     GNUNET_SERVER_notify_transmit_ready_cancel (pos->th);
519     pos->th = NULL;
520   }
521   GSF_handle_local_client_disconnect_ (pos);
522   GNUNET_CONTAINER_DLL_remove (client_head, client_tail, pos);
523   GNUNET_free (pos);
524 }
525
526
527 /* end of gnunet-service-fs_lc.c */