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 "doh.h"
40 #include "http2.h"
41 #include "http_proxy.h"
42 #include "cf-h1-proxy.h"
43 #include "cf-h2-proxy.h"
44 #include "cf-haproxy.h"
45 #include "cf-https-connect.h"
46 #include "socks.h"
47 #include "strtok.h"
48 #include "vtls/vtls.h"
49 #include "vquic/vquic.h"
50
51 /* The last 3 #include files should be in this order */
52 #include "curl_printf.h"
53 #include "curl_memory.h"
54 #include "memdebug.h"
55
56 #ifndef ARRAYSIZE
57 #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
58 #endif
59
Curl_debug(struct Curl_easy * data,curl_infotype type,char * ptr,size_t size)60 void Curl_debug(struct Curl_easy *data, curl_infotype type,
61 char *ptr, size_t size)
62 {
63 if(data->set.verbose) {
64 static const char s_infotype[CURLINFO_END][3] = {
65 "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
66 if(data->set.fdebug) {
67 bool inCallback = Curl_is_in_callback(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, inCallback);
71 }
72 else {
73 switch(type) {
74 case CURLINFO_TEXT:
75 case CURLINFO_HEADER_OUT:
76 case CURLINFO_HEADER_IN:
77 fwrite(s_infotype[type], 2, 1, data->set.err);
78 fwrite(ptr, size, 1, 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 */
Curl_failf(struct Curl_easy * data,const char * fmt,...)91 void 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(error, CURL_ERROR_SIZE, fmt, ap);
100
101 if(data->set.errorbuffer && !data->state.errorbuf) {
102 strcpy(data->set.errorbuffer, error);
103 data->state.errorbuf = TRUE; /* wrote error string */
104 }
105 error[len++] = '\n';
106 error[len] = '\0';
107 Curl_debug(data, CURLINFO_TEXT, error, len);
108 va_end(ap);
109 }
110 }
111
112 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
113
114 /* Curl_infof() is for info message along the way */
115 #define MAXINFO 2048
116
117 static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat,
118 const char * const fmt, va_list ap) CURL_PRINTF(3, 0);
119
trc_infof(struct Curl_easy * data,struct curl_trc_feat * feat,const char * const fmt,va_list ap)120 static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat,
121 const char * const fmt, va_list ap)
122 {
123 int len = 0;
124 char buffer[MAXINFO + 5];
125 if(feat)
126 len = msnprintf(buffer, (MAXINFO + 1), "[%s] ", feat->name);
127 len += mvsnprintf(buffer + len, (MAXINFO + 1) - len, fmt, ap);
128 if(len >= MAXINFO) { /* too long, shorten with '...' */
129 --len;
130 buffer[len++] = '.';
131 buffer[len++] = '.';
132 buffer[len++] = '.';
133 }
134 buffer[len++] = '\n';
135 buffer[len] = '\0';
136 Curl_debug(data, CURLINFO_TEXT, buffer, len);
137 }
138
Curl_infof(struct Curl_easy * data,const char * fmt,...)139 void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
140 {
141 DEBUGASSERT(!strchr(fmt, '\n'));
142 if(Curl_trc_is_verbose(data)) {
143 va_list ap;
144 va_start(ap, fmt);
145 trc_infof(data, data->state.feat, fmt, ap);
146 va_end(ap);
147 }
148 }
149
Curl_trc_cf_infof(struct Curl_easy * data,struct Curl_cfilter * cf,const char * fmt,...)150 void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
151 const char *fmt, ...)
152 {
153 DEBUGASSERT(cf);
154 if(Curl_trc_cf_is_verbose(cf, data)) {
155 va_list ap;
156 int len = 0;
157 char buffer[MAXINFO + 2];
158 if(data->state.feat)
159 len += msnprintf(buffer + len, MAXINFO - len, "[%s] ",
160 data->state.feat->name);
161 if(cf->sockindex)
162 len += msnprintf(buffer + len, MAXINFO - len, "[%s-%d] ",
163 cf->cft->name, cf->sockindex);
164 else
165 len += msnprintf(buffer + len, MAXINFO - len, "[%s] ", cf->cft->name);
166 va_start(ap, fmt);
167 len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
168 va_end(ap);
169 buffer[len++] = '\n';
170 buffer[len] = '\0';
171 Curl_debug(data, CURLINFO_TEXT, buffer, len);
172 }
173 }
174
175 struct curl_trc_feat Curl_trc_feat_read = {
176 "READ",
177 CURL_LOG_LVL_NONE,
178 };
179 struct curl_trc_feat Curl_trc_feat_write = {
180 "WRITE",
181 CURL_LOG_LVL_NONE,
182 };
183
Curl_trc_read(struct Curl_easy * data,const char * fmt,...)184 void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
185 {
186 DEBUGASSERT(!strchr(fmt, '\n'));
187 if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) {
188 va_list ap;
189 va_start(ap, fmt);
190 trc_infof(data, &Curl_trc_feat_read, fmt, ap);
191 va_end(ap);
192 }
193 }
194
Curl_trc_write(struct Curl_easy * data,const char * fmt,...)195 void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...)
196 {
197 DEBUGASSERT(!strchr(fmt, '\n'));
198 if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) {
199 va_list ap;
200 va_start(ap, fmt);
201 trc_infof(data, &Curl_trc_feat_write, fmt, ap);
202 va_end(ap);
203 }
204 }
205
206 #ifndef CURL_DISABLE_FTP
207 struct curl_trc_feat Curl_trc_feat_ftp = {
208 "FTP",
209 CURL_LOG_LVL_NONE,
210 };
211
Curl_trc_ftp(struct Curl_easy * data,const char * fmt,...)212 void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
213 {
214 DEBUGASSERT(!strchr(fmt, '\n'));
215 if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) {
216 va_list ap;
217 va_start(ap, fmt);
218 trc_infof(data, &Curl_trc_feat_ftp, fmt, ap);
219 va_end(ap);
220 }
221 }
222 #endif /* !CURL_DISABLE_FTP */
223
224 #ifndef CURL_DISABLE_SMTP
225 struct curl_trc_feat Curl_trc_feat_smtp = {
226 "SMTP",
227 CURL_LOG_LVL_NONE,
228 };
229
Curl_trc_smtp(struct Curl_easy * data,const char * fmt,...)230 void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...)
231 {
232 DEBUGASSERT(!strchr(fmt, '\n'));
233 if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) {
234 va_list ap;
235 va_start(ap, fmt);
236 trc_infof(data, &Curl_trc_feat_smtp, fmt, ap);
237 va_end(ap);
238 }
239 }
240 #endif /* !CURL_DISABLE_SMTP */
241
242 #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
243 struct curl_trc_feat Curl_trc_feat_ws = {
244 "WS",
245 CURL_LOG_LVL_NONE,
246 };
247
Curl_trc_ws(struct Curl_easy * data,const char * fmt,...)248 void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...)
249 {
250 DEBUGASSERT(!strchr(fmt, '\n'));
251 if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) {
252 va_list ap;
253 va_start(ap, fmt);
254 trc_infof(data, &Curl_trc_feat_ws, fmt, ap);
255 va_end(ap);
256 }
257 }
258 #endif /* !CURL_DISABLE_WEBSOCKETS && !CURL_DISABLE_HTTP */
259
260 #define TRC_CT_NONE (0)
261 #define TRC_CT_PROTOCOL (1<<(0))
262 #define TRC_CT_NETWORK (1<<(1))
263 #define TRC_CT_PROXY (1<<(2))
264
265 struct trc_feat_def {
266 struct curl_trc_feat *feat;
267 unsigned int category;
268 };
269
270 static struct trc_feat_def trc_feats[] = {
271 { &Curl_trc_feat_read, TRC_CT_NONE },
272 { &Curl_trc_feat_write, TRC_CT_NONE },
273 #ifndef CURL_DISABLE_FTP
274 { &Curl_trc_feat_ftp, TRC_CT_PROTOCOL },
275 #endif
276 #ifndef CURL_DISABLE_DOH
277 { &Curl_doh_trc, TRC_CT_NETWORK },
278 #endif
279 #ifndef CURL_DISABLE_SMTP
280 { &Curl_trc_feat_smtp, TRC_CT_PROTOCOL },
281 #endif
282 #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
283 { &Curl_trc_feat_ws, TRC_CT_PROTOCOL },
284 #endif
285 };
286
287 struct trc_cft_def {
288 struct Curl_cftype *cft;
289 unsigned int category;
290 };
291
292 static struct trc_cft_def trc_cfts[] = {
293 { &Curl_cft_tcp, TRC_CT_NETWORK },
294 { &Curl_cft_udp, TRC_CT_NETWORK },
295 { &Curl_cft_unix, TRC_CT_NETWORK },
296 { &Curl_cft_tcp_accept, TRC_CT_NETWORK },
297 { &Curl_cft_happy_eyeballs, TRC_CT_NETWORK },
298 { &Curl_cft_setup, TRC_CT_PROTOCOL },
299 #ifdef USE_NGHTTP2
300 { &Curl_cft_nghttp2, TRC_CT_PROTOCOL },
301 #endif
302 #ifdef USE_SSL
303 { &Curl_cft_ssl, TRC_CT_NETWORK },
304 #ifndef CURL_DISABLE_PROXY
305 { &Curl_cft_ssl_proxy, TRC_CT_PROXY },
306 #endif
307 #endif
308 #if !defined(CURL_DISABLE_PROXY)
309 #if !defined(CURL_DISABLE_HTTP)
310 { &Curl_cft_h1_proxy, TRC_CT_PROXY },
311 #ifdef USE_NGHTTP2
312 { &Curl_cft_h2_proxy, TRC_CT_PROXY },
313 #endif
314 { &Curl_cft_http_proxy, TRC_CT_PROXY },
315 #endif /* !CURL_DISABLE_HTTP */
316 { &Curl_cft_haproxy, TRC_CT_PROXY },
317 { &Curl_cft_socks_proxy, TRC_CT_PROXY },
318 #endif /* !CURL_DISABLE_PROXY */
319 #ifdef USE_HTTP3
320 { &Curl_cft_http3, TRC_CT_PROTOCOL },
321 #endif
322 #if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
323 { &Curl_cft_http_connect, TRC_CT_PROTOCOL },
324 #endif
325 };
326
trc_apply_level_by_name(const char * const token,int lvl)327 static void trc_apply_level_by_name(const char * const token, int lvl)
328 {
329 size_t i;
330
331 for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) {
332 if(strcasecompare(token, trc_cfts[i].cft->name)) {
333 trc_cfts[i].cft->log_level = lvl;
334 break;
335 }
336 }
337 for(i = 0; i < ARRAYSIZE(trc_feats); ++i) {
338 if(strcasecompare(token, trc_feats[i].feat->name)) {
339 trc_feats[i].feat->log_level = lvl;
340 break;
341 }
342 }
343 }
344
trc_apply_level_by_category(int category,int lvl)345 static void trc_apply_level_by_category(int category, int lvl)
346 {
347 size_t i;
348
349 for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) {
350 if(!category || (trc_cfts[i].category & category))
351 trc_cfts[i].cft->log_level = lvl;
352 }
353 for(i = 0; i < ARRAYSIZE(trc_feats); ++i) {
354 if(!category || (trc_feats[i].category & category))
355 trc_feats[i].feat->log_level = lvl;
356 }
357 }
358
trc_opt(const char * config)359 static CURLcode trc_opt(const char *config)
360 {
361 char *token, *tok_buf, *tmp;
362 int lvl;
363
364 tmp = strdup(config);
365 if(!tmp)
366 return CURLE_OUT_OF_MEMORY;
367
368 token = Curl_strtok_r(tmp, ", ", &tok_buf);
369 while(token) {
370 switch(*token) {
371 case '-':
372 lvl = CURL_LOG_LVL_NONE;
373 ++token;
374 break;
375 case '+':
376 lvl = CURL_LOG_LVL_INFO;
377 ++token;
378 break;
379 default:
380 lvl = CURL_LOG_LVL_INFO;
381 break;
382 }
383 if(strcasecompare(token, "all"))
384 trc_apply_level_by_category(TRC_CT_NONE, lvl);
385 else if(strcasecompare(token, "protocol"))
386 trc_apply_level_by_category(TRC_CT_PROTOCOL, lvl);
387 else if(strcasecompare(token, "network"))
388 trc_apply_level_by_category(TRC_CT_NETWORK, lvl);
389 else if(strcasecompare(token, "proxy"))
390 trc_apply_level_by_category(TRC_CT_PROXY, lvl);
391 else
392 trc_apply_level_by_name(token, lvl);
393
394 token = Curl_strtok_r(NULL, ", ", &tok_buf);
395 }
396 free(tmp);
397 return CURLE_OK;
398 }
399
Curl_trc_opt(const char * config)400 CURLcode Curl_trc_opt(const char *config)
401 {
402 CURLcode result = config ? trc_opt(config) : CURLE_OK;
403 #ifdef DEBUGBUILD
404 /* CURL_DEBUG can override anything */
405 if(!result) {
406 const char *dbg_config = getenv("CURL_DEBUG");
407 if(dbg_config)
408 result = trc_opt(dbg_config);
409 }
410 #endif /* DEBUGBUILD */
411 return result;
412 }
413
Curl_trc_init(void)414 CURLcode Curl_trc_init(void)
415 {
416 #ifdef DEBUGBUILD
417 return Curl_trc_opt(NULL);
418 #else
419 return CURLE_OK;
420 #endif
421 }
422
423 #else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
424
Curl_trc_init(void)425 CURLcode Curl_trc_init(void)
426 {
427 return CURLE_OK;
428 }
429
430 #endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
431