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