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