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