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 case CURLINFO_EARLYDATA_SENT_T:
453 *param_offt = data->progress.earlydata_sent;
454 break;
455 default:
456 return CURLE_UNKNOWN_OPTION;
457 }
458
459 return CURLE_OK;
460 }
461
getinfo_double(struct Curl_easy * data,CURLINFO info,double * param_doublep)462 static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info,
463 double *param_doublep)
464 {
465 #ifdef DEBUGBUILD
466 char *timestr = getenv("CURL_TIME");
467 if(timestr) {
468 unsigned long val = strtoul(timestr, NULL, 10);
469 switch(info) {
470 case CURLINFO_TOTAL_TIME:
471 case CURLINFO_NAMELOOKUP_TIME:
472 case CURLINFO_CONNECT_TIME:
473 case CURLINFO_APPCONNECT_TIME:
474 case CURLINFO_PRETRANSFER_TIME:
475 case CURLINFO_STARTTRANSFER_TIME:
476 case CURLINFO_REDIRECT_TIME:
477 case CURLINFO_SPEED_DOWNLOAD:
478 case CURLINFO_SPEED_UPLOAD:
479 *param_doublep = (double)val;
480 return CURLE_OK;
481 default:
482 break;
483 }
484 }
485 #endif
486 switch(info) {
487 case CURLINFO_TOTAL_TIME:
488 *param_doublep = DOUBLE_SECS(data->progress.timespent);
489 break;
490 case CURLINFO_NAMELOOKUP_TIME:
491 *param_doublep = DOUBLE_SECS(data->progress.t_nslookup);
492 break;
493 case CURLINFO_CONNECT_TIME:
494 *param_doublep = DOUBLE_SECS(data->progress.t_connect);
495 break;
496 case CURLINFO_APPCONNECT_TIME:
497 *param_doublep = DOUBLE_SECS(data->progress.t_appconnect);
498 break;
499 case CURLINFO_PRETRANSFER_TIME:
500 *param_doublep = DOUBLE_SECS(data->progress.t_pretransfer);
501 break;
502 case CURLINFO_STARTTRANSFER_TIME:
503 *param_doublep = DOUBLE_SECS(data->progress.t_starttransfer);
504 break;
505 case CURLINFO_SIZE_UPLOAD:
506 *param_doublep = (double)data->progress.ul.cur_size;
507 break;
508 case CURLINFO_SIZE_DOWNLOAD:
509 *param_doublep = (double)data->progress.dl.cur_size;
510 break;
511 case CURLINFO_SPEED_DOWNLOAD:
512 *param_doublep = (double)data->progress.dl.speed;
513 break;
514 case CURLINFO_SPEED_UPLOAD:
515 *param_doublep = (double)data->progress.ul.speed;
516 break;
517 case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
518 *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN) ?
519 (double)data->progress.dl.total_size : -1;
520 break;
521 case CURLINFO_CONTENT_LENGTH_UPLOAD:
522 *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN) ?
523 (double)data->progress.ul.total_size : -1;
524 break;
525 case CURLINFO_REDIRECT_TIME:
526 *param_doublep = DOUBLE_SECS(data->progress.t_redirect);
527 break;
528
529 default:
530 return CURLE_UNKNOWN_OPTION;
531 }
532
533 return CURLE_OK;
534 }
535
getinfo_slist(struct Curl_easy * data,CURLINFO info,struct curl_slist ** param_slistp)536 static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info,
537 struct curl_slist **param_slistp)
538 {
539 union {
540 struct curl_certinfo *to_certinfo;
541 struct curl_slist *to_slist;
542 } ptr;
543
544 switch(info) {
545 case CURLINFO_SSL_ENGINES:
546 *param_slistp = Curl_ssl_engines_list(data);
547 break;
548 case CURLINFO_COOKIELIST:
549 *param_slistp = Curl_cookie_list(data);
550 break;
551 case CURLINFO_CERTINFO:
552 /* Return the a pointer to the certinfo struct. Not really an slist
553 pointer but we can pretend it is here */
554 ptr.to_certinfo = &data->info.certs;
555 *param_slistp = ptr.to_slist;
556 break;
557 case CURLINFO_TLS_SESSION:
558 case CURLINFO_TLS_SSL_PTR:
559 {
560 struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **)
561 param_slistp;
562 struct curl_tlssessioninfo *tsi = &data->tsi;
563 #ifdef USE_SSL
564 struct connectdata *conn = data->conn;
565 #endif
566
567 *tsip = tsi;
568 tsi->backend = Curl_ssl_backend();
569 tsi->internals = NULL;
570
571 #ifdef USE_SSL
572 if(conn && tsi->backend != CURLSSLBACKEND_NONE) {
573 tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0);
574 }
575 #endif
576 }
577 break;
578 default:
579 return CURLE_UNKNOWN_OPTION;
580 }
581
582 return CURLE_OK;
583 }
584
getinfo_socket(struct Curl_easy * data,CURLINFO info,curl_socket_t * param_socketp)585 static CURLcode getinfo_socket(struct Curl_easy *data, CURLINFO info,
586 curl_socket_t *param_socketp)
587 {
588 switch(info) {
589 case CURLINFO_ACTIVESOCKET:
590 *param_socketp = Curl_getconnectinfo(data, NULL);
591 break;
592 default:
593 return CURLE_UNKNOWN_OPTION;
594 }
595
596 return CURLE_OK;
597 }
598
Curl_getinfo(struct Curl_easy * data,CURLINFO info,...)599 CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...)
600 {
601 va_list arg;
602 long *param_longp = NULL;
603 double *param_doublep = NULL;
604 curl_off_t *param_offt = NULL;
605 const char **param_charp = NULL;
606 struct curl_slist **param_slistp = NULL;
607 curl_socket_t *param_socketp = NULL;
608 int type;
609 CURLcode result = CURLE_UNKNOWN_OPTION;
610
611 if(!data)
612 return CURLE_BAD_FUNCTION_ARGUMENT;
613
614 va_start(arg, info);
615
616 type = CURLINFO_TYPEMASK & (int)info;
617 switch(type) {
618 case CURLINFO_STRING:
619 param_charp = va_arg(arg, const char **);
620 if(param_charp)
621 result = getinfo_char(data, info, param_charp);
622 break;
623 case CURLINFO_LONG:
624 param_longp = va_arg(arg, long *);
625 if(param_longp)
626 result = getinfo_long(data, info, param_longp);
627 break;
628 case CURLINFO_DOUBLE:
629 param_doublep = va_arg(arg, double *);
630 if(param_doublep)
631 result = getinfo_double(data, info, param_doublep);
632 break;
633 case CURLINFO_OFF_T:
634 param_offt = va_arg(arg, curl_off_t *);
635 if(param_offt)
636 result = getinfo_offt(data, info, param_offt);
637 break;
638 case CURLINFO_SLIST:
639 param_slistp = va_arg(arg, struct curl_slist **);
640 if(param_slistp)
641 result = getinfo_slist(data, info, param_slistp);
642 break;
643 case CURLINFO_SOCKET:
644 param_socketp = va_arg(arg, curl_socket_t *);
645 if(param_socketp)
646 result = getinfo_socket(data, info, param_socketp);
647 break;
648 default:
649 break;
650 }
651
652 va_end(arg);
653
654 return result;
655 }
656