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#include <curl/curl.h>
28
29#include "curl_trc.h"
30#include "urldata.h"
31#include "easyif.h"
32#include "cfilters.h"
33#include "timeval.h"
34#include "multiif.h"
35#include "strcase.h"
36
37#include "cf-socket.h"
38#include "connect.h"
39#include "http2.h"
40#include "http_proxy.h"
41#include "cf-h1-proxy.h"
42#include "cf-h2-proxy.h"
43#include "cf-haproxy.h"
44#include "cf-https-connect.h"
45#include "socks.h"
46#include "strtok.h"
47#include "vtls/vtls.h"
48#include "vquic/vquic.h"
49
50/* The last 3 #include files should be in this order */
51#include "curl_printf.h"
52#include "curl_memory.h"
53#include "memdebug.h"
54
55
56void Curl_debug(struct Curl_easy *data, curl_infotype type,
57 char *ptr, size_t size)
58{
59 if(data->set.verbose) {
60 static const char s_infotype[CURLINFO_END][3] = {
61 "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
62 if(data->set.fdebug) {
63 bool inCallback = Curl_is_in_callback(easy: data);
64 /* CURLOPT_DEBUGFUNCTION doc says the user may set CURLOPT_PRIVATE to
65 distinguish their handle from internal handles. */
66 if(data->internal)
67 DEBUGASSERT(!data->set.private_data);
68 Curl_set_in_callback(data, true);
69 (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
70 Curl_set_in_callback(data, value: inCallback);
71 }
72 else {
73 switch(type) {
74 case CURLINFO_TEXT:
75 case CURLINFO_HEADER_OUT:
76 case CURLINFO_HEADER_IN:
77 fwrite(ptr: s_infotype[type], size: 2, n: 1, s: data->set.err);
78 fwrite(ptr: ptr, size: size, n: 1, s: data->set.err);
79 break;
80 default: /* nada */
81 break;
82 }
83 }
84 }
85}
86
87
88/* Curl_failf() is for messages stating why we failed.
89 * The message SHALL NOT include any LF or CR.
90 */
91void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
92{
93 DEBUGASSERT(!strchr(fmt, '\n'));
94 if(data->set.verbose || data->set.errorbuffer) {
95 va_list ap;
96 int len;
97 char error[CURL_ERROR_SIZE + 2];
98 va_start(ap, fmt);
99 len = mvsnprintf(buffer: error, CURL_ERROR_SIZE, format: fmt, args: ap);
100
101 if(data->set.errorbuffer && !data->state.errorbuf) {
102 strcpy(dest: data->set.errorbuffer, src: error);
103 data->state.errorbuf = TRUE; /* wrote error string */
104 }
105 error[len++] = '\n';
106 error[len] = '\0';
107 Curl_debug(data, type: CURLINFO_TEXT, ptr: error, size: len);
108 va_end(ap);
109 }
110}
111
112/* Curl_infof() is for info message along the way */
113#define MAXINFO 2048
114
115void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
116{
117 DEBUGASSERT(!strchr(fmt, '\n'));
118 if(data && data->set.verbose) {
119 va_list ap;
120 int len;
121 char buffer[MAXINFO + 2];
122 va_start(ap, fmt);
123 len = mvsnprintf(buffer, MAXINFO, format: fmt, args: ap);
124 va_end(ap);
125 buffer[len++] = '\n';
126 buffer[len] = '\0';
127 Curl_debug(data, type: CURLINFO_TEXT, ptr: buffer, size: len);
128 }
129}
130
131#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
132
133void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
134 const char *fmt, ...)
135{
136 DEBUGASSERT(cf);
137 if(data && Curl_trc_cf_is_verbose(cf, data)) {
138 va_list ap;
139 int len;
140 char buffer[MAXINFO + 2];
141 len = msnprintf(buffer, MAXINFO, format: "[%s] ", cf->cft->name);
142 va_start(ap, fmt);
143 len += mvsnprintf(buffer: buffer + len, MAXINFO - len, format: fmt, args: ap);
144 va_end(ap);
145 buffer[len++] = '\n';
146 buffer[len] = '\0';
147 Curl_debug(data, type: CURLINFO_TEXT, ptr: buffer, size: len);
148 }
149}
150
151
152static struct Curl_cftype *cf_types[] = {
153 &Curl_cft_tcp,
154 &Curl_cft_udp,
155 &Curl_cft_unix,
156 &Curl_cft_tcp_accept,
157 &Curl_cft_happy_eyeballs,
158 &Curl_cft_setup,
159#ifdef USE_NGHTTP2
160 &Curl_cft_nghttp2,
161#endif
162#ifdef USE_SSL
163 &Curl_cft_ssl,
164 &Curl_cft_ssl_proxy,
165#endif
166#if !defined(CURL_DISABLE_PROXY)
167#if !defined(CURL_DISABLE_HTTP)
168 &Curl_cft_h1_proxy,
169#ifdef USE_NGHTTP2
170 &Curl_cft_h2_proxy,
171#endif
172 &Curl_cft_http_proxy,
173#endif /* !CURL_DISABLE_HTTP */
174 &Curl_cft_haproxy,
175 &Curl_cft_socks_proxy,
176#endif /* !CURL_DISABLE_PROXY */
177#ifdef ENABLE_QUIC
178 &Curl_cft_http3,
179#endif
180#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
181 &Curl_cft_http_connect,
182#endif
183 NULL,
184};
185
186CURLcode Curl_trc_opt(const char *config)
187{
188 char *token, *tok_buf, *tmp;
189 size_t i;
190 int lvl;
191
192 tmp = strdup(config);
193 if(!tmp)
194 return CURLE_OUT_OF_MEMORY;
195
196 token = strtok_r(s: tmp, delim: ", ", save_ptr: &tok_buf);
197 while(token) {
198 switch(*token) {
199 case '-':
200 lvl = CURL_LOG_LVL_NONE;
201 ++token;
202 break;
203 case '+':
204 lvl = CURL_LOG_LVL_INFO;
205 ++token;
206 break;
207 default:
208 lvl = CURL_LOG_LVL_INFO;
209 break;
210 }
211 for(i = 0; cf_types[i]; ++i) {
212 if(strcasecompare(token, "all")) {
213 cf_types[i]->log_level = lvl;
214 }
215 else if(strcasecompare(token, cf_types[i]->name)) {
216 cf_types[i]->log_level = lvl;
217 break;
218 }
219 }
220 token = strtok_r(NULL, delim: ", ", save_ptr: &tok_buf);
221 }
222 free(tmp);
223 return CURLE_OK;
224}
225
226CURLcode Curl_trc_init(void)
227{
228#ifdef DEBUGBUILD
229 /* WIP: we use the auto-init from an env var only in DEBUG builds for
230 * convenience. */
231 const char *config = getenv("CURL_DEBUG");
232 if(config) {
233 return Curl_trc_opt(config);
234 }
235#endif
236 return CURLE_OK;
237}
238#else /* !CURL_DISABLE_VERBOSE_STRINGS) */
239
240CURLcode Curl_trc_init(void)
241{
242 return CURLE_OK;
243}
244
245#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
246void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
247 const char *fmt, ...)
248{
249 (void)data;
250 (void)cf;
251 (void)fmt;
252}
253#endif
254
255#endif /* !DEBUGBUILD */
256