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