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