Parse comma-separated STUN server URLs into stun_server_t array.
Parses a comma-separated string of STUN server URLs into stun_server_t structs. If the input string is empty or NULL, uses the provided default string.
51 {
52 if (!out_servers || max_count <= 0) {
53 log_warn("stun_servers_parse: Invalid output array or max_count");
54 return -1;
55 }
56
57
58 const char *servers_to_parse = csv_servers;
59 if (!servers_to_parse || servers_to_parse[0] == '\0') {
60 servers_to_parse = default_csv;
61 }
62
63 if (!servers_to_parse || servers_to_parse[0] == '\0') {
64 log_warn("stun_servers_parse: No servers to parse and no defaults provided");
65 return 0;
66 }
67
68
69 pcre2_code *regex = stun_entry_regex_get();
70
71
72 if (!regex) {
73 log_warn("stun_servers_parse: PCRE2 regex not available, falling back to manual parsing");
74
75 int count = 0;
76 const char *current = servers_to_parse;
77 const char *end;
78
79 while (count < max_count && *current != '\0') {
80 while (isspace(*current)) {
81 current++;
82 }
83 if (*current == '\0')
84 break;
85
86 end = strchr(current, ',');
87 if (!end) {
88 end = current + strlen(current);
89 }
90
91 while (end > current && isspace(*(end - 1))) {
92 end--;
93 }
94
95 size_t len = end - current;
96 if (len == 0) {
97 current = *end ? end + 1 : end;
98 continue;
99 }
100
101 if (len >= STUN_MAX_URL_LEN) {
102 log_warn("stun_servers_parse: STUN server URL too long (max %d): %.*s", STUN_MAX_URL_LEN, (int)len, current);
103 return -1;
104 }
105
106 out_servers[count].host_len = (uint8_t)len;
107 memcpy(out_servers[count].host, current, len);
108 out_servers[count].host[len] = '\0';
109 count++;
110
111 current = *end ? end + 1 : end;
112 }
113 return count;
114 }
115
116
117 int count = 0;
118 pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL);
119 if (!match_data) {
120 log_warn("stun_servers_parse: Failed to allocate PCRE2 match data");
121 return -1;
122 }
123
124 size_t offset = 0;
125 int rc;
126
127 while (count < max_count) {
128 rc = pcre2_jit_match(regex, (PCRE2_SPTR8)servers_to_parse, strlen(servers_to_parse), offset, 0, match_data, NULL);
129
130 if (rc < 0) {
131
132 break;
133 }
134
135
136 size_t len;
138
139 if (!server_url) {
140 break;
141 }
142
143 if (len >= STUN_MAX_URL_LEN) {
144 log_warn("stun_servers_parse: STUN server URL too long (max %d): %.*s", STUN_MAX_URL_LEN, (int)len, server_url);
145 pcre2_match_data_free(match_data);
146 return -1;
147 }
148
149
150 out_servers[count].host_len = (uint8_t)len;
151 memcpy(out_servers[count].host, server_url, len);
152 out_servers[count].host[len] = '\0';
153
154 count++;
155
156
157 PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
158 offset = ovector[1];
159
160
161 if (offset < strlen(servers_to_parse) && servers_to_parse[offset] == ',') {
162 offset++;
163 }
164 }
165
166 pcre2_match_data_free(match_data);
167 return count;
168}
const char * asciichat_pcre2_extract_group_ptr(pcre2_match_data *match_data, int group_num, const char *subject, size_t *out_len)
Extract numbered capture group as pointer into subject (non-allocating)