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