James Kuszmaul | 4a42b18 | 2021-01-17 11:32:46 -0800 | [diff] [blame^] | 1 | #include "server.h" |
| 2 | #include "../main/config.h" |
| 3 | #include <rawrtc/config.h> |
| 4 | #include <rawrtc/ice_gather_options.h> |
| 5 | #include <rawrtc/ice_server.h> |
| 6 | #include <rawrtcc/code.h> |
| 7 | #include <rawrtcc/utils.h> |
| 8 | #include <re.h> |
| 9 | #include <string.h> // strlen |
| 10 | |
| 11 | #define DEBUG_MODULE "ice-server" |
| 12 | //#define RAWRTC_DEBUG_MODULE_LEVEL 7 // Note: Uncomment this to debug this module only |
| 13 | #include <rawrtcc/debug.h> |
| 14 | |
| 15 | /* |
| 16 | * ICE server URL-related regular expressions. |
| 17 | */ |
| 18 | static char const ice_server_url_regex[] = "[a-z]+:[^?]+[^]*"; |
| 19 | static char const ice_server_host_port_regex[] = "[^:]+[:]*[0-9]*"; |
| 20 | static char const ice_server_host_port_ipv6_regex[] = "\\[[0-9a-f:]+\\][:]*[0-9]*"; |
| 21 | static char const ice_server_transport_regex[] = "\\?transport=[a-z]+"; |
| 22 | |
| 23 | /* |
| 24 | * Valid ICE server schemes. |
| 25 | * |
| 26 | * Note: Update `ice_server_scheme_type_mapping`, |
| 27 | * `ice_server_scheme_secure_mapping` and |
| 28 | * `ice_server_scheme_port_mapping` if changed. |
| 29 | */ |
| 30 | static char const* const ice_server_schemes[] = { |
| 31 | "stun", |
| 32 | "stuns", |
| 33 | "turn", |
| 34 | "turns", |
| 35 | }; |
| 36 | static size_t const ice_server_schemes_length = ARRAY_SIZE(ice_server_schemes); |
| 37 | |
| 38 | /* |
| 39 | * ICE server scheme to server type mapping. |
| 40 | */ |
| 41 | static enum rawrtc_ice_server_type ice_server_scheme_type_mapping[] = { |
| 42 | RAWRTC_ICE_SERVER_TYPE_STUN, |
| 43 | RAWRTC_ICE_SERVER_TYPE_STUN, |
| 44 | RAWRTC_ICE_SERVER_TYPE_TURN, |
| 45 | RAWRTC_ICE_SERVER_TYPE_TURN, |
| 46 | }; |
| 47 | |
| 48 | /* |
| 49 | * ICE server scheme to secure mapping. |
| 50 | */ |
| 51 | static bool ice_server_scheme_secure_mapping[] = { |
| 52 | false, |
| 53 | true, |
| 54 | false, |
| 55 | true, |
| 56 | }; |
| 57 | |
| 58 | /* |
| 59 | * ICE server scheme to default port mapping. |
| 60 | */ |
| 61 | static uint_fast16_t ice_server_scheme_port_mapping[] = { |
| 62 | 3478, |
| 63 | 5349, |
| 64 | 3478, |
| 65 | 5349, |
| 66 | }; |
| 67 | |
| 68 | /* |
| 69 | * Valid ICE server transports. |
| 70 | * |
| 71 | * Note: Update `ice_server_transport_normal_transport_mapping` and |
| 72 | * `ice_server_transport_secure_transport_mapping` if changed. |
| 73 | */ |
| 74 | static char const* const ice_server_transports[] = { |
| 75 | "udp", |
| 76 | "tcp", |
| 77 | }; |
| 78 | static size_t const ice_server_transports_length = ARRAY_SIZE(ice_server_transports); |
| 79 | |
| 80 | /* |
| 81 | * ICE server transport to non-secure transport mapping. |
| 82 | */ |
| 83 | static enum rawrtc_ice_server_transport ice_server_transport_normal_transport_mapping[] = { |
| 84 | RAWRTC_ICE_SERVER_TRANSPORT_UDP, |
| 85 | RAWRTC_ICE_SERVER_TRANSPORT_TCP, |
| 86 | }; |
| 87 | |
| 88 | /* |
| 89 | * ICE server transport to secure transport mapping. |
| 90 | */ |
| 91 | static enum rawrtc_ice_server_transport ice_server_transport_secure_transport_mapping[] = { |
| 92 | RAWRTC_ICE_SERVER_TRANSPORT_DTLS, |
| 93 | RAWRTC_ICE_SERVER_TRANSPORT_TLS, |
| 94 | }; |
| 95 | |
| 96 | /* |
| 97 | * Parse ICE server's transport. |
| 98 | */ |
| 99 | static enum rawrtc_code decode_ice_server_transport( |
| 100 | enum rawrtc_ice_server_transport* const transportp, // de-referenced, not checked |
| 101 | struct pl* const query, // not checked |
| 102 | bool const secure) { |
| 103 | enum rawrtc_code error; |
| 104 | struct pl transport; |
| 105 | size_t i; |
| 106 | |
| 107 | // Decode transport |
| 108 | error = |
| 109 | rawrtc_error_to_code(re_regex(query->p, query->l, ice_server_transport_regex, &transport)); |
| 110 | if (error) { |
| 111 | return error; |
| 112 | } |
| 113 | |
| 114 | // Translate transport to ICE server transport |
| 115 | for (i = 0; i < ice_server_transports_length; ++i) { |
| 116 | if (pl_strcmp(&transport, ice_server_transports[i]) == 0) { |
| 117 | if (!secure) { |
| 118 | *transportp = ice_server_transport_normal_transport_mapping[i]; |
| 119 | } else { |
| 120 | *transportp = ice_server_transport_secure_transport_mapping[i]; |
| 121 | } |
| 122 | return RAWRTC_CODE_SUCCESS; |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | // Not found |
| 127 | return RAWRTC_CODE_INVALID_ARGUMENT; |
| 128 | } |
| 129 | |
| 130 | /* |
| 131 | * Parse an ICE scheme to an ICE server type, 'secure' flag and |
| 132 | * default port. |
| 133 | */ |
| 134 | static enum rawrtc_code decode_ice_server_scheme( |
| 135 | enum rawrtc_ice_server_type* const typep, // de-referenced, not checked |
| 136 | bool* const securep, // de-referenced, not checked |
| 137 | uint_fast16_t* const portp, // de-referenced, not checked |
| 138 | struct pl* const scheme // not checked |
| 139 | ) { |
| 140 | size_t i; |
| 141 | |
| 142 | // Translate scheme to ICE server type (and set if secure) |
| 143 | for (i = 0; i < ice_server_schemes_length; ++i) { |
| 144 | if (pl_strcmp(scheme, ice_server_schemes[i]) == 0) { |
| 145 | // Set values |
| 146 | *typep = ice_server_scheme_type_mapping[i]; |
| 147 | *securep = ice_server_scheme_secure_mapping[i]; |
| 148 | *portp = ice_server_scheme_port_mapping[i]; |
| 149 | |
| 150 | // Done |
| 151 | return RAWRTC_CODE_SUCCESS; |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | // Not found |
| 156 | return RAWRTC_CODE_INVALID_ARGUMENT; |
| 157 | } |
| 158 | |
| 159 | /* |
| 160 | * Parse an ICE server URL according to RFC 7064 and RFC 7065 |
| 161 | * (although the `transport` part is inaccurate for RFC 7064 but it |
| 162 | * seems useful) |
| 163 | */ |
| 164 | static enum rawrtc_code decode_ice_server_url( |
| 165 | struct rawrtc_ice_server_url* const url // not checked |
| 166 | ) { |
| 167 | enum rawrtc_code error; |
| 168 | struct pl scheme; |
| 169 | struct pl host_port; |
| 170 | struct pl query; |
| 171 | bool secure; |
| 172 | struct pl port_pl; |
| 173 | uint_fast16_t port; |
| 174 | |
| 175 | // Decode URL |
| 176 | error = rawrtc_error_to_code( |
| 177 | re_regex(url->url, strlen(url->url), ice_server_url_regex, &scheme, &host_port, &query)); |
| 178 | if (error) { |
| 179 | DEBUG_WARNING("Invalid ICE server URL: %s\n", url->url); |
| 180 | goto out; |
| 181 | } |
| 182 | |
| 183 | // TODO: Can scheme or host be NULL? |
| 184 | |
| 185 | // Get server type, secure flag and default port from scheme |
| 186 | error = decode_ice_server_scheme(&url->type, &secure, &port, &scheme); |
| 187 | if (error) { |
| 188 | DEBUG_WARNING("Invalid scheme in ICE server URL (%s): %r\n", url->url, &scheme); |
| 189 | goto out; |
| 190 | } |
| 191 | |
| 192 | // Set default address |
| 193 | sa_set_in(&url->resolved_address, INADDR_ANY, (uint16_t) port); |
| 194 | |
| 195 | // Decode host: Either IPv4 or IPv6 including the port (if any) |
| 196 | // Try IPv6 first, then normal hostname/IPv4. |
| 197 | error = rawrtc_error_to_code(re_regex( |
| 198 | host_port.p, host_port.l, ice_server_host_port_ipv6_regex, &url->host, NULL, &port_pl)); |
| 199 | if (error) { |
| 200 | error = rawrtc_error_to_code(re_regex( |
| 201 | host_port.p, host_port.l, ice_server_host_port_regex, &url->host, NULL, &port_pl)); |
| 202 | if (error) { |
| 203 | DEBUG_WARNING( |
| 204 | "Invalid host or port in ICE server URL (%s): %r\n", url->url, &host_port); |
| 205 | goto out; |
| 206 | } |
| 207 | |
| 208 | // Try decoding IPv4 |
| 209 | sa_set(&url->resolved_address, &url->host, (uint16_t) port); |
| 210 | } else { |
| 211 | // Try decoding IPv6 |
| 212 | error = rawrtc_error_to_code(sa_set(&url->resolved_address, &url->host, (uint16_t) port)); |
| 213 | if (error) { |
| 214 | DEBUG_WARNING( |
| 215 | "Invalid IPv6 address in ICE server URL (%s): %r\n", url->url, &host_port); |
| 216 | goto out; |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | // Decode port (if any) |
| 221 | if (pl_isset(&port_pl)) { |
| 222 | uint_fast32_t port_u32; |
| 223 | |
| 224 | // Get port |
| 225 | port_u32 = pl_u32(&port_pl); |
| 226 | if (port_u32 == 0 || port_u32 > UINT16_MAX) { |
| 227 | DEBUG_WARNING( |
| 228 | "Invalid port number in ICE server URL (%s): %" PRIu32 "\n", url->url, port_u32); |
| 229 | error = RAWRTC_CODE_INVALID_ARGUMENT; |
| 230 | goto out; |
| 231 | } |
| 232 | |
| 233 | // Set port |
| 234 | sa_set_port(&url->resolved_address, (uint16_t) port_u32); |
| 235 | } |
| 236 | |
| 237 | // Translate transport (if any) & secure flag to ICE server transport |
| 238 | if (pl_isset(&query)) { |
| 239 | error = decode_ice_server_transport(&url->transport, &query, secure); |
| 240 | if (error) { |
| 241 | DEBUG_WARNING("Invalid transport in ICE server URL (%s): %r\n", url->url, &query); |
| 242 | goto out; |
| 243 | } |
| 244 | } else { |
| 245 | // Set default transport (depending on secure flag) |
| 246 | if (secure) { |
| 247 | url->transport = rawrtc_default_config.ice_server_secure_transport; |
| 248 | } else { |
| 249 | url->transport = rawrtc_default_config.ice_server_normal_transport; |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | // Done |
| 254 | error = RAWRTC_CODE_SUCCESS; |
| 255 | |
| 256 | out: |
| 257 | return error; |
| 258 | } |
| 259 | |
| 260 | /* |
| 261 | * Destructor for URLs of the ICE gatherer. |
| 262 | */ |
| 263 | static void rawrtc_ice_server_url_destroy(void* arg) { |
| 264 | struct rawrtc_ice_server_url* const url = arg; |
| 265 | |
| 266 | // Remove from list |
| 267 | list_unlink(&url->le); |
| 268 | |
| 269 | // Un-reference |
| 270 | mem_deref(url->url); |
| 271 | } |
| 272 | |
| 273 | /* |
| 274 | * Copy a URL for the ICE gatherer. |
| 275 | */ |
| 276 | static enum rawrtc_code rawrtc_ice_server_url_create( |
| 277 | struct rawrtc_ice_server_url** const urlp, // de-referenced |
| 278 | char* const url_s // copied |
| 279 | ) { |
| 280 | struct rawrtc_ice_server_url* url; |
| 281 | enum rawrtc_code error; |
| 282 | |
| 283 | // Check arguments |
| 284 | if (!urlp || !url_s) { |
| 285 | return RAWRTC_CODE_INVALID_ARGUMENT; |
| 286 | } |
| 287 | |
| 288 | // Allocate |
| 289 | url = mem_zalloc(sizeof(*url), rawrtc_ice_server_url_destroy); |
| 290 | if (!url) { |
| 291 | return RAWRTC_CODE_NO_MEMORY; |
| 292 | } |
| 293 | |
| 294 | // Copy URL |
| 295 | error = rawrtc_strdup(&url->url, url_s); |
| 296 | if (error) { |
| 297 | goto out; |
| 298 | } |
| 299 | |
| 300 | // Parse URL |
| 301 | // Note: `url->host` points inside `url->url`, so we MUST have copied the URL first. |
| 302 | error = decode_ice_server_url(url); |
| 303 | if (error) { |
| 304 | goto out; |
| 305 | } |
| 306 | |
| 307 | // Done |
| 308 | error = RAWRTC_CODE_SUCCESS; |
| 309 | |
| 310 | out: |
| 311 | if (error) { |
| 312 | mem_deref(url); |
| 313 | } else { |
| 314 | // Set pointer |
| 315 | *urlp = url; |
| 316 | } |
| 317 | return error; |
| 318 | } |
| 319 | |
| 320 | /* |
| 321 | * Destructor for an existing ICE server. |
| 322 | */ |
| 323 | static void rawrtc_ice_server_destroy(void* arg) { |
| 324 | struct rawrtc_ice_server* const server = arg; |
| 325 | |
| 326 | // Un-reference |
| 327 | list_flush(&server->urls); |
| 328 | mem_deref(server->username); |
| 329 | mem_deref(server->credential); |
| 330 | } |
| 331 | |
| 332 | /* |
| 333 | * Create an ICE server. |
| 334 | */ |
| 335 | enum rawrtc_code rawrtc_ice_server_create( |
| 336 | struct rawrtc_ice_server** const serverp, // de-referenced |
| 337 | char* const* const urls, // copied |
| 338 | size_t const n_urls, |
| 339 | char* const username, // nullable, copied |
| 340 | char* const credential, // nullable, copied |
| 341 | enum rawrtc_ice_credential_type const credential_type) { |
| 342 | struct rawrtc_ice_server* server; |
| 343 | enum rawrtc_code error = RAWRTC_CODE_SUCCESS; |
| 344 | size_t i; |
| 345 | |
| 346 | // Check arguments |
| 347 | if (!serverp || !urls) { |
| 348 | return RAWRTC_CODE_INVALID_ARGUMENT; |
| 349 | } |
| 350 | |
| 351 | // Allocate |
| 352 | server = mem_zalloc(sizeof(*server), rawrtc_ice_server_destroy); |
| 353 | if (!server) { |
| 354 | return RAWRTC_CODE_NO_MEMORY; |
| 355 | } |
| 356 | |
| 357 | // Copy URLs to list |
| 358 | list_init(&server->urls); |
| 359 | for (i = 0; i < n_urls; ++i) { |
| 360 | struct rawrtc_ice_server_url* url; |
| 361 | |
| 362 | // Ensure URLs aren't null |
| 363 | if (!urls[i]) { |
| 364 | error = RAWRTC_CODE_INVALID_ARGUMENT; |
| 365 | goto out; |
| 366 | } |
| 367 | |
| 368 | // Copy URL |
| 369 | error = rawrtc_ice_server_url_create(&url, urls[i]); |
| 370 | if (error) { |
| 371 | goto out; |
| 372 | } |
| 373 | |
| 374 | // Append URL to list |
| 375 | list_append(&server->urls, &url->le, url); |
| 376 | } |
| 377 | |
| 378 | // Set fields |
| 379 | if (credential_type != RAWRTC_ICE_CREDENTIAL_TYPE_NONE) { |
| 380 | if (username) { |
| 381 | error = rawrtc_strdup(&server->username, username); |
| 382 | if (error) { |
| 383 | goto out; |
| 384 | } |
| 385 | } |
| 386 | if (credential) { |
| 387 | error = rawrtc_strdup(&server->credential, credential); |
| 388 | if (error) { |
| 389 | goto out; |
| 390 | } |
| 391 | } |
| 392 | } |
| 393 | server->credential_type = credential_type; // TODO: Validation needed in case TOKEN is used? |
| 394 | |
| 395 | out: |
| 396 | if (error) { |
| 397 | mem_deref(server); |
| 398 | } else { |
| 399 | // Set pointer |
| 400 | *serverp = server; |
| 401 | } |
| 402 | return error; |
| 403 | } |
| 404 | |
| 405 | /* |
| 406 | * Copy an ICE server. |
| 407 | */ |
| 408 | enum rawrtc_code rawrtc_ice_server_copy( |
| 409 | struct rawrtc_ice_server** const serverp, // de-referenced |
| 410 | struct rawrtc_ice_server* const source_server) { |
| 411 | size_t n_urls; |
| 412 | char** urls = NULL; |
| 413 | struct le* le; |
| 414 | size_t i; |
| 415 | enum rawrtc_code error; |
| 416 | |
| 417 | // Check arguments |
| 418 | if (!serverp || !source_server) { |
| 419 | return RAWRTC_CODE_INVALID_ARGUMENT; |
| 420 | } |
| 421 | |
| 422 | // Create temporary ICE server URL array |
| 423 | n_urls = list_count(&source_server->urls); |
| 424 | if (n_urls > 0) { |
| 425 | urls = mem_alloc(sizeof(char*) * n_urls, NULL); |
| 426 | if (!urls) { |
| 427 | return RAWRTC_CODE_NO_MEMORY; |
| 428 | } |
| 429 | } |
| 430 | |
| 431 | // Copy ICE server URL (str) pointers |
| 432 | for (le = list_head(&source_server->urls), i = 0; le != NULL; le = le->next, ++i) { |
| 433 | struct rawrtc_ice_server_url* const url = le->data; |
| 434 | urls[i] = url->url; |
| 435 | } |
| 436 | |
| 437 | // Copy |
| 438 | error = rawrtc_ice_server_create( |
| 439 | serverp, urls, n_urls, source_server->username, source_server->credential, |
| 440 | source_server->credential_type); |
| 441 | if (error) { |
| 442 | goto out; |
| 443 | } |
| 444 | |
| 445 | out: |
| 446 | // Un-reference |
| 447 | mem_deref(urls); |
| 448 | return error; |
| 449 | } |