xref: /curl/lib/getinfo.c (revision b06b3515)
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  */
Curl_initinfo(struct Curl_easy * data)47 CURLcode 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_posttransfer = 0;
57   pro->t_starttransfer = 0;
58   pro->timespent = 0;
59   pro->t_redirect = 0;
60   pro->is_t_startransfer_set = FALSE;
61 
62   info->httpcode = 0;
63   info->httpproxycode = 0;
64   info->httpversion = 0;
65   info->filetime = -1; /* -1 is an illegal time and thus means unknown */
66   info->timecond = FALSE;
67 
68   info->header_size = 0;
69   info->request_size = 0;
70   info->proxyauthavail = 0;
71   info->httpauthavail = 0;
72   info->numconnects = 0;
73 
74   free(info->contenttype);
75   info->contenttype = NULL;
76 
77   free(info->wouldredirect);
78   info->wouldredirect = NULL;
79 
80   memset(&info->primary, 0, sizeof(info->primary));
81   info->primary.remote_port = -1;
82   info->primary.local_port = -1;
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 
getinfo_char(struct Curl_easy * data,CURLINFO info,const char ** param_charp)94 static 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.primary.remote_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.primary.local_ip;
162     break;
163   case CURLINFO_RTSP_SESSION_ID:
164 #ifndef CURL_DISABLE_RTSP
165     *param_charp = data->set.str[STRING_RTSP_SESSION_ID];
166 #else
167     *param_charp = NULL;
168 #endif
169     break;
170   case CURLINFO_SCHEME:
171     *param_charp = data->info.conn_scheme;
172     break;
173   case CURLINFO_CAPATH:
174 #ifdef CURL_CA_PATH
175     *param_charp = CURL_CA_PATH;
176 #else
177     *param_charp = NULL;
178 #endif
179     break;
180   case CURLINFO_CAINFO:
181 #ifdef CURL_CA_BUNDLE
182     *param_charp = CURL_CA_BUNDLE;
183 #else
184     *param_charp = NULL;
185 #endif
186     break;
187   default:
188     return CURLE_UNKNOWN_OPTION;
189   }
190 
191   return CURLE_OK;
192 }
193 
getinfo_long(struct Curl_easy * data,CURLINFO info,long * param_longp)194 static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
195                              long *param_longp)
196 {
197   curl_socket_t sockfd;
198 
199   union {
200     unsigned long *to_ulong;
201     long          *to_long;
202   } lptr;
203 
204 #ifdef DEBUGBUILD
205   char *timestr = getenv("CURL_TIME");
206   if(timestr) {
207     unsigned long val = strtoul(timestr, NULL, 10);
208     switch(info) {
209     case CURLINFO_LOCAL_PORT:
210       *param_longp = (long)val;
211       return CURLE_OK;
212     default:
213       break;
214     }
215   }
216   /* use another variable for this to allow different values */
217   timestr = getenv("CURL_DEBUG_SIZE");
218   if(timestr) {
219     unsigned long val = strtoul(timestr, NULL, 10);
220     switch(info) {
221     case CURLINFO_HEADER_SIZE:
222     case CURLINFO_REQUEST_SIZE:
223       *param_longp = (long)val;
224       return CURLE_OK;
225     default:
226       break;
227     }
228   }
229 #endif
230 
231   switch(info) {
232   case CURLINFO_RESPONSE_CODE:
233     *param_longp = data->info.httpcode;
234     break;
235   case CURLINFO_HTTP_CONNECTCODE:
236     *param_longp = data->info.httpproxycode;
237     break;
238   case CURLINFO_FILETIME:
239     if(data->info.filetime > LONG_MAX)
240       *param_longp = LONG_MAX;
241 #if !defined(MSDOS) && !defined(__AMIGA__)
242     else if(data->info.filetime < LONG_MIN)
243       *param_longp = LONG_MIN;
244 #endif
245     else
246       *param_longp = (long)data->info.filetime;
247     break;
248   case CURLINFO_HEADER_SIZE:
249     *param_longp = (long)data->info.header_size;
250     break;
251   case CURLINFO_REQUEST_SIZE:
252     *param_longp = (long)data->info.request_size;
253     break;
254   case CURLINFO_SSL_VERIFYRESULT:
255     *param_longp = data->set.ssl.certverifyresult;
256     break;
257   case CURLINFO_PROXY_SSL_VERIFYRESULT:
258 #ifndef CURL_DISABLE_PROXY
259     *param_longp = data->set.proxy_ssl.certverifyresult;
260 #else
261     *param_longp = 0;
262 #endif
263     break;
264   case CURLINFO_REDIRECT_COUNT:
265     *param_longp = data->state.followlocation;
266     break;
267   case CURLINFO_HTTPAUTH_AVAIL:
268     lptr.to_long = param_longp;
269     *lptr.to_ulong = data->info.httpauthavail;
270     break;
271   case CURLINFO_PROXYAUTH_AVAIL:
272     lptr.to_long = param_longp;
273     *lptr.to_ulong = data->info.proxyauthavail;
274     break;
275   case CURLINFO_OS_ERRNO:
276     *param_longp = data->state.os_errno;
277     break;
278   case CURLINFO_NUM_CONNECTS:
279     *param_longp = data->info.numconnects;
280     break;
281   case CURLINFO_LASTSOCKET:
282     sockfd = Curl_getconnectinfo(data, NULL);
283 
284     /* note: this is not a good conversion for systems with 64-bit sockets and
285        32-bit longs */
286     if(sockfd != CURL_SOCKET_BAD)
287       *param_longp = (long)sockfd;
288     else
289       /* this interface is documented to return -1 in case of badness, which
290          may not be the same as the CURL_SOCKET_BAD value */
291       *param_longp = -1;
292     break;
293   case CURLINFO_PRIMARY_PORT:
294     /* Return the (remote) port of the most recent (primary) connection */
295     *param_longp = data->info.primary.remote_port;
296     break;
297   case CURLINFO_LOCAL_PORT:
298     /* Return the local port of the most recent (primary) connection */
299     *param_longp = data->info.primary.local_port;
300     break;
301   case CURLINFO_PROXY_ERROR:
302     *param_longp = (long)data->info.pxcode;
303     break;
304   case CURLINFO_CONDITION_UNMET:
305     if(data->info.httpcode == 304)
306       *param_longp = 1L;
307     else
308       /* return if the condition prevented the document to get transferred */
309       *param_longp = data->info.timecond ? 1L : 0L;
310     break;
311 #ifndef CURL_DISABLE_RTSP
312   case CURLINFO_RTSP_CLIENT_CSEQ:
313     *param_longp = data->state.rtsp_next_client_CSeq;
314     break;
315   case CURLINFO_RTSP_SERVER_CSEQ:
316     *param_longp = data->state.rtsp_next_server_CSeq;
317     break;
318   case CURLINFO_RTSP_CSEQ_RECV:
319     *param_longp = data->state.rtsp_CSeq_recv;
320     break;
321 #else
322   case CURLINFO_RTSP_CLIENT_CSEQ:
323   case CURLINFO_RTSP_SERVER_CSEQ:
324   case CURLINFO_RTSP_CSEQ_RECV:
325     *param_longp = 0;
326     break;
327 #endif
328   case CURLINFO_HTTP_VERSION:
329     switch(data->info.httpversion) {
330     case 10:
331       *param_longp = CURL_HTTP_VERSION_1_0;
332       break;
333     case 11:
334       *param_longp = CURL_HTTP_VERSION_1_1;
335       break;
336     case 20:
337       *param_longp = CURL_HTTP_VERSION_2_0;
338       break;
339     case 30:
340       *param_longp = CURL_HTTP_VERSION_3;
341       break;
342     default:
343       *param_longp = CURL_HTTP_VERSION_NONE;
344       break;
345     }
346     break;
347   case CURLINFO_PROTOCOL:
348     *param_longp = (long)data->info.conn_protocol;
349     break;
350   case CURLINFO_USED_PROXY:
351     *param_longp =
352 #ifdef CURL_DISABLE_PROXY
353       0
354 #else
355       data->info.used_proxy
356 #endif
357       ;
358     break;
359   default:
360     return CURLE_UNKNOWN_OPTION;
361   }
362 
363   return CURLE_OK;
364 }
365 
366 #define DOUBLE_SECS(x) (double)(x)/1000000
367 
getinfo_offt(struct Curl_easy * data,CURLINFO info,curl_off_t * param_offt)368 static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
369                              curl_off_t *param_offt)
370 {
371 #ifdef DEBUGBUILD
372   char *timestr = getenv("CURL_TIME");
373   if(timestr) {
374     unsigned long val = strtoul(timestr, NULL, 10);
375     switch(info) {
376     case CURLINFO_TOTAL_TIME_T:
377     case CURLINFO_NAMELOOKUP_TIME_T:
378     case CURLINFO_CONNECT_TIME_T:
379     case CURLINFO_APPCONNECT_TIME_T:
380     case CURLINFO_PRETRANSFER_TIME_T:
381     case CURLINFO_POSTTRANSFER_TIME_T:
382     case CURLINFO_QUEUE_TIME_T:
383     case CURLINFO_STARTTRANSFER_TIME_T:
384     case CURLINFO_REDIRECT_TIME_T:
385     case CURLINFO_SPEED_DOWNLOAD_T:
386     case CURLINFO_SPEED_UPLOAD_T:
387       *param_offt = (curl_off_t)val;
388       return CURLE_OK;
389     default:
390       break;
391     }
392   }
393 #endif
394   switch(info) {
395   case CURLINFO_FILETIME_T:
396     *param_offt = (curl_off_t)data->info.filetime;
397     break;
398   case CURLINFO_SIZE_UPLOAD_T:
399     *param_offt = data->progress.ul.cur_size;
400     break;
401   case CURLINFO_SIZE_DOWNLOAD_T:
402     *param_offt = data->progress.dl.cur_size;
403     break;
404   case CURLINFO_SPEED_DOWNLOAD_T:
405     *param_offt = data->progress.dl.speed;
406     break;
407   case CURLINFO_SPEED_UPLOAD_T:
408     *param_offt = data->progress.ul.speed;
409     break;
410   case CURLINFO_CONTENT_LENGTH_DOWNLOAD_T:
411     *param_offt = (data->progress.flags & PGRS_DL_SIZE_KNOWN) ?
412       data->progress.dl.total_size : -1;
413     break;
414   case CURLINFO_CONTENT_LENGTH_UPLOAD_T:
415     *param_offt = (data->progress.flags & PGRS_UL_SIZE_KNOWN) ?
416       data->progress.ul.total_size : -1;
417     break;
418    case CURLINFO_TOTAL_TIME_T:
419     *param_offt = data->progress.timespent;
420     break;
421   case CURLINFO_NAMELOOKUP_TIME_T:
422     *param_offt = data->progress.t_nslookup;
423     break;
424   case CURLINFO_CONNECT_TIME_T:
425     *param_offt = data->progress.t_connect;
426     break;
427   case CURLINFO_APPCONNECT_TIME_T:
428     *param_offt = data->progress.t_appconnect;
429     break;
430   case CURLINFO_PRETRANSFER_TIME_T:
431     *param_offt = data->progress.t_pretransfer;
432     break;
433   case CURLINFO_POSTTRANSFER_TIME_T:
434     *param_offt = data->progress.t_posttransfer;
435     break;
436   case CURLINFO_STARTTRANSFER_TIME_T:
437     *param_offt = data->progress.t_starttransfer;
438     break;
439   case CURLINFO_QUEUE_TIME_T:
440     *param_offt = data->progress.t_postqueue;
441     break;
442   case CURLINFO_REDIRECT_TIME_T:
443     *param_offt = data->progress.t_redirect;
444     break;
445   case CURLINFO_RETRY_AFTER:
446     *param_offt = data->info.retry_after;
447     break;
448   case CURLINFO_XFER_ID:
449     *param_offt = data->id;
450     break;
451   case CURLINFO_CONN_ID:
452     *param_offt = data->conn ?
453       data->conn->connection_id : data->state.recent_conn_id;
454     break;
455   case CURLINFO_EARLYDATA_SENT_T:
456     *param_offt = data->progress.earlydata_sent;
457     break;
458   default:
459     return CURLE_UNKNOWN_OPTION;
460   }
461 
462   return CURLE_OK;
463 }
464 
getinfo_double(struct Curl_easy * data,CURLINFO info,double * param_doublep)465 static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info,
466                                double *param_doublep)
467 {
468 #ifdef DEBUGBUILD
469   char *timestr = getenv("CURL_TIME");
470   if(timestr) {
471     unsigned long val = strtoul(timestr, NULL, 10);
472     switch(info) {
473     case CURLINFO_TOTAL_TIME:
474     case CURLINFO_NAMELOOKUP_TIME:
475     case CURLINFO_CONNECT_TIME:
476     case CURLINFO_APPCONNECT_TIME:
477     case CURLINFO_PRETRANSFER_TIME:
478     case CURLINFO_STARTTRANSFER_TIME:
479     case CURLINFO_REDIRECT_TIME:
480     case CURLINFO_SPEED_DOWNLOAD:
481     case CURLINFO_SPEED_UPLOAD:
482       *param_doublep = (double)val;
483       return CURLE_OK;
484     default:
485       break;
486     }
487   }
488 #endif
489   switch(info) {
490   case CURLINFO_TOTAL_TIME:
491     *param_doublep = DOUBLE_SECS(data->progress.timespent);
492     break;
493   case CURLINFO_NAMELOOKUP_TIME:
494     *param_doublep = DOUBLE_SECS(data->progress.t_nslookup);
495     break;
496   case CURLINFO_CONNECT_TIME:
497     *param_doublep = DOUBLE_SECS(data->progress.t_connect);
498     break;
499   case CURLINFO_APPCONNECT_TIME:
500     *param_doublep = DOUBLE_SECS(data->progress.t_appconnect);
501     break;
502   case CURLINFO_PRETRANSFER_TIME:
503     *param_doublep = DOUBLE_SECS(data->progress.t_pretransfer);
504     break;
505   case CURLINFO_STARTTRANSFER_TIME:
506     *param_doublep = DOUBLE_SECS(data->progress.t_starttransfer);
507     break;
508   case CURLINFO_SIZE_UPLOAD:
509     *param_doublep = (double)data->progress.ul.cur_size;
510     break;
511   case CURLINFO_SIZE_DOWNLOAD:
512     *param_doublep = (double)data->progress.dl.cur_size;
513     break;
514   case CURLINFO_SPEED_DOWNLOAD:
515     *param_doublep = (double)data->progress.dl.speed;
516     break;
517   case CURLINFO_SPEED_UPLOAD:
518     *param_doublep = (double)data->progress.ul.speed;
519     break;
520   case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
521     *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN) ?
522       (double)data->progress.dl.total_size : -1;
523     break;
524   case CURLINFO_CONTENT_LENGTH_UPLOAD:
525     *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN) ?
526       (double)data->progress.ul.total_size : -1;
527     break;
528   case CURLINFO_REDIRECT_TIME:
529     *param_doublep = DOUBLE_SECS(data->progress.t_redirect);
530     break;
531 
532   default:
533     return CURLE_UNKNOWN_OPTION;
534   }
535 
536   return CURLE_OK;
537 }
538 
getinfo_slist(struct Curl_easy * data,CURLINFO info,struct curl_slist ** param_slistp)539 static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info,
540                               struct curl_slist **param_slistp)
541 {
542   union {
543     struct curl_certinfo *to_certinfo;
544     struct curl_slist    *to_slist;
545   } ptr;
546 
547   switch(info) {
548   case CURLINFO_SSL_ENGINES:
549     *param_slistp = Curl_ssl_engines_list(data);
550     break;
551   case CURLINFO_COOKIELIST:
552     *param_slistp = Curl_cookie_list(data);
553     break;
554   case CURLINFO_CERTINFO:
555     /* Return the a pointer to the certinfo struct. Not really an slist
556        pointer but we can pretend it is here */
557     ptr.to_certinfo = &data->info.certs;
558     *param_slistp = ptr.to_slist;
559     break;
560   case CURLINFO_TLS_SESSION:
561   case CURLINFO_TLS_SSL_PTR:
562     {
563       struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **)
564                                           param_slistp;
565       struct curl_tlssessioninfo *tsi = &data->tsi;
566 #ifdef USE_SSL
567       struct connectdata *conn = data->conn;
568 #endif
569 
570       *tsip = tsi;
571       tsi->backend = Curl_ssl_backend();
572       tsi->internals = NULL;
573 
574 #ifdef USE_SSL
575       if(conn && tsi->backend != CURLSSLBACKEND_NONE) {
576         tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0);
577       }
578 #endif
579     }
580     break;
581   default:
582     return CURLE_UNKNOWN_OPTION;
583   }
584 
585   return CURLE_OK;
586 }
587 
getinfo_socket(struct Curl_easy * data,CURLINFO info,curl_socket_t * param_socketp)588 static CURLcode getinfo_socket(struct Curl_easy *data, CURLINFO info,
589                                curl_socket_t *param_socketp)
590 {
591   switch(info) {
592   case CURLINFO_ACTIVESOCKET:
593     *param_socketp = Curl_getconnectinfo(data, NULL);
594     break;
595   default:
596     return CURLE_UNKNOWN_OPTION;
597   }
598 
599   return CURLE_OK;
600 }
601 
Curl_getinfo(struct Curl_easy * data,CURLINFO info,...)602 CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...)
603 {
604   va_list arg;
605   long *param_longp = NULL;
606   double *param_doublep = NULL;
607   curl_off_t *param_offt = NULL;
608   const char **param_charp = NULL;
609   struct curl_slist **param_slistp = NULL;
610   curl_socket_t *param_socketp = NULL;
611   int type;
612   CURLcode result = CURLE_UNKNOWN_OPTION;
613 
614   if(!data)
615     return CURLE_BAD_FUNCTION_ARGUMENT;
616 
617   va_start(arg, info);
618 
619   type = CURLINFO_TYPEMASK & (int)info;
620   switch(type) {
621   case CURLINFO_STRING:
622     param_charp = va_arg(arg, const char **);
623     if(param_charp)
624       result = getinfo_char(data, info, param_charp);
625     break;
626   case CURLINFO_LONG:
627     param_longp = va_arg(arg, long *);
628     if(param_longp)
629       result = getinfo_long(data, info, param_longp);
630     break;
631   case CURLINFO_DOUBLE:
632     param_doublep = va_arg(arg, double *);
633     if(param_doublep)
634       result = getinfo_double(data, info, param_doublep);
635     break;
636   case CURLINFO_OFF_T:
637     param_offt = va_arg(arg, curl_off_t *);
638     if(param_offt)
639       result = getinfo_offt(data, info, param_offt);
640     break;
641   case CURLINFO_SLIST:
642     param_slistp = va_arg(arg, struct curl_slist **);
643     if(param_slistp)
644       result = getinfo_slist(data, info, param_slistp);
645     break;
646   case CURLINFO_SOCKET:
647     param_socketp = va_arg(arg, curl_socket_t *);
648     if(param_socketp)
649       result = getinfo_socket(data, info, param_socketp);
650     break;
651   default:
652     break;
653   }
654 
655   va_end(arg);
656 
657   return result;
658 }
659