• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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