ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
strings.c File Reference

Session string generation and validation implementation. More...

Go to the source code of this file.

Data Structures

struct  word_cache_entry_t
 Hashtable entry for cached word lookups. More...
 

Macros

#define PCRE2_CODE_UNIT_WIDTH   8
 

Functions

void acds_strings_destroy (void)
 Cleanup function for session string cache Called by asciichat_shared_destroy() during library cleanup. Safe to call multiple times (idempotent).
 
asciichat_error_t acds_string_init (void)
 Initialize session string system with cached word validation.
 
asciichat_error_t acds_string_generate (char *output, size_t output_size)
 Generate random session string.
 
bool is_session_string (const char *str)
 Check if a string is a valid session string.
 

Detailed Description

Session string generation and validation implementation.

This module generates memorable session strings in the format: adjective-noun-noun (e.g., "swift-river-mountain")

It maintains cached hashtables of adjectives and nouns for fast O(1) validation, ensuring session strings only contain real words.

Definition in file discovery/strings.c.

Macro Definition Documentation

◆ PCRE2_CODE_UNIT_WIDTH

#define PCRE2_CODE_UNIT_WIDTH   8

Definition at line 21 of file discovery/strings.c.

Function Documentation

◆ acds_string_generate()

asciichat_error_t acds_string_generate ( char *  output,
size_t  output_size 
)

Generate random session string.

Parameters
outputOutput buffer for session string
output_sizeSize of output buffer (should be at least SESSION_STRING_BUFFER_SIZE bytes)
Returns
ASCIICHAT_OK on success, error code otherwise

Generates strings in format: adjective-noun-noun Example: "affectionate-acquaintance-acquaintance" (38 characters max)

Use acds_string_get_max_length() to get the maximum possible length. Entropy: 2500 adjectives * 5000 nouns * 5000 nouns = 62.5 billion combinations

Definition at line 222 of file discovery/strings.c.

