gnunet

Main GNUnet Logic
Log | Files | Refs | Submodules | README | LICENSE

gnunet-service-regex.c (10291B)


      1 /*
      2      This file is part of GNUnet.
      3      Copyright (C) 2013, 2026 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      SPDX-License-Identifier: AGPL3.0-or-later
     19  */
     20 
     21 /**
     22  * @file regex/gnunet-service-regex.c
     23  * @brief service to advertise capabilities described as regex and to
     24  *        lookup capabilities by regex
     25  * @author Christian Grothoff
     26  */
     27 #include "gnunet_pils_service.h"
     28 #include "regex_internal_lib.h"
     29 #include "regex_ipc.h"
     30 
     31 #include "gnunet_util_lib.h"
     32 
     33 
     34 /**
     35  * Information about one of our clients.
     36  */
     37 struct ClientEntry
     38 {
     39   /**
     40    * Queue for transmissions to @e client.
     41    */
     42   struct GNUNET_MQ_Handle *mq;
     43 
     44   /**
     45    * Handle identifying the client.
     46    */
     47   struct GNUNET_SERVICE_Client *client;
     48 
     49   /**
     50    * Search handle (if this client is searching).
     51    */
     52   struct REGEX_INTERNAL_Search *sh;
     53 
     54   /**
     55    * Announcement handle (if this client is announcing).
     56    */
     57   struct REGEX_INTERNAL_Announcement *ah;
     58 
     59   /**
     60    * Refresh frequency for announcements.
     61    */
     62   struct GNUNET_TIME_Relative frequency;
     63 
     64   /**
     65    * Task for re-announcing.
     66    */
     67   struct GNUNET_SCHEDULER_Task *refresh_task;
     68 };
     69 
     70 
     71 /**
     72  * Connection to the DHT.
     73  */
     74 static struct GNUNET_DHT_Handle *dht;
     75 
     76 /**
     77  * Handle for doing statistics.
     78  */
     79 static struct GNUNET_STATISTICS_Handle *stats;
     80 
     81 /**
     82  * Handle for pils service.
     83  */
     84 static struct GNUNET_PILS_Handle *pils;
     85 
     86 
     87 /**
     88  * Task run during shutdown.
     89  *
     90  * @param cls unused
     91  */
     92 static void
     93 cleanup_task (void *cls)
     94 {
     95   if (NULL != dht)
     96   {
     97     GNUNET_DHT_disconnect (dht);
     98     dht = NULL;
     99   }
    100   if (NULL != stats)
    101   {
    102     GNUNET_STATISTICS_destroy (stats,
    103                                GNUNET_NO);
    104     stats = NULL;
    105   }
    106   if (NULL != pils)
    107   {
    108     GNUNET_PILS_disconnect (pils);
    109     pils = NULL;
    110   }
    111 }
    112 
    113 
    114 /**
    115  * Periodic task to refresh our announcement of the regex.
    116  *
    117  * @param cls the `struct ClientEntry *` of the client that triggered the
    118  *        announcement
    119  */
    120 static void
    121 reannounce (void *cls)
    122 {
    123   struct ClientEntry *ce = cls;
    124 
    125   REGEX_INTERNAL_reannounce (ce->ah);
    126   ce->refresh_task = GNUNET_SCHEDULER_add_delayed (ce->frequency,
    127                                                    &reannounce,
    128                                                    ce);
    129 }
    130 
    131 
    132 /**
    133  * Check ANNOUNCE message.
    134  *
    135  * @param cls identification of the client
    136  * @param am the actual message
    137  * @return #GNUNET_OK if @a am is well-formed
    138  */
    139 static int
    140 check_announce (void *cls,
    141                 const struct AnnounceMessage *am)
    142 {
    143   struct ClientEntry *ce = cls;
    144 
    145   GNUNET_MQ_check_zero_termination (am);
    146   if (NULL != ce->ah)
    147   {
    148     /* only one announcement per client allowed */
    149     GNUNET_break (0);
    150     return GNUNET_SYSERR;
    151   }
    152   return GNUNET_OK;
    153 }
    154 
    155 
    156 /**
    157  * Handle ANNOUNCE message.
    158  *
    159  * @param cls identification of the client
    160  * @param am the actual message
    161  */
    162 static void
    163 handle_announce (void *cls,
    164                  const struct AnnounceMessage *am)
    165 {
    166   struct ClientEntry *ce = cls;
    167   const char *regex;
    168 
    169   regex = (const char *) &am[1];
    170   ce->frequency = GNUNET_TIME_relative_ntoh (am->refresh_delay);
    171   ce->refresh_task = GNUNET_SCHEDULER_add_delayed (ce->frequency,
    172                                                    &reannounce,
    173                                                    ce);
    174   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    175               "Starting to announce regex `%s' every %s\n",
    176               regex,
    177               GNUNET_STRINGS_relative_time_to_string (ce->frequency,
    178                                                       GNUNET_NO));
    179   ce->ah = REGEX_INTERNAL_announce (dht,
    180                                     pils,
    181                                     regex,
    182                                     ntohs (am->compression),
    183                                     stats);
    184   if (NULL == ce->ah)
    185   {
    186     GNUNET_break (0);
    187     GNUNET_SCHEDULER_cancel (ce->refresh_task);
    188     ce->refresh_task = NULL;
    189     GNUNET_SERVICE_client_drop (ce->client);
    190     return;
    191   }
    192   GNUNET_SERVICE_client_continue (ce->client);
    193 }
    194 
    195 
    196 /**
    197  * Handle result, pass it back to the client.
    198  *
    199  * @param cls the struct ClientEntry of the client searching
    200  * @param id Peer providing a regex that matches the string.
    201  * @param get_path Path of the get request.
    202  * @param get_path_length Length of @a get_path.
    203  * @param put_path Path of the put request.
    204  * @param put_path_length Length of the @a put_path.
    205  */
    206 static void
    207 handle_search_result (void *cls,
    208                       const struct GNUNET_PeerIdentity *id,
    209                       const struct GNUNET_DHT_PathElement *get_path,
    210                       unsigned int get_path_length,
    211                       const struct GNUNET_DHT_PathElement *put_path,
    212                       unsigned int put_path_length)
    213 {
    214   struct ClientEntry *ce = cls;
    215   struct GNUNET_MQ_Envelope *env;
    216   struct ResultMessage *result;
    217   struct GNUNET_PeerIdentity *gp;
    218   uint16_t size;
    219 
    220   if ((get_path_length >= 65536) ||
    221       (put_path_length >= 65536) ||
    222       ( ((get_path_length + put_path_length)
    223          * sizeof(struct GNUNET_PeerIdentity))
    224         + sizeof(struct ResultMessage) >= GNUNET_MAX_MESSAGE_SIZE) )
    225   {
    226     GNUNET_break (0);
    227     return;
    228   }
    229   size = (get_path_length + put_path_length)
    230          * sizeof(struct GNUNET_PeerIdentity);
    231   env = GNUNET_MQ_msg_extra (result,
    232                              size,
    233                              GNUNET_MESSAGE_TYPE_REGEX_RESULT);
    234   result->get_path_length = htons ((uint16_t) get_path_length);
    235   result->put_path_length = htons ((uint16_t) put_path_length);
    236   result->id = *id;
    237   gp = &result->id;
    238   for (unsigned int i = 0; i<get_path_length; i++)
    239     gp[i + 1] = get_path[i].pred;
    240   for (unsigned int i = 0; i<put_path_length; i++)
    241     gp[i + get_path_length + 1] = put_path[i].pred;
    242   GNUNET_MQ_send (ce->mq,
    243                   env);
    244 }
    245 
    246 
    247 /**
    248  * Check SEARCH message.
    249  *
    250  * @param cls identification of the client
    251  * @param sm the actual message
    252  */
    253 static int
    254 check_search (void *cls,
    255               const struct RegexSearchMessage *sm)
    256 {
    257   struct ClientEntry *ce = cls;
    258   const char *string;
    259   uint16_t size;
    260 
    261   size = ntohs (sm->header.size) - sizeof(*sm);
    262   string = (const char *) &sm[1];
    263   if ('\0' != string[size - 1])
    264   {
    265     GNUNET_break (0);
    266     return GNUNET_SYSERR;
    267   }
    268   if (NULL != ce->sh)
    269   {
    270     /* only one search allowed per client */
    271     GNUNET_break (0);
    272     return GNUNET_SYSERR;
    273   }
    274   return GNUNET_OK;
    275 }
    276 
    277 
    278 /**
    279  * Handle SEARCH message.
    280  *
    281  * @param cls identification of the client
    282  * @param sm the actual message
    283  */
    284 static void
    285 handle_search (void *cls,
    286                const struct RegexSearchMessage *sm)
    287 {
    288   struct ClientEntry *ce = cls;
    289   const char *string;
    290 
    291   string = (const char *) &sm[1];
    292   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    293               "Starting to search for `%s'\n",
    294               string);
    295   ce->sh = REGEX_INTERNAL_search (dht,
    296                                   string,
    297                                   &handle_search_result,
    298                                   ce,
    299                                   stats);
    300   if (NULL == ce->sh)
    301   {
    302     GNUNET_break (0);
    303     GNUNET_SERVICE_client_drop (ce->client);
    304     return;
    305   }
    306   GNUNET_SERVICE_client_continue (ce->client);
    307 }
    308 
    309 
    310 /**
    311  * Process regex requests.
    312  *
    313  * @param cls closure
    314  * @param cfg configuration to use
    315  * @param service the initialized service
    316  */
    317 static void
    318 run (void *cls,
    319      const struct GNUNET_CONFIGURATION_Handle *cfg,
    320      struct GNUNET_SERVICE_Handle *service)
    321 {
    322   pils = GNUNET_PILS_connect (cfg, NULL, NULL);
    323   if (NULL == pils)
    324   {
    325     GNUNET_SCHEDULER_shutdown ();
    326     return;
    327   }
    328   dht = GNUNET_DHT_connect (cfg, 1024);
    329   if (NULL == dht)
    330   {
    331     GNUNET_PILS_disconnect (pils);
    332     pils = NULL;
    333     GNUNET_SCHEDULER_shutdown ();
    334     return;
    335   }
    336   GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
    337                                  NULL);
    338   stats = GNUNET_STATISTICS_create ("regex", cfg);
    339 }
    340 
    341 
    342 /**
    343  * Callback called when a client connects to the service.
    344  *
    345  * @param cls closure for the service
    346  * @param c the new client that connected to the service
    347  * @param mq the message queue used to send messages to the client
    348  * @return @a c
    349  */
    350 static void *
    351 client_connect_cb (void *cls,
    352                    struct GNUNET_SERVICE_Client *c,
    353                    struct GNUNET_MQ_Handle *mq)
    354 {
    355   struct ClientEntry *ce;
    356 
    357   ce = GNUNET_new (struct ClientEntry);
    358   ce->client = c;
    359   ce->mq = mq;
    360   return ce;
    361 }
    362 
    363 
    364 /**
    365  * Callback called when a client disconnected from the service
    366  *
    367  * @param cls closure for the service
    368  * @param c the client that disconnected
    369  * @param internal_cls should be equal to @a c
    370  */
    371 static void
    372 client_disconnect_cb (void *cls,
    373                       struct GNUNET_SERVICE_Client *c,
    374                       void *internal_cls)
    375 {
    376   struct ClientEntry *ce = internal_cls;
    377 
    378   if (NULL != ce->refresh_task)
    379   {
    380     GNUNET_SCHEDULER_cancel (ce->refresh_task);
    381     ce->refresh_task = NULL;
    382   }
    383   if (NULL != ce->ah)
    384   {
    385     REGEX_INTERNAL_announce_cancel (ce->ah);
    386     ce->ah = NULL;
    387   }
    388   if (NULL != ce->sh)
    389   {
    390     REGEX_INTERNAL_search_cancel (ce->sh);
    391     ce->sh = NULL;
    392   }
    393   GNUNET_free (ce);
    394 }
    395 
    396 
    397 /**
    398  * Define "main" method using service macro.
    399  */
    400 GNUNET_SERVICE_MAIN
    401   (GNUNET_OS_project_data_gnunet (),
    402   "regex",
    403   GNUNET_SERVICE_OPTION_NONE,
    404   &run,
    405   &client_connect_cb,
    406   &client_disconnect_cb,
    407   NULL,
    408   GNUNET_MQ_hd_var_size (announce,
    409                          GNUNET_MESSAGE_TYPE_REGEX_ANNOUNCE,
    410                          struct AnnounceMessage,
    411                          NULL),
    412   GNUNET_MQ_hd_var_size (search,
    413                          GNUNET_MESSAGE_TYPE_REGEX_SEARCH,
    414                          struct RegexSearchMessage,
    415                          NULL),
    416   GNUNET_MQ_handler_end ());
    417 
    418 
    419 /* end of gnunet-service-regex.c */