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 #include "tool_setup.h"
25
26 #define ENABLE_CURLX_PRINTF
27 /* use our own printf() functions */
28 #include "curlx.h"
29
30 #include "tool_cfgable.h"
31 #include "tool_msgs.h"
32 #include "tool_cb_dbg.h"
33 #include "tool_util.h"
34
35 #include "memdebug.h" /* keep this as LAST include */
36
37 static void dump(const char *timebuf, const char *text,
38 FILE *stream, const unsigned char *ptr, size_t size,
39 trace tracetype, curl_infotype infotype);
40
41 /*
42 ** callback for CURLOPT_DEBUGFUNCTION
43 */
44
tool_debug_cb(CURL * handle,curl_infotype type,char * data,size_t size,void * userdata)45 int tool_debug_cb(CURL *handle, curl_infotype type,
46 char *data, size_t size,
47 void *userdata)
48 {
49 struct OperationConfig *operation = userdata;
50 struct GlobalConfig *config = operation->global;
51 FILE *output = stderr;
52 const char *text;
53 struct timeval tv;
54 char timebuf[20];
55 time_t secs;
56
57 (void)handle; /* not used */
58
59 if(config->tracetime) {
60 struct tm *now;
61 static time_t epoch_offset;
62 static int known_offset;
63 tv = tvnow();
64 if(!known_offset) {
65 epoch_offset = time(NULL) - tv.tv_sec;
66 known_offset = 1;
67 }
68 secs = epoch_offset + tv.tv_sec;
69 /* !checksrc! disable BANNEDFUNC 1 */
70 now = localtime(&secs); /* not thread safe but we don't care */
71 msnprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld ",
72 now->tm_hour, now->tm_min, now->tm_sec, (long)tv.tv_usec);
73 }
74 else
75 timebuf[0] = 0;
76
77 if(!config->trace_stream) {
78 /* open for append */
79 if(!strcmp("-", config->trace_dump))
80 config->trace_stream = stdout;
81 else if(!strcmp("%", config->trace_dump))
82 /* Ok, this is somewhat hackish but we do it undocumented for now */
83 config->trace_stream = stderr;
84 else {
85 config->trace_stream = fopen(config->trace_dump, FOPEN_WRITETEXT);
86 config->trace_fopened = TRUE;
87 }
88 }
89
90 if(config->trace_stream)
91 output = config->trace_stream;
92
93 if(!output) {
94 warnf(config, "Failed to create/open output");
95 return 0;
96 }
97
98 if(config->tracetype == TRACE_PLAIN) {
99 /*
100 * This is the trace look that is similar to what libcurl makes on its
101 * own.
102 */
103 static const char * const s_infotype[] = {
104 "*", "<", ">", "{", "}", "{", "}"
105 };
106 static bool newl = FALSE;
107 static bool traced_data = FALSE;
108
109 switch(type) {
110 case CURLINFO_HEADER_OUT:
111 if(size > 0) {
112 size_t st = 0;
113 size_t i;
114 for(i = 0; i < size - 1; i++) {
115 if(data[i] == '\n') { /* LF */
116 if(!newl) {
117 fprintf(output, "%s%s ", timebuf, s_infotype[type]);
118 }
119 (void)fwrite(data + st, i - st + 1, 1, output);
120 st = i + 1;
121 newl = FALSE;
122 }
123 }
124 if(!newl)
125 fprintf(output, "%s%s ", timebuf, s_infotype[type]);
126 (void)fwrite(data + st, i - st + 1, 1, output);
127 }
128 newl = (size && (data[size - 1] != '\n')) ? TRUE : FALSE;
129 traced_data = FALSE;
130 break;
131 case CURLINFO_TEXT:
132 case CURLINFO_HEADER_IN:
133 if(!newl)
134 fprintf(output, "%s%s ", timebuf, s_infotype[type]);
135 (void)fwrite(data, size, 1, output);
136 newl = (size && (data[size - 1] != '\n')) ? TRUE : FALSE;
137 traced_data = FALSE;
138 break;
139 case CURLINFO_DATA_OUT:
140 case CURLINFO_DATA_IN:
141 case CURLINFO_SSL_DATA_IN:
142 case CURLINFO_SSL_DATA_OUT:
143 if(!traced_data) {
144 /* if the data is output to a tty and we're sending this debug trace
145 to stderr or stdout, we don't display the alert about the data not
146 being shown as the data _is_ shown then just not via this
147 function */
148 if(!config->isatty || ((output != stderr) && (output != stdout))) {
149 if(!newl)
150 fprintf(output, "%s%s ", timebuf, s_infotype[type]);
151 fprintf(output, "[%zu bytes data]\n", size);
152 newl = FALSE;
153 traced_data = TRUE;
154 }
155 }
156 break;
157 default: /* nada */
158 newl = FALSE;
159 traced_data = FALSE;
160 break;
161 }
162
163 return 0;
164 }
165
166 switch(type) {
167 case CURLINFO_TEXT:
168 fprintf(output, "%s== Info: %.*s", timebuf, (int)size, data);
169 /* FALLTHROUGH */
170 default: /* in case a new one is introduced to shock us */
171 return 0;
172
173 case CURLINFO_HEADER_OUT:
174 text = "=> Send header";
175 break;
176 case CURLINFO_DATA_OUT:
177 text = "=> Send data";
178 break;
179 case CURLINFO_HEADER_IN:
180 text = "<= Recv header";
181 break;
182 case CURLINFO_DATA_IN:
183 text = "<= Recv data";
184 break;
185 case CURLINFO_SSL_DATA_IN:
186 text = "<= Recv SSL data";
187 break;
188 case CURLINFO_SSL_DATA_OUT:
189 text = "=> Send SSL data";
190 break;
191 }
192
193 dump(timebuf, text, output, (unsigned char *) data, size, config->tracetype,
194 type);
195 return 0;
196 }
197
dump(const char * timebuf,const char * text,FILE * stream,const unsigned char * ptr,size_t size,trace tracetype,curl_infotype infotype)198 static void dump(const char *timebuf, const char *text,
199 FILE *stream, const unsigned char *ptr, size_t size,
200 trace tracetype, curl_infotype infotype)
201 {
202 size_t i;
203 size_t c;
204
205 unsigned int width = 0x10;
206
207 if(tracetype == TRACE_ASCII)
208 /* without the hex output, we can fit more on screen */
209 width = 0x40;
210
211 fprintf(stream, "%s%s, %zu bytes (0x%zx)\n", timebuf, text, size, size);
212
213 for(i = 0; i < size; i += width) {
214
215 fprintf(stream, "%04zx: ", i);
216
217 if(tracetype == TRACE_BIN) {
218 /* hex not disabled, show it */
219 for(c = 0; c < width; c++)
220 if(i + c < size)
221 fprintf(stream, "%02x ", ptr[i + c]);
222 else
223 fputs(" ", stream);
224 }
225
226 for(c = 0; (c < width) && (i + c < size); c++) {
227 /* check for 0D0A; if found, skip past and start a new line of output */
228 if((tracetype == TRACE_ASCII) &&
229 (i + c + 1 < size) && (ptr[i + c] == 0x0D) &&
230 (ptr[i + c + 1] == 0x0A)) {
231 i += (c + 2 - width);
232 break;
233 }
234 (void)infotype;
235 fprintf(stream, "%c", ((ptr[i + c] >= 0x20) && (ptr[i + c] < 0x7F)) ?
236 ptr[i + c] : UNPRINTABLE_CHAR);
237 /* check again for 0D0A, to avoid an extra \n if it's at width */
238 if((tracetype == TRACE_ASCII) &&
239 (i + c + 2 < size) && (ptr[i + c + 1] == 0x0D) &&
240 (ptr[i + c + 2] == 0x0A)) {
241 i += (c + 3 - width);
242 break;
243 }
244 }
245 fputc('\n', stream); /* newline */
246 }
247 fflush(stream);
248 }
249