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 */