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