Home | History | Annotate | Download | only in src
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel (at) 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 
     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 
    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