aboutsummaryrefslogtreecommitdiff
path: root/src/namecache/plugin_namecache_flat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/namecache/plugin_namecache_flat.c')
-rw-r--r--src/namecache/plugin_namecache_flat.c414
1 files changed, 208 insertions, 206 deletions
diff --git a/src/namecache/plugin_namecache_flat.c b/src/namecache/plugin_namecache_flat.c
index c48056a29..14406c13d 100644
--- a/src/namecache/plugin_namecache_flat.c
+++ b/src/namecache/plugin_namecache_flat.c
@@ -33,7 +33,8 @@
33/** 33/**
34 * Context for all functions in this plugin. 34 * Context for all functions in this plugin.
35 */ 35 */
36struct Plugin { 36struct Plugin
37{
37 const struct GNUNET_CONFIGURATION_Handle *cfg; 38 const struct GNUNET_CONFIGURATION_Handle *cfg;
38 39
39 /** 40 /**
@@ -47,7 +48,8 @@ struct Plugin {
47 struct GNUNET_CONTAINER_MultiHashMap *hm; 48 struct GNUNET_CONTAINER_MultiHashMap *hm;
48}; 49};
49 50
50struct FlatFileEntry { 51struct FlatFileEntry
52{
51 /** 53 /**
52 * Block 54 * Block
53 */ 55 */
@@ -68,121 +70,121 @@ struct FlatFileEntry {
68 * @return #GNUNET_OK on success 70 * @return #GNUNET_OK on success
69 */ 71 */
70static int 72static int
71database_setup(struct Plugin *plugin) 73database_setup (struct Plugin *plugin)
72{ 74{
73 char *afsdir; 75 char *afsdir;
74 char* block_buffer; 76 char*block_buffer;
75 char* buffer; 77 char*buffer;
76 char* line; 78 char*line;
77 char* query; 79 char*query;
78 char* block; 80 char*block;
79 size_t size; 81 size_t size;
80 struct FlatFileEntry *entry; 82 struct FlatFileEntry *entry;
81 struct GNUNET_DISK_FileHandle *fh; 83 struct GNUNET_DISK_FileHandle *fh;
82 84
83 if (GNUNET_OK != 85 if (GNUNET_OK !=
84 GNUNET_CONFIGURATION_get_value_filename(plugin->cfg, 86 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
85 "namecache-flat", 87 "namecache-flat",
86 "FILENAME", 88 "FILENAME",
87 &afsdir)) 89 &afsdir))
90 {
91 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
92 "namecache-flat", "FILENAME");
93 return GNUNET_SYSERR;
94 }
95 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
96 {
97 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
88 { 98 {
89 GNUNET_log_config_missing(GNUNET_ERROR_TYPE_ERROR, 99 GNUNET_break (0);
90 "namecache-flat", "FILENAME"); 100 GNUNET_free (afsdir);
91 return GNUNET_SYSERR; 101 return GNUNET_SYSERR;
92 } 102 }
93 if (GNUNET_OK != GNUNET_DISK_file_test(afsdir)) 103 }
94 {
95 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file(afsdir))
96 {
97 GNUNET_break(0);
98 GNUNET_free(afsdir);
99 return GNUNET_SYSERR;
100 }
101 }
102 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */ 104 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
103 plugin->fn = afsdir; 105 plugin->fn = afsdir;
104 106
105 /* Load data from file into hashmap */ 107 /* Load data from file into hashmap */
106 plugin->hm = GNUNET_CONTAINER_multihashmap_create(10, 108 plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
107 GNUNET_NO); 109 GNUNET_NO);
108 fh = GNUNET_DISK_file_open(afsdir, 110 fh = GNUNET_DISK_file_open (afsdir,
109 GNUNET_DISK_OPEN_CREATE | 111 GNUNET_DISK_OPEN_CREATE
110 GNUNET_DISK_OPEN_READWRITE, 112 | GNUNET_DISK_OPEN_READWRITE,
111 GNUNET_DISK_PERM_USER_WRITE | 113 GNUNET_DISK_PERM_USER_WRITE
112 GNUNET_DISK_PERM_USER_READ); 114 | GNUNET_DISK_PERM_USER_READ);
113 if (NULL == fh) 115 if (NULL == fh)
114 { 116 {
115 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, 117 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
116 _("Unable to initialize file: %s.\n"), 118 _ ("Unable to initialize file: %s.\n"),
117 afsdir); 119 afsdir);
118 return GNUNET_SYSERR; 120 return GNUNET_SYSERR;
119 } 121 }
120 122
121 if (GNUNET_SYSERR == GNUNET_DISK_file_size(afsdir, 123 if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir,
122 &size, 124 &size,
123 GNUNET_YES, 125 GNUNET_YES,
124 GNUNET_YES)) 126 GNUNET_YES))
125 { 127 {
126 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, 128 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
127 _("Unable to get filesize: %s.\n"), 129 _ ("Unable to get filesize: %s.\n"),
128 afsdir); 130 afsdir);
129 GNUNET_DISK_file_close(fh); 131 GNUNET_DISK_file_close (fh);
130 return GNUNET_SYSERR; 132 return GNUNET_SYSERR;
131 } 133 }
132 134
133 if (0 == size) 135 if (0 == size)
134 { 136 {
135 GNUNET_DISK_file_close(fh); 137 GNUNET_DISK_file_close (fh);
136 return GNUNET_OK; 138 return GNUNET_OK;
137 } 139 }
138 140
139 buffer = GNUNET_malloc(size + 1); 141 buffer = GNUNET_malloc (size + 1);
140 142
141 if (GNUNET_SYSERR == GNUNET_DISK_file_read(fh, 143 if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh,
142 buffer, 144 buffer,
143 size)) 145 size))
144 { 146 {
145 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, 147 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
146 _("Unable to read file: %s.\n"), 148 _ ("Unable to read file: %s.\n"),
147 afsdir); 149 afsdir);
148 GNUNET_free(buffer); 150 GNUNET_free (buffer);
149 GNUNET_DISK_file_close(fh); 151 GNUNET_DISK_file_close (fh);
150 return GNUNET_SYSERR; 152 return GNUNET_SYSERR;
151 } 153 }
152 buffer[size] = '\0'; 154 buffer[size] = '\0';
153 155
154 GNUNET_DISK_file_close(fh); 156 GNUNET_DISK_file_close (fh);
155 if (0 < size) 157 if (0 < size)
158 {
159 line = strtok (buffer, "\n");
160 while (line != NULL)
156 { 161 {
157 line = strtok(buffer, "\n"); 162 query = strtok (line, ",");
158 while (line != NULL) 163 if (NULL == query)
159 { 164 break;
160 query = strtok(line, ","); 165 block = strtok (NULL, ",");
161 if (NULL == query) 166 if (NULL == block)
162 break; 167 break;
163 block = strtok(NULL, ","); 168 line = strtok (NULL, "\n");
164 if (NULL == block) 169 entry = GNUNET_malloc (sizeof(struct FlatFileEntry));
165 break; 170 GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_hash_from_string (query,
166 line = strtok(NULL, "\n"); 171 &entry->query));
167 entry = GNUNET_malloc(sizeof(struct FlatFileEntry)); 172 GNUNET_STRINGS_base64_decode (block,
168 GNUNET_assert(GNUNET_OK == GNUNET_CRYPTO_hash_from_string(query, 173 strlen (block),
169 &entry->query)); 174 (void**) &block_buffer);
170 GNUNET_STRINGS_base64_decode(block, 175 entry->block = (struct GNUNET_GNSRECORD_Block *) block_buffer;
171 strlen(block), 176 if (GNUNET_OK !=
172 (void**)&block_buffer); 177 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
173 entry->block = (struct GNUNET_GNSRECORD_Block *)block_buffer; 178 &entry->query,
174 if (GNUNET_OK != 179 entry,
175 GNUNET_CONTAINER_multihashmap_put(plugin->hm, 180 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
176 &entry->query, 181 {
177 entry, 182 GNUNET_free (entry);
178 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) 183 GNUNET_break (0);
179 { 184 }
180 GNUNET_free(entry);
181 GNUNET_break(0);
182 }
183 }
184 } 185 }
185 GNUNET_free(buffer); 186 }
187 GNUNET_free (buffer);
186 return GNUNET_OK; 188 return GNUNET_OK;
187} 189}
188 190
@@ -192,9 +194,9 @@ database_setup(struct Plugin *plugin)
192 * @param plugin the plugin context 194 * @param plugin the plugin context
193 */ 195 */
194static int 196static int
195store_and_free_entries(void *cls, 197store_and_free_entries (void *cls,
196 const struct GNUNET_HashCode *key, 198 const struct GNUNET_HashCode *key,
197 void *value) 199 void *value)
198{ 200{
199 struct GNUNET_DISK_FileHandle *fh = cls; 201 struct GNUNET_DISK_FileHandle *fh = cls;
200 struct FlatFileEntry *entry = value; 202 struct FlatFileEntry *entry = value;
@@ -204,29 +206,29 @@ store_and_free_entries(void *cls,
204 struct GNUNET_CRYPTO_HashAsciiEncoded query; 206 struct GNUNET_CRYPTO_HashAsciiEncoded query;
205 size_t block_size; 207 size_t block_size;
206 208
207 block_size = ntohl(entry->block->purpose.size) + 209 block_size = ntohl (entry->block->purpose.size)
208 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey) + 210 + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)
209 sizeof(struct GNUNET_CRYPTO_EcdsaSignature); 211 + sizeof(struct GNUNET_CRYPTO_EcdsaSignature);
210 212
211 GNUNET_STRINGS_base64_encode((char*)entry->block, 213 GNUNET_STRINGS_base64_encode ((char*) entry->block,
212 block_size, 214 block_size,
213 &block_b64); 215 &block_b64);
214 GNUNET_CRYPTO_hash_to_enc(&entry->query, 216 GNUNET_CRYPTO_hash_to_enc (&entry->query,
215 &query); 217 &query);
216 GNUNET_asprintf(&line, 218 GNUNET_asprintf (&line,
217 "%s,%s\n", 219 "%s,%s\n",
218 (char*)&query, 220 (char*) &query,
219 block_b64); 221 block_b64);
220 222
221 GNUNET_free(block_b64); 223 GNUNET_free (block_b64);
222 224
223 GNUNET_DISK_file_write(fh, 225 GNUNET_DISK_file_write (fh,
224 line, 226 line,
225 strlen(line)); 227 strlen (line));
226 228
227 GNUNET_free(entry->block); 229 GNUNET_free (entry->block);
228 GNUNET_free(entry); 230 GNUNET_free (entry);
229 GNUNET_free(line); 231 GNUNET_free (line);
230 return GNUNET_YES; 232 return GNUNET_YES;
231} 233}
232 234
@@ -236,49 +238,49 @@ store_and_free_entries(void *cls,
236 * @param plugin the plugin context (state for this module) 238 * @param plugin the plugin context (state for this module)
237 */ 239 */
238static void 240static void
239database_shutdown(struct Plugin *plugin) 241database_shutdown (struct Plugin *plugin)
240{ 242{
241 struct GNUNET_DISK_FileHandle *fh; 243 struct GNUNET_DISK_FileHandle *fh;
242 244
243 fh = GNUNET_DISK_file_open(plugin->fn, 245 fh = GNUNET_DISK_file_open (plugin->fn,
244 GNUNET_DISK_OPEN_CREATE | 246 GNUNET_DISK_OPEN_CREATE
245 GNUNET_DISK_OPEN_TRUNCATE | 247 | GNUNET_DISK_OPEN_TRUNCATE
246 GNUNET_DISK_OPEN_READWRITE, 248 | GNUNET_DISK_OPEN_READWRITE,
247 GNUNET_DISK_PERM_USER_WRITE | 249 GNUNET_DISK_PERM_USER_WRITE
248 GNUNET_DISK_PERM_USER_READ); 250 | GNUNET_DISK_PERM_USER_READ);
249 if (NULL == fh) 251 if (NULL == fh)
250 { 252 {
251 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, 253 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
252 _("Unable to initialize file: %s.\n"), 254 _ ("Unable to initialize file: %s.\n"),
253 plugin->fn); 255 plugin->fn);
254 return; 256 return;
255 } 257 }
256 258
257 GNUNET_CONTAINER_multihashmap_iterate(plugin->hm, 259 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
258 &store_and_free_entries, 260 &store_and_free_entries,
259 fh); 261 fh);
260 GNUNET_CONTAINER_multihashmap_destroy(plugin->hm); 262 GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
261 GNUNET_DISK_file_close(fh); 263 GNUNET_DISK_file_close (fh);
262} 264}
263 265
264static int 266static int
265expire_blocks(void *cls, 267expire_blocks (void *cls,
266 const struct GNUNET_HashCode *key, 268 const struct GNUNET_HashCode *key,
267 void *value) 269 void *value)
268{ 270{
269 struct Plugin *plugin = cls; 271 struct Plugin *plugin = cls;
270 struct FlatFileEntry *entry = value; 272 struct FlatFileEntry *entry = value;
271 struct GNUNET_TIME_Absolute now; 273 struct GNUNET_TIME_Absolute now;
272 struct GNUNET_TIME_Absolute expiration; 274 struct GNUNET_TIME_Absolute expiration;
273 275
274 now = GNUNET_TIME_absolute_get(); 276 now = GNUNET_TIME_absolute_get ();
275 expiration = GNUNET_TIME_absolute_ntoh(entry->block->expiration_time); 277 expiration = GNUNET_TIME_absolute_ntoh (entry->block->expiration_time);
276 278
277 if (0 == GNUNET_TIME_absolute_get_difference(now, 279 if (0 == GNUNET_TIME_absolute_get_difference (now,
278 expiration).rel_value_us) 280 expiration).rel_value_us)
279 { 281 {
280 GNUNET_CONTAINER_multihashmap_remove_all(plugin->hm, key); 282 GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, key);
281 } 283 }
282 return GNUNET_YES; 284 return GNUNET_YES;
283} 285}
284 286
@@ -290,11 +292,11 @@ expire_blocks(void *cls,
290 * @param plugin the plugin 292 * @param plugin the plugin
291 */ 293 */
292static void 294static void
293namecache_expire_blocks(struct Plugin *plugin) 295namecache_expire_blocks (struct Plugin *plugin)
294{ 296{
295 GNUNET_CONTAINER_multihashmap_iterate(plugin->hm, 297 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
296 &expire_blocks, 298 &expire_blocks,
297 plugin); 299 plugin);
298} 300}
299 301
300 302
@@ -306,43 +308,43 @@ namecache_expire_blocks(struct Plugin *plugin)
306 * @return #GNUNET_OK on success, else #GNUNET_SYSERR 308 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
307 */ 309 */
308static int 310static int
309namecache_cache_block(void *cls, 311namecache_cache_block (void *cls,
310 const struct GNUNET_GNSRECORD_Block *block) 312 const struct GNUNET_GNSRECORD_Block *block)
311{ 313{
312 struct Plugin *plugin = cls; 314 struct Plugin *plugin = cls;
313 struct GNUNET_HashCode query; 315 struct GNUNET_HashCode query;
314 struct FlatFileEntry *entry; 316 struct FlatFileEntry *entry;
315 size_t block_size; 317 size_t block_size;
316 318
317 namecache_expire_blocks(plugin); 319 namecache_expire_blocks (plugin);
318 GNUNET_CRYPTO_hash(&block->derived_key, 320 GNUNET_CRYPTO_hash (&block->derived_key,
319 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey), 321 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
320 &query); 322 &query);
321 block_size = ntohl(block->purpose.size) + 323 block_size = ntohl (block->purpose.size)
322 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey) + 324 + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)
323 sizeof(struct GNUNET_CRYPTO_EcdsaSignature); 325 + sizeof(struct GNUNET_CRYPTO_EcdsaSignature);
324 if (block_size > 64 * 65536) 326 if (block_size > 64 * 65536)
325 { 327 {
326 GNUNET_break(0); 328 GNUNET_break (0);
327 return GNUNET_SYSERR; 329 return GNUNET_SYSERR;
328 } 330 }
329 entry = GNUNET_malloc(sizeof(struct FlatFileEntry)); 331 entry = GNUNET_malloc (sizeof(struct FlatFileEntry));
330 entry->block = GNUNET_malloc(block_size); 332 entry->block = GNUNET_malloc (block_size);
331 GNUNET_memcpy(entry->block, block, block_size); 333 GNUNET_memcpy (entry->block, block, block_size);
332 GNUNET_CONTAINER_multihashmap_remove_all(plugin->hm, &query); 334 GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, &query);
333 if (GNUNET_OK != 335 if (GNUNET_OK !=
334 GNUNET_CONTAINER_multihashmap_put(plugin->hm, 336 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
335 &query, 337 &query,
336 entry, 338 entry,
337 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) 339 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
338 { 340 {
339 GNUNET_free(entry); 341 GNUNET_free (entry);
340 GNUNET_break(0); 342 GNUNET_break (0);
341 return GNUNET_SYSERR; 343 return GNUNET_SYSERR;
342 } 344 }
343 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, 345 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
344 "Caching block under derived key `%s'\n", 346 "Caching block under derived key `%s'\n",
345 GNUNET_h2s_full(&query)); 347 GNUNET_h2s_full (&query));
346 return GNUNET_OK; 348 return GNUNET_OK;
347} 349}
348 350
@@ -358,20 +360,20 @@ namecache_cache_block(void *cls,
358 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error 360 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
359 */ 361 */
360static int 362static int
361namecache_lookup_block(void *cls, 363namecache_lookup_block (void *cls,
362 const struct GNUNET_HashCode *query, 364 const struct GNUNET_HashCode *query,
363 GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls) 365 GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
364{ 366{
365 struct Plugin *plugin = cls; 367 struct Plugin *plugin = cls;
366 const struct GNUNET_GNSRECORD_Block *block; 368 const struct GNUNET_GNSRECORD_Block *block;
367 369
368 block = GNUNET_CONTAINER_multihashmap_get(plugin->hm, query); 370 block = GNUNET_CONTAINER_multihashmap_get (plugin->hm, query);
369 if (NULL == block) 371 if (NULL == block)
370 return GNUNET_NO; 372 return GNUNET_NO;
371 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, 373 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
372 "Found block under derived key `%s'\n", 374 "Found block under derived key `%s'\n",
373 GNUNET_h2s_full(query)); 375 GNUNET_h2s_full (query));
374 iter(iter_cls, block); 376 iter (iter_cls, block);
375 return GNUNET_YES; 377 return GNUNET_YES;
376} 378}
377 379
@@ -383,7 +385,7 @@ namecache_lookup_block(void *cls,
383 * @return NULL on error, otherwise the plugin context 385 * @return NULL on error, otherwise the plugin context
384 */ 386 */
385void * 387void *
386libgnunet_plugin_namecache_flat_init(void *cls) 388libgnunet_plugin_namecache_flat_init (void *cls)
387{ 389{
388 static struct Plugin plugin; 390 static struct Plugin plugin;
389 const struct GNUNET_CONFIGURATION_Handle *cfg = cls; 391 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
@@ -391,19 +393,19 @@ libgnunet_plugin_namecache_flat_init(void *cls)
391 393
392 if (NULL != plugin.cfg) 394 if (NULL != plugin.cfg)
393 return NULL; /* can only initialize once! */ 395 return NULL; /* can only initialize once! */
394 memset(&plugin, 0, sizeof(struct Plugin)); 396 memset (&plugin, 0, sizeof(struct Plugin));
395 plugin.cfg = cfg; 397 plugin.cfg = cfg;
396 if (GNUNET_OK != database_setup(&plugin)) 398 if (GNUNET_OK != database_setup (&plugin))
397 { 399 {
398 database_shutdown(&plugin); 400 database_shutdown (&plugin);
399 return NULL; 401 return NULL;
400 } 402 }
401 api = GNUNET_new(struct GNUNET_NAMECACHE_PluginFunctions); 403 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
402 api->cls = &plugin; 404 api->cls = &plugin;
403 api->cache_block = &namecache_cache_block; 405 api->cache_block = &namecache_cache_block;
404 api->lookup_block = &namecache_lookup_block; 406 api->lookup_block = &namecache_lookup_block;
405 GNUNET_log(GNUNET_ERROR_TYPE_INFO, 407 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
406 _("flat plugin running\n")); 408 _ ("flat plugin running\n"));
407 return api; 409 return api;
408} 410}
409 411
@@ -415,16 +417,16 @@ libgnunet_plugin_namecache_flat_init(void *cls)
415 * @return always NULL 417 * @return always NULL
416 */ 418 */
417void * 419void *
418libgnunet_plugin_namecache_flat_done(void *cls) 420libgnunet_plugin_namecache_flat_done (void *cls)
419{ 421{
420 struct GNUNET_NAMECACHE_PluginFunctions *api = cls; 422 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
421 struct Plugin *plugin = api->cls; 423 struct Plugin *plugin = api->cls;
422 424
423 database_shutdown(plugin); 425 database_shutdown (plugin);
424 plugin->cfg = NULL; 426 plugin->cfg = NULL;
425 GNUNET_free(api); 427 GNUNET_free (api);
426 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, 428 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
427 "flat plugin is finished\n"); 429 "flat plugin is finished\n");
428 return NULL; 430 return NULL;
429} 431}
430 432