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