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
Curl_debug(struct Curl_easy * data,curl_infotype type,char * ptr,size_t size)57 void Curl_debug(struct Curl_easy *data, curl_infotype type,
58 char *ptr, size_t size)
59 {
60 if(data->set.verbose) {
61 static const char s_infotype[CURLINFO_END][3] = {
62 "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
63 if(data->set.fdebug) {
64 bool inCallback = Curl_is_in_callback(data);
65 Curl_set_in_callback(data, true);
66 (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
67 Curl_set_in_callback(data, inCallback);
68 }
69 else {
70 switch(type) {
71 case CURLINFO_TEXT:
72 case CURLINFO_HEADER_OUT:
73 case CURLINFO_HEADER_IN:
74 fwrite(s_infotype[type], 2, 1, data->set.err);
75 fwrite(ptr, size, 1, data->set.err);
76 break;
77 default: /* nada */
78 break;
79 }
80 }
81 }
82 }
83
84
85 /* Curl_failf() is for messages stating why we failed.
86 * The message SHALL NOT include any LF or CR.
87 */
Curl_failf(struct Curl_easy * data,const char * fmt,...)88 void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
89 {
90 DEBUGASSERT(!strchr(fmt, '\n'));
91 if(data->set.verbose || data->set.errorbuffer) {
92 va_list ap;
93 int len;
94 char error[CURL_ERROR_SIZE + 2];
95 va_start(ap, fmt);
96 len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
97
98 if(data->set.errorbuffer && !data->state.errorbuf) {
99 strcpy(data->set.errorbuffer, error);
100 data->state.errorbuf = TRUE; /* wrote error string */
101 }
102 error[len++] = '\n';
103 error[len] = '\0';
104 Curl_debug(data, CURLINFO_TEXT, error, len);
105 va_end(ap);
106 }
107 }
108
109 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
110
111 /* Curl_infof() is for info message along the way */
112 #define MAXINFO 2048
113
114 static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat,
115 const char * const fmt, va_list ap) CURL_PRINTF(3, 0);
116
trc_infof(struct Curl_easy * data,struct curl_trc_feat * feat,const char * const fmt,va_list ap)117 static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat,
118 const char * const fmt, va_list ap)
119 {
120 int len = 0;
121 char buffer[MAXINFO + 2];
122 if(feat)
123 len = msnprintf(buffer, MAXINFO, "[%s] ", feat->name);
124 len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
125 buffer[len++] = '\n';
126 buffer[len] = '\0';
127 Curl_debug(data, CURLINFO_TEXT, buffer, len);
128 }
129
Curl_infof(struct Curl_easy * data,const char * fmt,...)130 void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
131 {
132 DEBUGASSERT(!strchr(fmt, '\n'));
133 if(Curl_trc_is_verbose(data)) {
134 va_list ap;
135 va_start(ap, fmt);
136 trc_infof(data, data->state.feat, fmt, ap);
137 va_end(ap);
138 }
139 }
140
Curl_trc_cf_infof(struct Curl_easy * data,struct Curl_cfilter * cf,const char * fmt,...)141 void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
142 const char *fmt, ...)
143 {
144 DEBUGASSERT(cf);
145 if(Curl_trc_cf_is_verbose(cf, data)) {
146 va_list ap;
147 int len = 0;
148 char buffer[MAXINFO + 2];
149 if(data->state.feat)
150 len += msnprintf(buffer + len, MAXINFO - len, "[%s] ",
151 data->state.feat->name);
152 if(cf->sockindex)
153 len += msnprintf(buffer + len, MAXINFO - len, "[%s-%d] ",
154 cf->cft->name, cf->sockindex);
155 else
156 len += msnprintf(buffer + len, MAXINFO - len, "[%s] ", cf->cft->name);
157 va_start(ap, fmt);
158 len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
159 va_end(ap);
160 buffer[len++] = '\n';
161 buffer[len] = '\0';
162 Curl_debug(data, CURLINFO_TEXT, buffer, len);
163 }
164 }
165
166 struct curl_trc_feat Curl_trc_feat_read = {
167 "READ",
168 CURL_LOG_LVL_NONE,
169 };
170 struct curl_trc_feat Curl_trc_feat_write = {
171 "WRITE",
172 CURL_LOG_LVL_NONE,
173 };
174
Curl_trc_read(struct Curl_easy * data,const char * fmt,...)175 void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
176 {
177 DEBUGASSERT(!strchr(fmt, '\n'));
178 if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) {
179 va_list ap;
180 va_start(ap, fmt);
181 trc_infof(data, &Curl_trc_feat_read, fmt, ap);
182 va_end(ap);
183 }
184 }
185
Curl_trc_write(struct Curl_easy * data,const char * fmt,...)186 void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...)
187 {
188 DEBUGASSERT(!strchr(fmt, '\n'));
189 if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) {
190 va_list ap;
191 va_start(ap, fmt);
192 trc_infof(data, &Curl_trc_feat_write, fmt, ap);
193 va_end(ap);
194 }
195 }
196
197 #ifndef CURL_DISABLE_FTP
198 struct curl_trc_feat Curl_trc_feat_ftp = {
199 "FTP",
200 CURL_LOG_LVL_NONE,
201 };
202
Curl_trc_ftp(struct Curl_easy * data,const char * fmt,...)203 void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
204 {
205 DEBUGASSERT(!strchr(fmt, '\n'));
206 if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) {
207 va_list ap;
208 va_start(ap, fmt);
209 trc_infof(data, &Curl_trc_feat_ftp, fmt, ap);
210 va_end(ap);
211 }
212 }
213 #endif /* !CURL_DISABLE_FTP */
214
215 static struct curl_trc_feat *trc_feats[] = {
216 &Curl_trc_feat_read,
217 &Curl_trc_feat_write,
218 #ifndef CURL_DISABLE_FTP
219 &Curl_trc_feat_ftp,
220 #endif
221 #ifndef CURL_DISABLE_DOH
222 &Curl_doh_trc,
223 #endif
224 NULL,
225 };
226
227 static struct Curl_cftype *cf_types[] = {
228 &Curl_cft_tcp,
229 &Curl_cft_udp,
230 &Curl_cft_unix,
231 &Curl_cft_tcp_accept,
232 &Curl_cft_happy_eyeballs,
233 &Curl_cft_setup,
234 #ifdef USE_NGHTTP2
235 &Curl_cft_nghttp2,
236 #endif
237 #ifdef USE_SSL
238 &Curl_cft_ssl,
239 #ifndef CURL_DISABLE_PROXY
240 &Curl_cft_ssl_proxy,
241 #endif
242 #endif
243 #if !defined(CURL_DISABLE_PROXY)
244 #if !defined(CURL_DISABLE_HTTP)
245 &Curl_cft_h1_proxy,
246 #ifdef USE_NGHTTP2
247 &Curl_cft_h2_proxy,
248 #endif
249 &Curl_cft_http_proxy,
250 #endif /* !CURL_DISABLE_HTTP */
251 &Curl_cft_haproxy,
252 &Curl_cft_socks_proxy,
253 #endif /* !CURL_DISABLE_PROXY */
254 #ifdef USE_HTTP3
255 &Curl_cft_http3,
256 #endif
257 #if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
258 &Curl_cft_http_connect,
259 #endif
260 NULL,
261 };
262
Curl_trc_opt(const char * config)263 CURLcode Curl_trc_opt(const char *config)
264 {
265 char *token, *tok_buf, *tmp;
266 size_t i;
267 int lvl;
268
269 tmp = strdup(config);
270 if(!tmp)
271 return CURLE_OUT_OF_MEMORY;
272
273 token = strtok_r(tmp, ", ", &tok_buf);
274 while(token) {
275 switch(*token) {
276 case '-':
277 lvl = CURL_LOG_LVL_NONE;
278 ++token;
279 break;
280 case '+':
281 lvl = CURL_LOG_LVL_INFO;
282 ++token;
283 break;
284 default:
285 lvl = CURL_LOG_LVL_INFO;
286 break;
287 }
288 for(i = 0; cf_types[i]; ++i) {
289 if(strcasecompare(token, "all")) {
290 cf_types[i]->log_level = lvl;
291 }
292 else if(strcasecompare(token, cf_types[i]->name)) {
293 cf_types[i]->log_level = lvl;
294 break;
295 }
296 }
297 for(i = 0; trc_feats[i]; ++i) {
298 if(strcasecompare(token, "all")) {
299 trc_feats[i]->log_level = lvl;
300 }
301 else if(strcasecompare(token, trc_feats[i]->name)) {
302 trc_feats[i]->log_level = lvl;
303 break;
304 }
305 }
306 token = strtok_r(NULL, ", ", &tok_buf);
307 }
308 free(tmp);
309 return CURLE_OK;
310 }
311
Curl_trc_init(void)312 CURLcode Curl_trc_init(void)
313 {
314 #ifdef DEBUGBUILD
315 /* WIP: we use the auto-init from an env var only in DEBUG builds for
316 * convenience. */
317 const char *config = getenv("CURL_DEBUG");
318 if(config) {
319 return Curl_trc_opt(config);
320 }
321 #endif /* DEBUGBUILD */
322 return CURLE_OK;
323 }
324 #else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
325
Curl_trc_init(void)326 CURLcode Curl_trc_init(void)
327 {
328 return CURLE_OK;
329 }
330
331 #endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
332