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