xref: /curl/lib/getinfo.c (revision 962097b8)
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   case CURLINFO_EARLYDATA_SENT_T:
453     *param_offt = data->progress.earlydata_sent;
454     break;
455   default:
456     return CURLE_UNKNOWN_OPTION;
457   }
458 
459   return CURLE_OK;
460 }
461 
getinfo_double(struct Curl_easy * data,CURLINFO info,double * param_doublep)462 static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info,
463                                double *param_doublep)
464 {
465 #ifdef DEBUGBUILD
466   char *timestr = getenv("CURL_TIME");
467   if(timestr) {
468     unsigned long val = strtoul(timestr, NULL, 10);
469     switch(info) {
470     case CURLINFO_TOTAL_TIME:
471     case CURLINFO_NAMELOOKUP_TIME:
472     case CURLINFO_CONNECT_TIME:
473     case CURLINFO_APPCONNECT_TIME:
474     case CURLINFO_PRETRANSFER_TIME:
475     case CURLINFO_STARTTRANSFER_TIME:
476     case CURLINFO_REDIRECT_TIME:
477     case CURLINFO_SPEED_DOWNLOAD:
478     case CURLINFO_SPEED_UPLOAD:
479       *param_doublep = (double)val;
480       return CURLE_OK;
481     default:
482       break;
483     }
484   }
485 #endif
486   switch(info) {
487   case CURLINFO_TOTAL_TIME:
488     *param_doublep = DOUBLE_SECS(data->progress.timespent);
489     break;
490   case CURLINFO_NAMELOOKUP_TIME:
491     *param_doublep = DOUBLE_SECS(data->progress.t_nslookup);
492     break;
493   case CURLINFO_CONNECT_TIME:
494     *param_doublep = DOUBLE_SECS(data->progress.t_connect);
495     break;
496   case CURLINFO_APPCONNECT_TIME:
497     *param_doublep = DOUBLE_SECS(data->progress.t_appconnect);
498     break;
499   case CURLINFO_PRETRANSFER_TIME:
500     *param_doublep = DOUBLE_SECS(data->progress.t_pretransfer);
501     break;
502   case CURLINFO_STARTTRANSFER_TIME:
503     *param_doublep = DOUBLE_SECS(data->progress.t_starttransfer);
504     break;
505   case CURLINFO_SIZE_UPLOAD:
506     *param_doublep = (double)data->progress.ul.cur_size;
507     break;
508   case CURLINFO_SIZE_DOWNLOAD:
509     *param_doublep = (double)data->progress.dl.cur_size;
510     break;
511   case CURLINFO_SPEED_DOWNLOAD:
512     *param_doublep = (double)data->progress.dl.speed;
513     break;
514   case CURLINFO_SPEED_UPLOAD:
515     *param_doublep = (double)data->progress.ul.speed;
516     break;
517   case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
518     *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN) ?
519       (double)data->progress.dl.total_size : -1;
520     break;
521   case CURLINFO_CONTENT_LENGTH_UPLOAD:
522     *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN) ?
523       (double)data->progress.ul.total_size : -1;
524     break;
525   case CURLINFO_REDIRECT_TIME:
526     *param_doublep = DOUBLE_SECS(data->progress.t_redirect);
527     break;
528 
529   default:
530     return CURLE_UNKNOWN_OPTION;
531   }
532 
533   return CURLE_OK;
534 }
535 
getinfo_slist(struct Curl_easy * data,CURLINFO info,struct curl_slist ** param_slistp)536 static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info,
537                               struct curl_slist **param_slistp)
538 {
539   union {
540     struct curl_certinfo *to_certinfo;
541     struct curl_slist    *to_slist;
542   } ptr;
543 
544   switch(info) {
545   case CURLINFO_SSL_ENGINES:
546     *param_slistp = Curl_ssl_engines_list(data);
547     break;
548   case CURLINFO_COOKIELIST:
549     *param_slistp = Curl_cookie_list(data);
550     break;
551   case CURLINFO_CERTINFO:
552     /* Return the a pointer to the certinfo struct. Not really an slist
553        pointer but we can pretend it is here */
554     ptr.to_certinfo = &data->info.certs;
555     *param_slistp = ptr.to_slist;
556     break;
557   case CURLINFO_TLS_SESSION:
558   case CURLINFO_TLS_SSL_PTR:
559     {
560       struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **)
561                                           param_slistp;
562       struct curl_tlssessioninfo *tsi = &data->tsi;
563 #ifdef USE_SSL
564       struct connectdata *conn = data->conn;
565 #endif
566 
567       *tsip = tsi;
568       tsi->backend = Curl_ssl_backend();
569       tsi->internals = NULL;
570 
571 #ifdef USE_SSL
572       if(conn && tsi->backend != CURLSSLBACKEND_NONE) {
573         tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0);
574       }
575 #endif
576     }
577     break;
578   default:
579     return CURLE_UNKNOWN_OPTION;
580   }
581 
582   return CURLE_OK;
583 }
584 
getinfo_socket(struct Curl_easy * data,CURLINFO info,curl_socket_t * param_socketp)585 static CURLcode getinfo_socket(struct Curl_easy *data, CURLINFO info,
586                                curl_socket_t *param_socketp)
587 {
588   switch(info) {
589   case CURLINFO_ACTIVESOCKET:
590     *param_socketp = Curl_getconnectinfo(data, NULL);
591     break;
592   default:
593     return CURLE_UNKNOWN_OPTION;
594   }
595 
596   return CURLE_OK;
597 }
598 
Curl_getinfo(struct Curl_easy * data,CURLINFO info,...)599 CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...)
600 {
601   va_list arg;
602   long *param_longp = NULL;
603   double *param_doublep = NULL;
604   curl_off_t *param_offt = NULL;
605   const char **param_charp = NULL;
606   struct curl_slist **param_slistp = NULL;
607   curl_socket_t *param_socketp = NULL;
608   int type;
609   CURLcode result = CURLE_UNKNOWN_OPTION;
610 
611   if(!data)
612     return CURLE_BAD_FUNCTION_ARGUMENT;
613 
614   va_start(arg, info);
615 
616   type = CURLINFO_TYPEMASK & (int)info;
617   switch(type) {
618   case CURLINFO_STRING:
619     param_charp = va_arg(arg, const char **);
620     if(param_charp)
621       result = getinfo_char(data, info, param_charp);
622     break;
623   case CURLINFO_LONG:
624     param_longp = va_arg(arg, long *);
625     if(param_longp)
626       result = getinfo_long(data, info, param_longp);
627     break;
628   case CURLINFO_DOUBLE:
629     param_doublep = va_arg(arg, double *);
630     if(param_doublep)
631       result = getinfo_double(data, info, param_doublep);
632     break;
633   case CURLINFO_OFF_T:
634     param_offt = va_arg(arg, curl_off_t *);
635     if(param_offt)
636       result = getinfo_offt(data, info, param_offt);
637     break;
638   case CURLINFO_SLIST:
639     param_slistp = va_arg(arg, struct curl_slist **);
640     if(param_slistp)
641       result = getinfo_slist(data, info, param_slistp);
642     break;
643   case CURLINFO_SOCKET:
644     param_socketp = va_arg(arg, curl_socket_t *);
645     if(param_socketp)
646       result = getinfo_socket(data, info, param_socketp);
647     break;
648   default:
649     break;
650   }
651 
652   va_end(arg);
653 
654   return result;
655 }
656