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
brotli_version(char * buf,size_t bufsz)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
zstd_version(char * buf,size_t bufsz)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
oldap_version(char * buf,size_t bufsz)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
psl_version(char * buf,size_t bufsz)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
idn_version(char * buf,size_t bufsz)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
curl_version(void)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)
idn_present(curl_version_info_data * info)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 #else
426 #define idn_present NULL
427 #endif
428
429 #if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \
430 !defined(CURL_DISABLE_HTTP)
https_proxy_present(curl_version_info_data * info)431 static int https_proxy_present(curl_version_info_data *info)
432 {
433 (void) info;
434 return Curl_ssl_supports(NULL, SSLSUPP_HTTPS_PROXY);
435 }
436 #endif
437
438 #if defined(USE_SSL) && defined(USE_ECH)
ech_present(curl_version_info_data * info)439 static int ech_present(curl_version_info_data *info)
440 {
441 (void) info;
442 return Curl_ssl_supports(NULL, SSLSUPP_ECH);
443 }
444 #endif
445
446 /*
447 * Features table.
448 *
449 * Keep the features alphabetically sorted.
450 * Use FEATURE() macro to define an entry: this allows documentation check.
451 */
452
453 #define FEATURE(name, present, bitmask) {(name), (present), (bitmask)}
454
455 struct feat {
456 const char *name;
457 int (*present)(curl_version_info_data *info);
458 int bitmask;
459 };
460
461 static const struct feat features_table[] = {
462 #ifndef CURL_DISABLE_ALTSVC
463 FEATURE("alt-svc", NULL, CURL_VERSION_ALTSVC),
464 #endif
465 #if defined(USE_ARES) && defined(CURLRES_THREADED) && defined(USE_HTTPSRR)
466 FEATURE("asyn-rr", NULL, 0),
467 #endif
468 #ifdef CURLRES_ASYNCH
469 FEATURE("AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS),
470 #endif
471 #ifdef HAVE_BROTLI
472 FEATURE("brotli", NULL, CURL_VERSION_BROTLI),
473 #endif
474 #ifdef DEBUGBUILD
475 FEATURE("Debug", NULL, CURL_VERSION_DEBUG),
476 #endif
477 #if defined(USE_SSL) && defined(USE_ECH)
478 FEATURE("ECH", ech_present, 0),
479
480 #ifndef USE_HTTPSRR
481 #error "ECH enabled but not HTTPSRR, must be a config error"
482 #endif
483 #endif
484 #ifdef USE_GSASL
485 FEATURE("gsasl", NULL, CURL_VERSION_GSASL),
486 #endif
487 #ifdef HAVE_GSSAPI
488 FEATURE("GSS-API", NULL, CURL_VERSION_GSSAPI),
489 #endif
490 #ifndef CURL_DISABLE_HSTS
491 FEATURE("HSTS", NULL, CURL_VERSION_HSTS),
492 #endif
493 #if defined(USE_NGHTTP2)
494 FEATURE("HTTP2", NULL, CURL_VERSION_HTTP2),
495 #endif
496 #if defined(USE_HTTP3)
497 FEATURE("HTTP3", NULL, CURL_VERSION_HTTP3),
498 #endif
499 #if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \
500 !defined(CURL_DISABLE_HTTP)
501 FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY),
502 #endif
503 #if defined(USE_HTTPSRR)
504 FEATURE("HTTPSRR", NULL, 0),
505 #endif
506 #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN)
507 FEATURE("IDN", idn_present, CURL_VERSION_IDN),
508 #endif
509 #ifdef USE_IPV6
510 FEATURE("IPv6", NULL, CURL_VERSION_IPV6),
511 #endif
512 #ifdef USE_KERBEROS5
513 FEATURE("Kerberos", NULL, CURL_VERSION_KERBEROS5),
514 #endif
515 #if (SIZEOF_CURL_OFF_T > 4) && \
516 ( (SIZEOF_OFF_T > 4) || defined(USE_WIN32_LARGE_FILES) )
517 FEATURE("Largefile", NULL, CURL_VERSION_LARGEFILE),
518 #endif
519 #ifdef HAVE_LIBZ
520 FEATURE("libz", NULL, CURL_VERSION_LIBZ),
521 #endif
522 #ifdef CURL_WITH_MULTI_SSL
523 FEATURE("MultiSSL", NULL, CURL_VERSION_MULTI_SSL),
524 #endif
525 #ifdef USE_NTLM
526 FEATURE("NTLM", NULL, CURL_VERSION_NTLM),
527 #endif
528 #if defined(USE_LIBPSL)
529 FEATURE("PSL", NULL, CURL_VERSION_PSL),
530 #endif
531 #ifdef USE_SPNEGO
532 FEATURE("SPNEGO", NULL, CURL_VERSION_SPNEGO),
533 #endif
534 #ifdef USE_SSL
535 FEATURE("SSL", NULL, CURL_VERSION_SSL),
536 #endif
537 #if defined(USE_SSLS_EXPORT)
538 FEATURE("SSLS-EXPORT", NULL, 0),
539 #endif
540 #ifdef USE_WINDOWS_SSPI
541 FEATURE("SSPI", NULL, CURL_VERSION_SSPI),
542 #endif
543 #ifdef GLOBAL_INIT_IS_THREADSAFE
544 FEATURE("threadsafe", NULL, CURL_VERSION_THREADSAFE),
545 #endif
546 #ifdef USE_TLS_SRP
547 FEATURE("TLS-SRP", NULL, CURL_VERSION_TLSAUTH_SRP),
548 #endif
549 #ifdef CURLDEBUG
550 FEATURE("TrackMemory", NULL, CURL_VERSION_CURLDEBUG),
551 #endif
552 #if defined(_WIN32) && defined(UNICODE) && defined(_UNICODE)
553 FEATURE("Unicode", NULL, CURL_VERSION_UNICODE),
554 #endif
555 #ifdef USE_UNIX_SOCKETS
556 FEATURE("UnixSockets", NULL, CURL_VERSION_UNIX_SOCKETS),
557 #endif
558 #ifdef HAVE_ZSTD
559 FEATURE("zstd", NULL, CURL_VERSION_ZSTD),
560 #endif
561 {NULL, NULL, 0}
562 };
563
564 static const char *feature_names[sizeof(features_table) /
565 sizeof(features_table[0])] = {NULL};
566
567
568 static curl_version_info_data version_info = {
569 CURLVERSION_NOW,
570 LIBCURL_VERSION,
571 LIBCURL_VERSION_NUM,
572 CURL_OS, /* as found by configure or set by hand at build-time */
573 0, /* features bitmask is built at runtime */
574 NULL, /* ssl_version */
575 0, /* ssl_version_num, this is kept at zero */
576 NULL, /* zlib_version */
577 supported_protocols,
578 NULL, /* c-ares version */
579 0, /* c-ares version numerical */
580 NULL, /* libidn version */
581 0, /* iconv version */
582 NULL, /* ssh lib version */
583 0, /* brotli_ver_num */
584 NULL, /* brotli version */
585 0, /* nghttp2 version number */
586 NULL, /* nghttp2 version string */
587 NULL, /* quic library string */
588 #ifdef CURL_CA_BUNDLE
589 CURL_CA_BUNDLE, /* cainfo */
590 #else
591 NULL,
592 #endif
593 #ifdef CURL_CA_PATH
594 CURL_CA_PATH, /* capath */
595 #else
596 NULL,
597 #endif
598 0, /* zstd_ver_num */
599 NULL, /* zstd version */
600 NULL, /* Hyper version */
601 NULL, /* gsasl version */
602 feature_names,
603 NULL /* rtmp version */
604 };
605
curl_version_info(CURLversion stamp)606 curl_version_info_data *curl_version_info(CURLversion stamp)
607 {
608 size_t n;
609 const struct feat *p;
610 int features = 0;
611
612 #if defined(USE_SSH)
613 static char ssh_buf[80]; /* 'ssh_buffer' clashes with libssh/libssh.h */
614 #endif
615 #ifdef USE_SSL
616 #ifdef CURL_WITH_MULTI_SSL
617 static char ssl_buffer[200];
618 #else
619 static char ssl_buffer[80];
620 #endif
621 #endif
622 #ifdef HAVE_BROTLI
623 static char brotli_buffer[80];
624 #endif
625 #ifdef HAVE_ZSTD
626 static char zstd_buffer[80];
627 #endif
628
629 (void)stamp; /* avoid compiler warnings, we do not use this */
630
631 #ifdef USE_SSL
632 Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer));
633 version_info.ssl_version = ssl_buffer;
634 #endif
635
636 #ifdef HAVE_LIBZ
637 version_info.libz_version = zlibVersion();
638 /* libz left NULL if non-existing */
639 #endif
640 #ifdef USE_ARES
641 {
642 int aresnum;
643 version_info.ares = ares_version(&aresnum);
644 version_info.ares_num = aresnum;
645 }
646 #endif
647 #ifdef USE_LIBIDN2
648 /* This returns a version string if we use the given version or later,
649 otherwise it returns NULL */
650 version_info.libidn = idn2_check_version(IDN2_VERSION);
651 #endif
652
653 #if defined(USE_SSH)
654 Curl_ssh_version(ssh_buf, sizeof(ssh_buf));
655 version_info.libssh_version = ssh_buf;
656 #endif
657
658 #ifdef HAVE_BROTLI
659 version_info.brotli_ver_num = BrotliDecoderVersion();
660 brotli_version(brotli_buffer, sizeof(brotli_buffer));
661 version_info.brotli_version = brotli_buffer;
662 #endif
663
664 #ifdef HAVE_ZSTD
665 version_info.zstd_ver_num = (unsigned int)ZSTD_versionNumber();
666 zstd_version(zstd_buffer, sizeof(zstd_buffer));
667 version_info.zstd_version = zstd_buffer;
668 #endif
669
670 #ifdef USE_NGHTTP2
671 {
672 nghttp2_info *h2 = nghttp2_version(0);
673 version_info.nghttp2_ver_num = (unsigned int)h2->version_num;
674 version_info.nghttp2_version = h2->version_str;
675 }
676 #endif
677
678 #ifdef USE_HTTP3
679 {
680 static char quicbuffer[80];
681 Curl_quic_ver(quicbuffer, sizeof(quicbuffer));
682 version_info.quic_version = quicbuffer;
683 }
684 #endif
685
686 #ifdef USE_GSASL
687 {
688 version_info.gsasl_version = gsasl_check_version(NULL);
689 }
690 #endif
691
692 /* Get available features, build bitmask and names array. */
693 n = 0;
694 for(p = features_table; p->name; p++)
695 if(!p->present || p->present(&version_info)) {
696 features |= p->bitmask;
697 feature_names[n++] = p->name;
698 }
699
700 feature_names[n] = NULL; /* Terminate array. */
701 version_info.features = features;
702
703 #ifdef USE_LIBRTMP
704 {
705 static char rtmp_version[30];
706 Curl_rtmp_version(rtmp_version, sizeof(rtmp_version));
707 version_info.rtmp_version = rtmp_version;
708 }
709 #endif
710
711 return &version_info;
712 }
713