222 {
223 if (!output || output_size < SESSION_STRING_BUFFER_SIZE) {
224 return SET_ERRNO(ERROR_INVALID_PARAM, "output buffer must be at least %zu bytes",
226 }
227
228 // libsodium is guaranteed to be initialized by acds_string_init() before this is called
229 // No need to call sodium_init() again (redundant initialization removed)
230
231 // Pick random adjective
232 uint32_t adj_idx = randombytes_uniform((uint32_t)adjectives_count);
233 const char *adj = adjectives[adj_idx];
234
235 // Pick two random nouns
236 uint32_t noun1_idx = randombytes_uniform((uint32_t)nouns_count);
237 uint32_t noun2_idx = randombytes_uniform((uint32_t)nouns_count);
238 const char *noun1 = nouns[noun1_idx];
239 const char *noun2 = nouns[noun2_idx];
240
241 // Format: adjective-noun-noun
242 int written = safe_snprintf(output, output_size, "%s-%s-%s", adj, noun1, noun2);
243 if (written < 0 || (size_t)written >= output_size) {
244 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Session string too long for buffer");
245 }
246
247 log_dev("Generated session string: %s", output);
248 return ASCIICHAT_OK;
249}
const char * adjectives[]
Array of English adjectives for ACDS session naming.
Definition adjectives.c:51
const size_t adjectives_count
Count of adjectives in the adjectives array.
Definition adjectives.c:411
unsigned int uint32_t
Definition common.h:58
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
@ ERROR_BUFFER_OVERFLOW
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148
const char * nouns[]
Array of English nouns for session and client naming.
Definition nouns.c:55
const size_t nouns_count
Count of nouns in the nouns array.
Definition nouns.c:772
#define SESSION_STRING_BUFFER_SIZE

References adjectives, adjectives_count, ASCIICHAT_OK, ERROR_BUFFER_OVERFLOW, ERROR_INVALID_PARAM, log_dev, nouns, nouns_count, safe_snprintf(), SESSION_STRING_BUFFER_SIZE, and SET_ERRNO.

Referenced by database_session_create(), and options_preset_unified().

◆ acds_string_init()

asciichat_error_t acds_string_init ( void  )

Initialize session string system with cached word validation.

Returns
ASCIICHAT_OK on success, error code otherwise

Initializes the random number generator and caches adjectives/nouns in a hashtable for fast validation. Must be called before acds_string_generate() and before any is_session_string() validation calls.

Uses libsodium's randombytes for cryptographically secure randomness. Cleans up cached data on program exit via atexit() handler.

Definition at line 218 of file discovery/strings.c.

218 {
219 return ASCIICHAT_OK;
220}

References ASCIICHAT_OK.

◆ acds_strings_destroy()

void acds_strings_destroy ( void  )

Cleanup function for session string cache Called by asciichat_shared_destroy() during library cleanup. Safe to call multiple times (idempotent).

Definition at line 53 of file discovery/strings.c.

53 {
54 // Shutdown if initialized
55 if (!lifecycle_shutdown(&g_cache_lc)) {
56 return; // Not initialized
57 }
58
59 // Cleanup adjectives cache
60 word_cache_entry_t *adj_entry, *adj_tmp;
61 HASH_ITER(hh, g_adjectives_cache, adj_entry, adj_tmp) {
62 HASH_DEL(g_adjectives_cache, adj_entry);
63 SAFE_FREE(adj_entry->word);
64 SAFE_FREE(adj_entry);
65 }
66 g_adjectives_cache = NULL;
67
68 // Cleanup nouns cache
69 word_cache_entry_t *noun_entry, *noun_tmp;
70 HASH_ITER(hh, g_nouns_cache, noun_entry, noun_tmp) {
71 HASH_DEL(g_nouns_cache, noun_entry);
72 SAFE_FREE(noun_entry->word);
73 SAFE_FREE(noun_entry);
74 }
75 g_nouns_cache = NULL;
76
77 log_dev("Session string word cache cleaned up");
78}
#define SAFE_FREE(ptr)
Definition common.h:376
bool lifecycle_shutdown(lifecycle_t *lc)
Definition lifecycle.c:108
Hashtable entry for cached word lookups.
char * word
The word string (key)

References lifecycle_shutdown(), log_dev, SAFE_FREE, and word_cache_entry_t::word.

Referenced by asciichat_shared_destroy().

◆ is_session_string()

bool is_session_string ( const char *  str)

Check if a string is a valid session string.

Parameters
strString to validate
Returns
true if valid session string (adjective-noun-noun format), false otherwise

A valid session string must:

  • Be in format: adjective-noun-noun
  • Contain exactly 3 words separated by exactly 2 hyphens
  • Have each word be a real adjective or noun from cached wordlists
  • Contain only lowercase ASCII letters and hyphens
  • Be 5-47 characters long
  • Not start or end with hyphen
  • Not contain consecutive hyphens

This function validates against the cached wordlists populated by acds_string_init(). Non-session inputs return false without setting an error. Internal failures set errno.

Definition at line 251 of file discovery/strings.c.

251 {
252 if (!str || str[0] == '\0') {
253 return false;
254 }
255
256 size_t len = strlen(str);
257
258 // Check length bounds (min 4, max defined by SESSION_STRING_MAX_LEN)
259 if (len < 4 || len > SESSION_STRING_MAX_LEN) {
260 return false;
261 }
262
263 // Session strings must be ASCII-only (homograph attack prevention)
264 if (!utf8_is_ascii_only(str)) {
265 return false;
266 }
267
268 // Get compiled regex (lazy initialization)
269 pcre2_code *regex = session_format_regex_get();
270 if (!regex) {
271 SET_ERRNO(ERROR_INTERNAL, "Session string PCRE2 regex failed to initialize");
272 return false;
273 }
274
275 // Validate format using PCRE2 regex
276 pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL);
277 if (!match_data) {
278 log_error("Failed to allocate match data for session string regex");
279 SET_ERRNO(ERROR_MEMORY, "Failed to allocate match data");
280 return false;
281 }
282
283 int match_result = pcre2_jit_match(regex, (PCRE2_SPTR8)str, len, 0, 0, match_data, NULL);
284
285 pcre2_match_data_free(match_data);
286
287 if (match_result < 0) {
288 if (match_result != PCRE2_ERROR_NOMATCH) {
289 SET_ERRNO(ERROR_INTERNAL, "Session string PCRE2 matching failed: %d", match_result);
290 }
291 return false;
292 }
293
294 // Extract the three words by finding hyphens
295 // Format is guaranteed to be: word-word-word where each word is 2-12 lowercase letters
296 const char *hyphen1 = strchr(str, '-');
297 if (!hyphen1) {
298 return false;
299 }
300
301 const char *hyphen2 = strchr(hyphen1 + 1, '-');
302 if (!hyphen2) {
303 return false;
304 }
305
306 // Extract three words
307 size_t adj_len = hyphen1 - str;
308 size_t noun1_len = hyphen2 - hyphen1 - 1;
309 size_t noun2_len = len - (hyphen2 - str) - 1;
310
311 if (adj_len >= 32 || noun1_len >= 32 || noun2_len >= 32) {
312 return false;
313 }
314
315 char adj[32], noun1[32], noun2[32];
316 memcpy(adj, str, adj_len);
317 adj[adj_len] = '\0';
318 memcpy(noun1, hyphen1 + 1, noun1_len);
319 noun1[noun1_len] = '\0';
320 memcpy(noun2, hyphen2 + 1, noun2_len);
321 noun2[noun2_len] = '\0';
322
323 // Lazy initialization: build validation caches on first use
324 // call_once ensures thread-safe exactly-once execution
325 build_validation_caches();
326 // If initialization fails silently (logs warning), hashtables will be empty
327 // and validation will fail, which is safe behavior
328
329 // Validate first word is an adjective
330 word_cache_entry_t *adj_entry = NULL;
331 HASH_FIND_STR(g_adjectives_cache, adj, adj_entry);
332 if (!adj_entry) {
333 return false;
334 }
335
336 // Validate second and third words are nouns
337 word_cache_entry_t *noun_entry1 = NULL;
338 HASH_FIND_STR(g_nouns_cache, noun1, noun_entry1);
339 if (!noun_entry1) {
340 return false;
341 }
342
343 word_cache_entry_t *noun_entry2 = NULL;
344 HASH_FIND_STR(g_nouns_cache, noun2, noun_entry2);
345 if (!noun_entry2) {
346 return false;
347 }
348
349 log_dev("Valid session string: %s", str);
350 return true;
351}
#define SESSION_STRING_MAX_LEN
Maximum length of a session string (without null terminator)
@ ERROR_MEMORY
Definition error_codes.h:56
@ ERROR_INTERNAL
Definition error_codes.h:92
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
bool utf8_is_ascii_only(const char *str)
Check if a string contains only ASCII characters.
Definition utf8.c:165

References ERROR_INTERNAL, ERROR_MEMORY, log_dev, log_error, SESSION_STRING_MAX_LEN, SET_ERRNO, and utf8_is_ascii_only().

Referenced by database_session_create(), discover_session_parallel(), and parse_client_address().