xref: /third_party/curl/lib/getinfo.c (revision 13498266)
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
25#include "curl_setup.h"
26
27#include <curl/curl.h>
28
29#include "urldata.h"
30#include "getinfo.h"
31
32#include "vtls/vtls.h"
33#include "connect.h" /* Curl_getconnectinfo() */
34#include "progress.h"
35
36/* The last #include files should be: */
37#include "curl_memory.h"
38#include "memdebug.h"
39
40/*
41 * Initialize statistical and informational data.
42 *
43 * This function is called in curl_easy_reset, curl_easy_duphandle and at the
44 * beginning of a perform session. It must reset the session-info variables,
45 * in particular all variables in struct PureInfo.
46 */
47CURLcode Curl_initinfo(struct Curl_easy *data)
48{
49  struct Progress *pro = &data->progress;
50  struct PureInfo *info = &data->info;
51
52  pro->t_nslookup = 0;
53  pro->t_connect = 0;
54  pro->t_appconnect = 0;
55  pro->t_pretransfer = 0;
56  pro->t_starttransfer = 0;
57  pro->timespent = 0;
58  pro->t_redirect = 0;
59  pro->is_t_startransfer_set = false;
60
61  info->httpcode = 0;
62  info->httpproxycode = 0;
63  info->httpversion = 0;
64  info->filetime = -1; /* -1 is an illegal time and thus means unknown */
65  info->timecond = FALSE;
66
67  info->header_size = 0;
68  info->request_size = 0;
69  info->proxyauthavail = 0;
70  info->httpauthavail = 0;
71  info->numconnects = 0;
72
73  free(info->contenttype);
74  info->contenttype = NULL;
75
76  free(info->wouldredirect);
77  info->wouldredirect = NULL;
78
79  info->conn_primary_ip[0] = '\0';
80  info->conn_local_ip[0] = '\0';
81  info->conn_primary_port = 0;
82  info->conn_local_port = 0;
83  info->retry_after = 0;
84
85  info->conn_scheme = 0;
86  info->conn_protocol = 0;
87
88#ifdef USE_SSL
89  Curl_ssl_free_certinfo(data);
90#endif
91  return CURLE_OK;
92}
93
94static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info,
95                             const char **param_charp)
96{
97  switch(info) {
98  case CURLINFO_EFFECTIVE_URL:
99    *param_charp = data->state.url?data->state.url:(char *)"";
100    break;
101  case CURLINFO_EFFECTIVE_METHOD: {
102    const char *m = data->set.str[STRING_CUSTOMREQUEST];
103    if(!m) {
104      if(data->set.opt_no_body)
105        m = "HEAD";
106#ifndef CURL_DISABLE_HTTP
107      else {
108        switch(data->state.httpreq) {
109        case HTTPREQ_POST:
110        case HTTPREQ_POST_FORM:
111        case HTTPREQ_POST_MIME:
112          m = "POST";
113          break;
114        case HTTPREQ_PUT:
115          m = "PUT";
116          break;
117        default: /* this should never happen */
118        case HTTPREQ_GET:
119          m = "GET";
120          break;
121        case HTTPREQ_HEAD:
122          m = "HEAD";
123          break;
124        }
125      }
126#endif
127    }
128    *param_charp = m;
129  }
130    break;
131  case CURLINFO_CONTENT_TYPE:
132    *param_charp = data->info.contenttype;
133    break;
134  case CURLINFO_PRIVATE:
135    *param_charp = (char *) data->set.private_data;
136    break;
137  case CURLINFO_FTP_ENTRY_PATH:
138    /* Return the entrypath string from the most recent connection.
139       This pointer was copied from the connectdata structure by FTP.
140       The actual string may be free()ed by subsequent libcurl calls so
141       it must be copied to a safer area before the next libcurl call.
142       Callers must never free it themselves. */
143    *param_charp = data->state.most_recent_ftp_entrypath;
144    break;
145  case CURLINFO_REDIRECT_URL:
146    /* Return the URL this request would have been redirected to if that
147       option had been enabled! */
148    *param_charp = data->info.wouldredirect;
149    break;
150  case CURLINFO_REFERER:
151    /* Return the referrer header for this request, or NULL if unset */
152    *param_charp = data->state.referer;
153    break;
154  case CURLINFO_PRIMARY_IP:
155    /* Return the ip address of the most recent (primary) connection */
156    *param_charp = data->info.conn_primary_ip;
157    break;
158  case CURLINFO_LOCAL_IP:
159    /* Return the source/local ip address of the most recent (primary)
160       connection */
161    *param_charp = data->info.conn_local_ip;
162    break;
163  case CURLINFO_RTSP_SESSION_ID:
164    *param_charp = data->set.str[STRING_RTSP_SESSION_ID];
165    break;
166  case CURLINFO_SCHEME:
167    *param_charp = data->info.conn_scheme;
168    break;
169  case CURLINFO_CAPATH:
170#ifdef CURL_CA_PATH
171    *param_charp = CURL_CA_PATH;
172#else
173    *param_charp = NULL;
174#endif
175    break;
176  case CURLINFO_CAINFO:
177#ifdef CURL_CA_BUNDLE
178    *param_charp = CURL_CA_BUNDLE;
179#else
180    *param_charp = NULL;
181#endif
182    break;
183
184  default:
185    return CURLE_UNKNOWN_OPTION;
186  }
187
188  return CURLE_OK;
189}
190
191static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
192                             long *param_longp)
193{
194  curl_socket_t sockfd;
195
196  union {
197    unsigned long *to_ulong;
198    long          *to_long;
199  } lptr;
200
201#ifdef DEBUGBUILD
202  char *timestr = getenv("CURL_TIME");
203  if(timestr) {
204    unsigned long val = strtol(timestr, NULL, 10);
205    switch(info) {
206    case CURLINFO_LOCAL_PORT:
207      *param_longp = (long)val;
208      return CURLE_OK;
209    default:
210      break;
211    }
212  }
213  /* use another variable for this to allow different values */
214  timestr = getenv("CURL_DEBUG_SIZE");
215  if(timestr) {
216    unsigned long val = strtol(timestr, NULL, 10);
217    switch(info) {
218    case CURLINFO_HEADER_SIZE:
219    case CURLINFO_REQUEST_SIZE:
220      *param_longp = (long)val;
221      return CURLE_OK;
222    default:
223      break;
224    }
225  }
226#endif
227
228  switch(info) {
229  case CURLINFO_RESPONSE_CODE:
230    *param_longp = data->info.httpcode;
231    break;
232  case CURLINFO_HTTP_CONNECTCODE:
233    *param_longp = data->info.httpproxycode;
234    break;
235  case CURLINFO_FILETIME:
236    if(data->info.filetime > LONG_MAX)
237      *param_longp = LONG_MAX;
238    else if(data->info.filetime < LONG_MIN)
239      *param_longp = LONG_MIN;
240    else
241      *param_longp = (long)data->info.filetime;
242    break;
243  case CURLINFO_HEADER_SIZE:
244    *param_longp = (long)data->info.header_size;
245    break;
246  case CURLINFO_REQUEST_SIZE:
247    *param_longp = (long)data->info.request_size;
248    break;
249  case CURLINFO_SSL_VERIFYRESULT:
250    *param_longp = data->set.ssl.certverifyresult;
251    break;
252#ifndef CURL_DISABLE_PROXY
253  case CURLINFO_PROXY_SSL_VERIFYRESULT:
254    *param_longp = data->set.proxy_ssl.certverifyresult;
255    break;
256#endif
257  case CURLINFO_REDIRECT_COUNT:
258    *param_longp = data->state.followlocation;
259    break;
260  case CURLINFO_HTTPAUTH_AVAIL:
261    lptr.to_long = param_longp;
262    *lptr.to_ulong = data->info.httpauthavail;
263    break;
264  case CURLINFO_PROXYAUTH_AVAIL:
265    lptr.to_long = param_longp;
266    *lptr.to_ulong = data->info.proxyauthavail;
267    break;
268  case CURLINFO_OS_ERRNO:
269    *param_longp = data->state.os_errno;
270    break;
271  case CURLINFO_NUM_CONNECTS:
272    *param_longp = data->info.numconnects;
273    break;
274  case CURLINFO_LASTSOCKET:
275    sockfd = Curl_getconnectinfo(data, NULL);
276
277    /* note: this is not a good conversion for systems with 64 bit sockets and
278       32 bit longs */
279    if(sockfd != CURL_SOCKET_BAD)
280      *param_longp = (long)sockfd;
281    else
282      /* this interface is documented to return -1 in case of badness, which
283         may not be the same as the CURL_SOCKET_BAD value */
284      *param_longp = -1;
285    break;
286  case CURLINFO_PRIMARY_PORT:
287    /* Return the (remote) port of the most recent (primary) connection */
288    *param_longp = data->info.conn_primary_port;
289    break;
290  case CURLINFO_LOCAL_PORT:
291    /* Return the local port of the most recent (primary) connection */
292    *param_longp = data->info.conn_local_port;
293    break;
294  case CURLINFO_PROXY_ERROR:
295    *param_longp = (long)data->info.pxcode;
296    break;
297  case CURLINFO_CONDITION_UNMET:
298    if(data->info.httpcode == 304)
299      *param_longp = 1L;
300    else
301      /* return if the condition prevented the document to get transferred */
302      *param_longp = data->info.timecond ? 1L : 0L;
303    break;
304#ifndef CURL_DISABLE_RTSP
305  case CURLINFO_RTSP_CLIENT_CSEQ:
306    *param_longp = data->state.rtsp_next_client_CSeq;
307    break;
308  case CURLINFO_RTSP_SERVER_CSEQ:
309    *param_longp = data->state.rtsp_next_server_CSeq;
310    break;
311  case CURLINFO_RTSP_CSEQ_RECV:
312    *param_longp = data->state.rtsp_CSeq_recv;
313    break;
314#endif
315  case CURLINFO_HTTP_VERSION:
316    switch(data->info.httpversion) {
317    case 10:
318      *param_longp = CURL_HTTP_VERSION_1_0;
319      break;
320    case 11:
321      *param_longp = CURL_HTTP_VERSION_1_1;
322      break;
323    case 20:
324      *param_longp = CURL_HTTP_VERSION_2_0;
325      break;
326    case 30:
327      *param_longp = CURL_HTTP_VERSION_3;
328      break;
329    default:
330      *param_longp = CURL_HTTP_VERSION_NONE;
331      break;
332    }
333    break;
334  case CURLINFO_PROTOCOL:
335    *param_longp = data->info.conn_protocol;
336    break;
337  default:
338    return CURLE_UNKNOWN_OPTION;
339  }
340
341  return CURLE_OK;
342}
343
344#define DOUBLE_SECS(x) (double)(x)/1000000
345
346static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
347                             curl_off_t *param_offt)
348{
349#ifdef DEBUGBUILD
350  char *timestr = getenv("CURL_TIME");
351  if(timestr) {
352    unsigned long val = strtol(timestr, NULL, 10);
353    switch(info) {
354    case CURLINFO_TOTAL_TIME_T:
355    case CURLINFO_NAMELOOKUP_TIME_T:
356    case CURLINFO_CONNECT_TIME_T:
357    case CURLINFO_APPCONNECT_TIME_T:
358    case CURLINFO_PRETRANSFER_TIME_T:
359    case CURLINFO_STARTTRANSFER_TIME_T:
360    case CURLINFO_REDIRECT_TIME_T:
361    case CURLINFO_SPEED_DOWNLOAD_T:
362    case CURLINFO_SPEED_UPLOAD_T:
363      *param_offt = (curl_off_t)val;
364      return CURLE_OK;
365    default:
366      break;
367    }
368  }
369#endif
370  switch(info) {
371  case CURLINFO_FILETIME_T:
372    *param_offt = (curl_off_t)data->info.filetime;
373    break;
374  case CURLINFO_SIZE_UPLOAD_T:
375    *param_offt = data->progress.uploaded;
376    break;
377  case CURLINFO_SIZE_DOWNLOAD_T:
378    *param_offt = data->progress.downloaded;
379    break;
380  case CURLINFO_SPEED_DOWNLOAD_T:
381    *param_offt = data->progress.dlspeed;
382    break;
383  case CURLINFO_SPEED_UPLOAD_T:
384    *param_offt = data->progress.ulspeed;
385    break;
386  case CURLINFO_CONTENT_LENGTH_DOWNLOAD_T:
387    *param_offt = (data->progress.flags & PGRS_DL_SIZE_KNOWN)?
388      data->progress.size_dl:-1;
389    break;
390  case CURLINFO_CONTENT_LENGTH_UPLOAD_T:
391    *param_offt = (data->progress.flags & PGRS_UL_SIZE_KNOWN)?
392      data->progress.size_ul:-1;
393    break;
394   case CURLINFO_TOTAL_TIME_T:
395    *param_offt = data->progress.timespent;
396    break;
397  case CURLINFO_NAMELOOKUP_TIME_T:
398    *param_offt = data->progress.t_nslookup;
399    break;
400  case CURLINFO_CONNECT_TIME_T:
401    *param_offt = data->progress.t_connect;
402    break;
403  case CURLINFO_APPCONNECT_TIME_T:
404    *param_offt = data->progress.t_appconnect;
405    break;
406  case CURLINFO_PRETRANSFER_TIME_T:
407    *param_offt = data->progress.t_pretransfer;
408    break;
409  case CURLINFO_STARTTRANSFER_TIME_T:
410    *param_offt = data->progress.t_starttransfer;
411    break;
412  case CURLINFO_QUEUE_TIME_T:
413    *param_offt = data->progress.t_postqueue;
414    break;
415  case CURLINFO_REDIRECT_TIME_T:
416    *param_offt = data->progress.t_redirect;
417    break;
418  case CURLINFO_RETRY_AFTER:
419    *param_offt = data->info.retry_after;
420    break;
421  case CURLINFO_XFER_ID:
422    *param_offt = data->id;
423    break;
424  case CURLINFO_CONN_ID:
425    *param_offt = data->conn?
426      data->conn->connection_id : data->state.recent_conn_id;
427    break;
428  default:
429    return CURLE_UNKNOWN_OPTION;
430  }
431
432  return CURLE_OK;
433}
434
435static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info,
436                               double *param_doublep)
437{
438#ifdef DEBUGBUILD
439  char *timestr = getenv("CURL_TIME");
440  if(timestr) {
441    unsigned long val = strtol(timestr, NULL, 10);
442    switch(info) {
443    case CURLINFO_TOTAL_TIME:
444    case CURLINFO_NAMELOOKUP_TIME:
445    case CURLINFO_CONNECT_TIME:
446    case CURLINFO_APPCONNECT_TIME:
447    case CURLINFO_PRETRANSFER_TIME:
448    case CURLINFO_STARTTRANSFER_TIME:
449    case CURLINFO_REDIRECT_TIME:
450    case CURLINFO_SPEED_DOWNLOAD:
451    case CURLINFO_SPEED_UPLOAD:
452      *param_doublep = (double)val;
453      return CURLE_OK;
454    default:
455      break;
456    }
457  }
458#endif
459  switch(info) {
460  case CURLINFO_TOTAL_TIME:
461    *param_doublep = DOUBLE_SECS(data->progress.timespent);
462    break;
463  case CURLINFO_NAMELOOKUP_TIME:
464    *param_doublep = DOUBLE_SECS(data->progress.t_nslookup);
465    break;
466  case CURLINFO_CONNECT_TIME:
467    *param_doublep = DOUBLE_SECS(data->progress.t_connect);
468    break;
469  case CURLINFO_APPCONNECT_TIME:
470    *param_doublep = DOUBLE_SECS(data->progress.t_appconnect);
471    break;
472  case CURLINFO_PRETRANSFER_TIME:
473    *param_doublep = DOUBLE_SECS(data->progress.t_pretransfer);
474    break;
475  case CURLINFO_STARTTRANSFER_TIME:
476    *param_doublep = DOUBLE_SECS(data->progress.t_starttransfer);
477    break;
478  case CURLINFO_SIZE_UPLOAD:
479    *param_doublep = (double)data->progress.uploaded;
480    break;
481  case CURLINFO_SIZE_DOWNLOAD:
482    *param_doublep = (double)data->progress.downloaded;
483    break;
484  case CURLINFO_SPEED_DOWNLOAD:
485    *param_doublep = (double)data->progress.dlspeed;
486    break;
487  case CURLINFO_SPEED_UPLOAD:
488    *param_doublep = (double)data->progress.ulspeed;
489    break;
490  case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
491    *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN)?
492      (double)data->progress.size_dl:-1;
493    break;
494  case CURLINFO_CONTENT_LENGTH_UPLOAD:
495    *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN)?
496      (double)data->progress.size_ul:-1;
497    break;
498  case CURLINFO_REDIRECT_TIME:
499    *param_doublep = DOUBLE_SECS(data->progress.t_redirect);
500    break;
501
502  default:
503    return CURLE_UNKNOWN_OPTION;
504  }
505
506  return CURLE_OK;
507}
508
509static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info,
510                              struct curl_slist **param_slistp)
511{
512  union {
513    struct curl_certinfo *to_certinfo;
514    struct curl_slist    *to_slist;
515  } ptr;
516
517  switch(info) {
518  case CURLINFO_SSL_ENGINES:
519    *param_slistp = Curl_ssl_engines_list(data);
520    break;
521  case CURLINFO_COOKIELIST:
522    *param_slistp = Curl_cookie_list(data);
523    break;
524  case CURLINFO_CERTINFO:
525    /* Return the a pointer to the certinfo struct. Not really an slist
526       pointer but we can pretend it is here */
527    ptr.to_certinfo = &data->info.certs;
528    *param_slistp = ptr.to_slist;
529    break;
530  case CURLINFO_TLS_SESSION:
531  case CURLINFO_TLS_SSL_PTR:
532    {
533      struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **)
534                                          param_slistp;
535      struct curl_tlssessioninfo *tsi = &data->tsi;
536#ifdef USE_SSL
537      struct connectdata *conn = data->conn;
538#endif
539
540      *tsip = tsi;
541      tsi->backend = Curl_ssl_backend();
542      tsi->internals = NULL;
543
544#ifdef USE_SSL
545      if(conn && tsi->backend != CURLSSLBACKEND_NONE) {
546        tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0);
547      }
548#endif
549    }
550    break;
551  default:
552    return CURLE_UNKNOWN_OPTION;
553  }
554
555  return CURLE_OK;
556}
557
558static CURLcode getinfo_socket(struct Curl_easy *data, CURLINFO info,
559                               curl_socket_t *param_socketp)
560{
561  switch(info) {
562  case CURLINFO_ACTIVESOCKET:
563    *param_socketp = Curl_getconnectinfo(data, NULL);
564    break;
565  default:
566    return CURLE_UNKNOWN_OPTION;
567  }
568
569  return CURLE_OK;
570}
571
572CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...)
573{
574  va_list arg;
575  long *param_longp = NULL;
576  double *param_doublep = NULL;
577  curl_off_t *param_offt = NULL;
578  const char **param_charp = NULL;
579  struct curl_slist **param_slistp = NULL;
580  curl_socket_t *param_socketp = NULL;
581  int type;
582  CURLcode result = CURLE_UNKNOWN_OPTION;
583
584  if(!data)
585    return CURLE_BAD_FUNCTION_ARGUMENT;
586
587  va_start(arg, info);
588
589  type = CURLINFO_TYPEMASK & (int)info;
590  switch(type) {
591  case CURLINFO_STRING:
592    param_charp = va_arg(arg, const char **);
593    if(param_charp)
594      result = getinfo_char(data, info, param_charp);
595    break;
596  case CURLINFO_LONG:
597    param_longp = va_arg(arg, long *);
598    if(param_longp)
599      result = getinfo_long(data, info, param_longp);
600    break;
601  case CURLINFO_DOUBLE:
602    param_doublep = va_arg(arg, double *);
603    if(param_doublep)
604      result = getinfo_double(data, info, param_doublep);
605    break;
606  case CURLINFO_OFF_T:
607    param_offt = va_arg(arg, curl_off_t *);
608    if(param_offt)
609      result = getinfo_offt(data, info, param_offt);
610    break;
611  case CURLINFO_SLIST:
612    param_slistp = va_arg(arg, struct curl_slist **);
613    if(param_slistp)
614      result = getinfo_slist(data, info, param_slistp);
615    break;
616  case CURLINFO_SOCKET:
617    param_socketp = va_arg(arg, curl_socket_t *);
618    if(param_socketp)
619      result = getinfo_socket(data, info, param_socketp);
620    break;
621  default:
622    break;
623  }
624
625  va_end(arg);
626
627  return result;
628}
629