version.c (17592B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25 #include "curl_setup.h" 26 27 #ifdef USE_NGHTTP2 28 #include <nghttp2/nghttp2.h> 29 #endif 30 31 #include <curl/curl.h> 32 #include "urldata.h" 33 #include "vtls/vtls.h" 34 #include "http2.h" 35 #include "vssh/ssh.h" 36 #include "vquic/vquic.h" 37 #include "curl_printf.h" 38 #include "easy_lock.h" 39 40 #ifdef USE_ARES 41 # include <ares.h> 42 #endif 43 44 #ifdef USE_LIBIDN2 45 #include <idn2.h> 46 #endif 47 48 #ifdef USE_LIBPSL 49 #include <libpsl.h> 50 #endif 51 52 #ifdef USE_LIBRTMP 53 #include <librtmp/rtmp.h> 54 #include "curl_rtmp.h" 55 #endif 56 57 #ifdef HAVE_LIBZ 58 #include <zlib.h> 59 #endif 60 61 #ifdef HAVE_BROTLI 62 #if defined(__GNUC__) || defined(__clang__) 63 /* Ignore -Wvla warnings in brotli headers */ 64 #pragma GCC diagnostic push 65 #pragma GCC diagnostic ignored "-Wvla" 66 #endif 67 #include <brotli/decode.h> 68 #if defined(__GNUC__) || defined(__clang__) 69 #pragma GCC diagnostic pop 70 #endif 71 #endif 72 73 #ifdef HAVE_ZSTD 74 #include <zstd.h> 75 #endif 76 77 #ifdef USE_GSASL 78 #include <gsasl.h> 79 #endif 80 81 #ifdef USE_OPENLDAP 82 #include <ldap.h> 83 #endif 84 85 #ifdef HAVE_BROTLI 86 static void brotli_version(char *buf, size_t bufsz) 87 { 88 uint32_t brotli_version = BrotliDecoderVersion(); 89 unsigned int major = brotli_version >> 24; 90 unsigned int minor = (brotli_version & 0x00FFFFFF) >> 12; 91 unsigned int patch = brotli_version & 0x00000FFF; 92 (void)msnprintf(buf, bufsz, "brotli/%u.%u.%u", major, minor, patch); 93 } 94 #endif 95 96 #ifdef HAVE_ZSTD 97 static void zstd_version(char *buf, size_t bufsz) 98 { 99 unsigned int version = ZSTD_versionNumber(); 100 unsigned int major = version / (100 * 100); 101 unsigned int minor = (version - (major * 100 * 100)) / 100; 102 unsigned int patch = version - (major * 100 * 100) - (minor * 100); 103 (void)msnprintf(buf, bufsz, "zstd/%u.%u.%u", major, minor, patch); 104 } 105 #endif 106 107 #ifdef USE_OPENLDAP 108 static void oldap_version(char *buf, size_t bufsz) 109 { 110 LDAPAPIInfo api; 111 api.ldapai_info_version = LDAP_API_INFO_VERSION; 112 113 if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) == LDAP_OPT_SUCCESS) { 114 unsigned int patch = (unsigned int)(api.ldapai_vendor_version % 100); 115 unsigned int major = (unsigned int)(api.ldapai_vendor_version / 10000); 116 unsigned int minor = 117 (((unsigned int)api.ldapai_vendor_version - major * 10000) 118 - patch) / 100; 119 msnprintf(buf, bufsz, "%s/%u.%u.%u", 120 api.ldapai_vendor_name, major, minor, patch); 121 ldap_memfree(api.ldapai_vendor_name); 122 ber_memvfree((void **)api.ldapai_extensions); 123 } 124 else 125 msnprintf(buf, bufsz, "OpenLDAP"); 126 } 127 #endif 128 129 #ifdef USE_LIBPSL 130 static void psl_version(char *buf, size_t bufsz) 131 { 132 #if defined(PSL_VERSION_MAJOR) && (PSL_VERSION_MAJOR > 0 || \ 133 PSL_VERSION_MINOR >= 11) 134 int num = psl_check_version_number(0); 135 msnprintf(buf, bufsz, "libpsl/%d.%d.%d", 136 num >> 16, (num >> 8) & 0xff, num & 0xff); 137 #else 138 msnprintf(buf, bufsz, "libpsl/%s", psl_get_version()); 139 #endif 140 } 141 #endif 142 143 #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) 144 #define USE_IDN 145 #endif 146 147 #ifdef USE_IDN 148 static void idn_version(char *buf, size_t bufsz) 149 { 150 #ifdef USE_LIBIDN2 151 msnprintf(buf, bufsz, "libidn2/%s", idn2_check_version(NULL)); 152 #elif defined(USE_WIN32_IDN) 153 msnprintf(buf, bufsz, "WinIDN"); 154 #elif defined(USE_APPLE_IDN) 155 msnprintf(buf, bufsz, "AppleIDN"); 156 #endif 157 } 158 #endif 159 160 /* 161 * curl_version() returns a pointer to a static buffer. 162 * 163 * It is implemented to work multi-threaded by making sure repeated invokes 164 * generate the exact same string and never write any temporary data like 165 * zeros in the data. 166 */ 167 168 #define VERSION_PARTS 16 /* number of substrings we can concatenate */ 169 170 char *curl_version(void) 171 { 172 static char out[300]; 173 char *outp; 174 size_t outlen; 175 const char *src[VERSION_PARTS]; 176 #ifdef USE_SSL 177 char ssl_version[200]; 178 #endif 179 #ifdef HAVE_LIBZ 180 char z_version[30]; 181 #endif 182 #ifdef HAVE_BROTLI 183 char br_version[30]; 184 #endif 185 #ifdef HAVE_ZSTD 186 char zstd_ver[30]; 187 #endif 188 #ifdef USE_ARES 189 char cares_version[30]; 190 #endif 191 #ifdef USE_IDN 192 char idn_ver[30]; 193 #endif 194 #ifdef USE_LIBPSL 195 char psl_ver[30]; 196 #endif 197 #ifdef USE_SSH 198 char ssh_version[30]; 199 #endif 200 #ifdef USE_NGHTTP2 201 char h2_version[30]; 202 #endif 203 #ifdef USE_HTTP3 204 char h3_version[30]; 205 #endif 206 #ifdef USE_LIBRTMP 207 char rtmp_version[30]; 208 #endif 209 #ifdef USE_GSASL 210 char gsasl_buf[30]; 211 #endif 212 #ifdef USE_OPENLDAP 213 char ldap_buf[30]; 214 #endif 215 int i = 0; 216 int j; 217 218 #ifdef DEBUGBUILD 219 /* Override version string when environment variable CURL_VERSION is set */ 220 const char *debugversion = getenv("CURL_VERSION"); 221 if(debugversion) { 222 msnprintf(out, sizeof(out), "%s", debugversion); 223 return out; 224 } 225 #endif 226 227 src[i++] = LIBCURL_NAME "/" LIBCURL_VERSION; 228 #ifdef USE_SSL 229 Curl_ssl_version(ssl_version, sizeof(ssl_version)); 230 src[i++] = ssl_version; 231 #endif 232 #ifdef HAVE_LIBZ 233 msnprintf(z_version, sizeof(z_version), "zlib/%s", zlibVersion()); 234 src[i++] = z_version; 235 #endif 236 #ifdef HAVE_BROTLI 237 brotli_version(br_version, sizeof(br_version)); 238 src[i++] = br_version; 239 #endif 240 #ifdef HAVE_ZSTD 241 zstd_version(zstd_ver, sizeof(zstd_ver)); 242 src[i++] = zstd_ver; 243 #endif 244 #ifdef USE_ARES 245 msnprintf(cares_version, sizeof(cares_version), 246 "c-ares/%s", ares_version(NULL)); 247 src[i++] = cares_version; 248 #endif 249 #ifdef USE_IDN 250 idn_version(idn_ver, sizeof(idn_ver)); 251 src[i++] = idn_ver; 252 #endif 253 #ifdef USE_LIBPSL 254 psl_version(psl_ver, sizeof(psl_ver)); 255 src[i++] = psl_ver; 256 #endif 257 #ifdef USE_SSH 258 Curl_ssh_version(ssh_version, sizeof(ssh_version)); 259 src[i++] = ssh_version; 260 #endif 261 #ifdef USE_NGHTTP2 262 Curl_http2_ver(h2_version, sizeof(h2_version)); 263 src[i++] = h2_version; 264 #endif 265 #ifdef USE_HTTP3 266 Curl_quic_ver(h3_version, sizeof(h3_version)); 267 src[i++] = h3_version; 268 #endif 269 #ifdef USE_LIBRTMP 270 Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); 271 src[i++] = rtmp_version; 272 #endif 273 #ifdef USE_GSASL 274 msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s", 275 gsasl_check_version(NULL)); 276 src[i++] = gsasl_buf; 277 #endif 278 #ifdef USE_OPENLDAP 279 oldap_version(ldap_buf, sizeof(ldap_buf)); 280 src[i++] = ldap_buf; 281 #endif 282 283 DEBUGASSERT(i <= VERSION_PARTS); 284 285 outp = &out[0]; 286 outlen = sizeof(out); 287 for(j = 0; j < i; j++) { 288 size_t n = strlen(src[j]); 289 /* we need room for a space, the string and the final zero */ 290 if(outlen <= (n + 2)) 291 break; 292 if(j) { 293 /* prepend a space if not the first */ 294 *outp++ = ' '; 295 outlen--; 296 } 297 memcpy(outp, src[j], n); 298 outp += n; 299 outlen -= n; 300 } 301 *outp = 0; 302 303 return out; 304 } 305 306 /* data for curl_version_info 307 308 Keep the list sorted alphabetically. It is also written so that each 309 protocol line has its own #if line to make things easier on the eye. 310 */ 311 312 static const char * const supported_protocols[] = { 313 #ifndef CURL_DISABLE_DICT 314 "dict", 315 #endif 316 #ifndef CURL_DISABLE_FILE 317 "file", 318 #endif 319 #ifndef CURL_DISABLE_FTP 320 "ftp", 321 #endif 322 #if defined(USE_SSL) && !defined(CURL_DISABLE_FTP) 323 "ftps", 324 #endif 325 #ifndef CURL_DISABLE_GOPHER 326 "gopher", 327 #endif 328 #if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER) 329 "gophers", 330 #endif 331 #ifndef CURL_DISABLE_HTTP 332 "http", 333 #endif 334 #if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) 335 "https", 336 #endif 337 #ifndef CURL_DISABLE_IMAP 338 "imap", 339 #endif 340 #if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP) 341 "imaps", 342 #endif 343 #ifndef CURL_DISABLE_LDAP 344 "ldap", 345 #if !defined(CURL_DISABLE_LDAPS) && \ 346 ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ 347 (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) 348 "ldaps", 349 #endif 350 #endif 351 #ifndef CURL_DISABLE_MQTT 352 "mqtt", 353 #endif 354 #ifndef CURL_DISABLE_POP3 355 "pop3", 356 #endif 357 #if defined(USE_SSL) && !defined(CURL_DISABLE_POP3) 358 "pop3s", 359 #endif 360 #ifdef USE_LIBRTMP 361 "rtmp", 362 "rtmpe", 363 "rtmps", 364 "rtmpt", 365 "rtmpte", 366 "rtmpts", 367 #endif 368 #ifndef CURL_DISABLE_RTSP 369 "rtsp", 370 #endif 371 #if defined(USE_SSH) && !defined(USE_WOLFSSH) 372 "scp", 373 #endif 374 #ifdef USE_SSH 375 "sftp", 376 #endif 377 #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) 378 "smb", 379 # ifdef USE_SSL 380 "smbs", 381 # endif 382 #endif 383 #ifndef CURL_DISABLE_SMTP 384 "smtp", 385 #endif 386 #if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP) 387 "smtps", 388 #endif 389 #ifndef CURL_DISABLE_TELNET 390 "telnet", 391 #endif 392 #ifndef CURL_DISABLE_TFTP 393 "tftp", 394 #endif 395 #ifndef CURL_DISABLE_HTTP 396 /* WebSocket support relies on HTTP */ 397 #ifndef CURL_DISABLE_WEBSOCKETS 398 "ws", 399 #endif 400 #if defined(USE_SSL) && !defined(CURL_DISABLE_WEBSOCKETS) 401 "wss", 402 #endif 403 #endif 404 405 NULL 406 }; 407 408 /* 409 * Feature presence runtime check functions. 410 * 411 * Warning: the value returned by these should not change between 412 * curl_global_init() and curl_global_cleanup() calls. 413 */ 414 415 #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) 416 static int idn_present(curl_version_info_data *info) 417 { 418 #if defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) 419 (void)info; 420 return TRUE; 421 #else 422 return info->libidn != NULL; 423 #endif 424 } 425 #endif 426 427 #if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \ 428 !defined(CURL_DISABLE_HTTP) 429 static int https_proxy_present(curl_version_info_data *info) 430 { 431 (void) info; 432 return Curl_ssl_supports(NULL, SSLSUPP_HTTPS_PROXY); 433 } 434 #endif 435 436 #if defined(USE_SSL) && defined(USE_ECH) 437 static int ech_present(curl_version_info_data *info) 438 { 439 (void) info; 440 return Curl_ssl_supports(NULL, SSLSUPP_ECH); 441 } 442 #endif 443 444 /* 445 * Features table. 446 * 447 * Keep the features alphabetically sorted. 448 * Use FEATURE() macro to define an entry: this allows documentation check. 449 */ 450 451 #define FEATURE(name, present, bitmask) {(name), (present), (bitmask)} 452 453 struct feat { 454 const char *name; 455 int (*present)(curl_version_info_data *info); 456 int bitmask; 457 }; 458 459 static const struct feat features_table[] = { 460 #ifndef CURL_DISABLE_ALTSVC 461 FEATURE("alt-svc", NULL, CURL_VERSION_ALTSVC), 462 #endif 463 #if defined(USE_ARES) && defined(CURLRES_THREADED) && defined(USE_HTTPSRR) 464 FEATURE("asyn-rr", NULL, 0), 465 #endif 466 #ifdef CURLRES_ASYNCH 467 FEATURE("AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS), 468 #endif 469 #ifdef HAVE_BROTLI 470 FEATURE("brotli", NULL, CURL_VERSION_BROTLI), 471 #endif 472 #ifdef DEBUGBUILD 473 FEATURE("Debug", NULL, CURL_VERSION_DEBUG), 474 #endif 475 #if defined(USE_SSL) && defined(USE_ECH) 476 FEATURE("ECH", ech_present, 0), 477 478 #ifndef USE_HTTPSRR 479 #error "ECH enabled but not HTTPSRR, must be a config error" 480 #endif 481 #endif 482 #ifdef USE_GSASL 483 FEATURE("gsasl", NULL, CURL_VERSION_GSASL), 484 #endif 485 #ifdef HAVE_GSSAPI 486 FEATURE("GSS-API", NULL, CURL_VERSION_GSSAPI), 487 #endif 488 #ifndef CURL_DISABLE_HSTS 489 FEATURE("HSTS", NULL, CURL_VERSION_HSTS), 490 #endif 491 #if defined(USE_NGHTTP2) 492 FEATURE("HTTP2", NULL, CURL_VERSION_HTTP2), 493 #endif 494 #if defined(USE_HTTP3) 495 FEATURE("HTTP3", NULL, CURL_VERSION_HTTP3), 496 #endif 497 #if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \ 498 !defined(CURL_DISABLE_HTTP) 499 FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY), 500 #endif 501 #if defined(USE_HTTPSRR) 502 FEATURE("HTTPSRR", NULL, 0), 503 #endif 504 #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) 505 FEATURE("IDN", idn_present, CURL_VERSION_IDN), 506 #endif 507 #ifdef USE_IPV6 508 FEATURE("IPv6", NULL, CURL_VERSION_IPV6), 509 #endif 510 #ifdef USE_KERBEROS5 511 FEATURE("Kerberos", NULL, CURL_VERSION_KERBEROS5), 512 #endif 513 #if (SIZEOF_CURL_OFF_T > 4) && \ 514 ( (SIZEOF_OFF_T > 4) || defined(USE_WIN32_LARGE_FILES) ) 515 FEATURE("Largefile", NULL, CURL_VERSION_LARGEFILE), 516 #endif 517 #ifdef HAVE_LIBZ 518 FEATURE("libz", NULL, CURL_VERSION_LIBZ), 519 #endif 520 #ifdef CURL_WITH_MULTI_SSL 521 FEATURE("MultiSSL", NULL, CURL_VERSION_MULTI_SSL), 522 #endif 523 #ifdef USE_NTLM 524 FEATURE("NTLM", NULL, CURL_VERSION_NTLM), 525 #endif 526 #if defined(USE_LIBPSL) 527 FEATURE("PSL", NULL, CURL_VERSION_PSL), 528 #endif 529 #ifdef USE_SPNEGO 530 FEATURE("SPNEGO", NULL, CURL_VERSION_SPNEGO), 531 #endif 532 #ifdef USE_SSL 533 FEATURE("SSL", NULL, CURL_VERSION_SSL), 534 #endif 535 #if defined(USE_SSLS_EXPORT) 536 FEATURE("SSLS-EXPORT", NULL, 0), 537 #endif 538 #ifdef USE_WINDOWS_SSPI 539 FEATURE("SSPI", NULL, CURL_VERSION_SSPI), 540 #endif 541 #ifdef GLOBAL_INIT_IS_THREADSAFE 542 FEATURE("threadsafe", NULL, CURL_VERSION_THREADSAFE), 543 #endif 544 #ifdef USE_TLS_SRP 545 FEATURE("TLS-SRP", NULL, CURL_VERSION_TLSAUTH_SRP), 546 #endif 547 #ifdef CURLDEBUG 548 FEATURE("TrackMemory", NULL, CURL_VERSION_CURLDEBUG), 549 #endif 550 #if defined(_WIN32) && defined(UNICODE) && defined(_UNICODE) 551 FEATURE("Unicode", NULL, CURL_VERSION_UNICODE), 552 #endif 553 #ifdef USE_UNIX_SOCKETS 554 FEATURE("UnixSockets", NULL, CURL_VERSION_UNIX_SOCKETS), 555 #endif 556 #ifdef HAVE_ZSTD 557 FEATURE("zstd", NULL, CURL_VERSION_ZSTD), 558 #endif 559 {NULL, NULL, 0} 560 }; 561 562 static const char *feature_names[sizeof(features_table) / 563 sizeof(features_table[0])] = {NULL}; 564 565 566 static curl_version_info_data version_info = { 567 CURLVERSION_NOW, 568 LIBCURL_VERSION, 569 LIBCURL_VERSION_NUM, 570 CURL_OS, /* as found by configure or set by hand at build-time */ 571 0, /* features bitmask is built at runtime */ 572 NULL, /* ssl_version */ 573 0, /* ssl_version_num, this is kept at zero */ 574 NULL, /* zlib_version */ 575 supported_protocols, 576 NULL, /* c-ares version */ 577 0, /* c-ares version numerical */ 578 NULL, /* libidn version */ 579 0, /* iconv version */ 580 NULL, /* ssh lib version */ 581 0, /* brotli_ver_num */ 582 NULL, /* brotli version */ 583 0, /* nghttp2 version number */ 584 NULL, /* nghttp2 version string */ 585 NULL, /* quic library string */ 586 #ifdef CURL_CA_BUNDLE 587 CURL_CA_BUNDLE, /* cainfo */ 588 #else 589 NULL, 590 #endif 591 #ifdef CURL_CA_PATH 592 CURL_CA_PATH, /* capath */ 593 #else 594 NULL, 595 #endif 596 0, /* zstd_ver_num */ 597 NULL, /* zstd version */ 598 NULL, /* Hyper version */ 599 NULL, /* gsasl version */ 600 feature_names, 601 NULL /* rtmp version */ 602 }; 603 604 curl_version_info_data *curl_version_info(CURLversion stamp) 605 { 606 size_t n; 607 const struct feat *p; 608 int features = 0; 609 610 #if defined(USE_SSH) 611 static char ssh_buf[80]; /* 'ssh_buffer' clashes with libssh/libssh.h */ 612 #endif 613 #ifdef USE_SSL 614 #ifdef CURL_WITH_MULTI_SSL 615 static char ssl_buffer[200]; 616 #else 617 static char ssl_buffer[80]; 618 #endif 619 #endif 620 #ifdef HAVE_BROTLI 621 static char brotli_buffer[80]; 622 #endif 623 #ifdef HAVE_ZSTD 624 static char zstd_buffer[80]; 625 #endif 626 627 (void)stamp; /* avoid compiler warnings, we do not use this */ 628 629 #ifdef USE_SSL 630 Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer)); 631 version_info.ssl_version = ssl_buffer; 632 #endif 633 634 #ifdef HAVE_LIBZ 635 version_info.libz_version = zlibVersion(); 636 /* libz left NULL if non-existing */ 637 #endif 638 #ifdef USE_ARES 639 { 640 int aresnum; 641 version_info.ares = ares_version(&aresnum); 642 version_info.ares_num = aresnum; 643 } 644 #endif 645 #ifdef USE_LIBIDN2 646 /* This returns a version string if we use the given version or later, 647 otherwise it returns NULL */ 648 version_info.libidn = idn2_check_version(IDN2_VERSION); 649 #endif 650 651 #if defined(USE_SSH) 652 Curl_ssh_version(ssh_buf, sizeof(ssh_buf)); 653 version_info.libssh_version = ssh_buf; 654 #endif 655 656 #ifdef HAVE_BROTLI 657 version_info.brotli_ver_num = BrotliDecoderVersion(); 658 brotli_version(brotli_buffer, sizeof(brotli_buffer)); 659 version_info.brotli_version = brotli_buffer; 660 #endif 661 662 #ifdef HAVE_ZSTD 663 version_info.zstd_ver_num = (unsigned int)ZSTD_versionNumber(); 664 zstd_version(zstd_buffer, sizeof(zstd_buffer)); 665 version_info.zstd_version = zstd_buffer; 666 #endif 667 668 #ifdef USE_NGHTTP2 669 { 670 nghttp2_info *h2 = nghttp2_version(0); 671 version_info.nghttp2_ver_num = (unsigned int)h2->version_num; 672 version_info.nghttp2_version = h2->version_str; 673 } 674 #endif 675 676 #ifdef USE_HTTP3 677 { 678 static char quicbuffer[80]; 679 Curl_quic_ver(quicbuffer, sizeof(quicbuffer)); 680 version_info.quic_version = quicbuffer; 681 } 682 #endif 683 684 #ifdef USE_GSASL 685 { 686 version_info.gsasl_version = gsasl_check_version(NULL); 687 } 688 #endif 689 690 /* Get available features, build bitmask and names array. */ 691 n = 0; 692 for(p = features_table; p->name; p++) 693 if(!p->present || p->present(&version_info)) { 694 features |= p->bitmask; 695 feature_names[n++] = p->name; 696 } 697 698 feature_names[n] = NULL; /* Terminate array. */ 699 version_info.features = features; 700 701 #ifdef USE_LIBRTMP 702 { 703 static char rtmp_version[30]; 704 Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); 705 version_info.rtmp_version = rtmp_version; 706 } 707 #endif 708 709 return &version_info; 710 }