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_starttransfer = 0;
57 pro->timespent = 0;
58 pro->t_redirect = 0;
59 pro->is_t_startransfer_set = false;
60
61 info->httpcode = 0;
62 info->httpproxycode = 0;
63 info->httpversion = 0;
64 info->filetime = -1; /* -1 is an illegal time and thus means unknown */
65 info->timecond = FALSE;
66
67 info->header_size = 0;
68 info->request_size = 0;
69 info->proxyauthavail = 0;
70 info->httpauthavail = 0;
71 info->numconnects = 0;
72
73 free(info->contenttype);
74 info->contenttype = NULL;
75
76 free(info->wouldredirect);
77 info->wouldredirect = NULL;
78
79 info->primary.remote_ip[0] = '\0';
80 info->primary.local_ip[0] = '\0';
81 info->primary.remote_port = 0;
82 info->primary.local_port = 0;
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 = strtol(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 = strtol(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 #ifndef CURL_DISABLE_PROXY
256 case CURLINFO_PROXY_SSL_VERIFYRESULT:
257 *param_longp = data->set.proxy_ssl.certverifyresult;
258 break;
259 #endif
260 case CURLINFO_REDIRECT_COUNT:
261 *param_longp = data->state.followlocation;
262 break;
263 case CURLINFO_HTTPAUTH_AVAIL:
264 lptr.to_long = param_longp;
265 *lptr.to_ulong = data->info.httpauthavail;
266 break;
267 case CURLINFO_PROXYAUTH_AVAIL:
268 lptr.to_long = param_longp;
269 *lptr.to_ulong = data->info.proxyauthavail;
270 break;
271 case CURLINFO_OS_ERRNO:
272 *param_longp = data->state.os_errno;
273 break;
274 case CURLINFO_NUM_CONNECTS:
275 *param_longp = data->info.numconnects;
276 break;
277 case CURLINFO_LASTSOCKET:
278 sockfd = Curl_getconnectinfo(data, NULL);
279
280 /* note: this is not a good conversion for systems with 64 bit sockets and
281 32 bit longs */
282 if(sockfd != CURL_SOCKET_BAD)
283 *param_longp = (long)sockfd;
284 else
285 /* this interface is documented to return -1 in case of badness, which
286 may not be the same as the CURL_SOCKET_BAD value */
287 *param_longp = -1;
288 break;
289 case CURLINFO_PRIMARY_PORT:
290 /* Return the (remote) port of the most recent (primary) connection */
291 *param_longp = data->info.primary.remote_port;
292 break;
293 case CURLINFO_LOCAL_PORT:
294 /* Return the local port of the most recent (primary) connection */
295 *param_longp = data->info.primary.local_port;
296 break;
297 case CURLINFO_PROXY_ERROR:
298 *param_longp = (long)data->info.pxcode;
299 break;
300 case CURLINFO_CONDITION_UNMET:
301 if(data->info.httpcode == 304)
302 *param_longp = 1L;
303 else
304 /* return if the condition prevented the document to get transferred */
305 *param_longp = data->info.timecond ? 1L : 0L;
306 break;
307 #ifndef CURL_DISABLE_RTSP
308 case CURLINFO_RTSP_CLIENT_CSEQ:
309 *param_longp = data->state.rtsp_next_client_CSeq;
310 break;
311 case CURLINFO_RTSP_SERVER_CSEQ:
312 *param_longp = data->state.rtsp_next_server_CSeq;
313 break;
314 case CURLINFO_RTSP_CSEQ_RECV:
315 *param_longp = data->state.rtsp_CSeq_recv;
316 break;
317 #endif
318 case CURLINFO_HTTP_VERSION:
319 switch(data->info.httpversion) {
320 case 10:
321 *param_longp = CURL_HTTP_VERSION_1_0;
322 break;
323 case 11:
324 *param_longp = CURL_HTTP_VERSION_1_1;
325 break;
326 case 20:
327 *param_longp = CURL_HTTP_VERSION_2_0;
328 break;
329 case 30:
330 *param_longp = CURL_HTTP_VERSION_3;
331 break;
332 default:
333 *param_longp = CURL_HTTP_VERSION_NONE;
334 break;
335 }
336 break;
337 case CURLINFO_PROTOCOL:
338 *param_longp = data->info.conn_protocol;
339 break;
340 case CURLINFO_USED_PROXY:
341 *param_longp =
342 #ifdef CURL_DISABLE_PROXY
343 0
344 #else
345 data->info.used_proxy
346 #endif
347 ;
348 break;
349 default:
350 return CURLE_UNKNOWN_OPTION;
351 }
352
353 return CURLE_OK;
354 }
355
356 #define DOUBLE_SECS(x) (double)(x)/1000000
357
getinfo_offt(struct Curl_easy * data,CURLINFO info,curl_off_t * param_offt)358 static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
359 curl_off_t *param_offt)
360 {
361 #ifdef DEBUGBUILD
362 char *timestr = getenv("CURL_TIME");
363 if(timestr) {
364 unsigned long val = strtol(timestr, NULL, 10);
365 switch(info) {
366 case CURLINFO_TOTAL_TIME_T:
367 case CURLINFO_NAMELOOKUP_TIME_T:
368 case CURLINFO_CONNECT_TIME_T:
369 case CURLINFO_APPCONNECT_TIME_T:
370 case CURLINFO_PRETRANSFER_TIME_T:
371 case CURLINFO_STARTTRANSFER_TIME_T:
372 case CURLINFO_REDIRECT_TIME_T:
373 case CURLINFO_SPEED_DOWNLOAD_T:
374 case CURLINFO_SPEED_UPLOAD_T:
375 *param_offt = (curl_off_t)val;
376 return CURLE_OK;
377 default:
378 break;
379 }
380 }
381 #endif
382 switch(info) {
383 case CURLINFO_FILETIME_T:
384 *param_offt = (curl_off_t)data->info.filetime;
385 break;
386 case CURLINFO_SIZE_UPLOAD_T:
387 *param_offt = data->progress.uploaded;
388 break;
389 case CURLINFO_SIZE_DOWNLOAD_T:
390 *param_offt = data->progress.downloaded;
391 break;
392 case CURLINFO_SPEED_DOWNLOAD_T:
393 *param_offt = data->progress.dlspeed;
394 break;
395 case CURLINFO_SPEED_UPLOAD_T:
396 *param_offt = data->progress.ulspeed;
397 break;
398 case CURLINFO_CONTENT_LENGTH_DOWNLOAD_T:
399 *param_offt = (data->progress.flags & PGRS_DL_SIZE_KNOWN)?
400 data->progress.size_dl:-1;
401 break;
402 case CURLINFO_CONTENT_LENGTH_UPLOAD_T:
403 *param_offt = (data->progress.flags & PGRS_UL_SIZE_KNOWN)?
404 data->progress.size_ul:-1;
405 break;
406 case CURLINFO_TOTAL_TIME_T:
407 *param_offt = data->progress.timespent;
408 break;
409 case CURLINFO_NAMELOOKUP_TIME_T:
410 *param_offt = data->progress.t_nslookup;
411 break;
412 case CURLINFO_CONNECT_TIME_T:
413 *param_offt = data->progress.t_connect;
414 break;
415 case CURLINFO_APPCONNECT_TIME_T:
416 *param_offt = data->progress.t_appconnect;
417 break;
418 case CURLINFO_PRETRANSFER_TIME_T:
419 *param_offt = data->progress.t_pretransfer;
420 break;
421 case CURLINFO_STARTTRANSFER_TIME_T:
422 *param_offt = data->progress.t_starttransfer;
423 break;
424 case CURLINFO_QUEUE_TIME_T:
425 *param_offt = data->progress.t_postqueue;
426 break;
427 case CURLINFO_REDIRECT_TIME_T:
428 *param_offt = data->progress.t_redirect;
429 break;
430 case CURLINFO_RETRY_AFTER:
431 *param_offt = data->info.retry_after;
432 break;
433 case CURLINFO_XFER_ID:
434 *param_offt = data->id;
435 break;
436 case CURLINFO_CONN_ID:
437 *param_offt = data->conn?
438 data->conn->connection_id : data->state.recent_conn_id;
439 break;
440 default:
441 return CURLE_UNKNOWN_OPTION;
442 }
443
444 return CURLE_OK;
445 }
446
getinfo_double(struct Curl_easy * data,CURLINFO info,double * param_doublep)447 static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info,
448 double *param_doublep)
449 {
450 #ifdef DEBUGBUILD
451 char *timestr = getenv("CURL_TIME");
452 if(timestr) {
453 unsigned long val = strtol(timestr, NULL, 10);
454 switch(info) {
455 case CURLINFO_TOTAL_TIME:
456 case CURLINFO_NAMELOOKUP_TIME:
457 case CURLINFO_CONNECT_TIME:
458 case CURLINFO_APPCONNECT_TIME:
459 case CURLINFO_PRETRANSFER_TIME:
460 case CURLINFO_STARTTRANSFER_TIME:
461 case CURLINFO_REDIRECT_TIME:
462 case CURLINFO_SPEED_DOWNLOAD:
463 case CURLINFO_SPEED_UPLOAD:
464 *param_doublep = (double)val;
465 return CURLE_OK;
466 default:
467 break;
468 }
469 }
470 #endif
471 switch(info) {
472 case CURLINFO_TOTAL_TIME:
473 *param_doublep = DOUBLE_SECS(data->progress.timespent);
474 break;
475 case CURLINFO_NAMELOOKUP_TIME:
476 *param_doublep = DOUBLE_SECS(data->progress.t_nslookup);
477 break;
478 case CURLINFO_CONNECT_TIME:
479 *param_doublep = DOUBLE_SECS(data->progress.t_connect);
480 break;
481 case CURLINFO_APPCONNECT_TIME:
482 *param_doublep = DOUBLE_SECS(data->progress.t_appconnect);
483 break;
484 case CURLINFO_PRETRANSFER_TIME:
485 *param_doublep = DOUBLE_SECS(data->progress.t_pretransfer);
486 break;
487 case CURLINFO_STARTTRANSFER_TIME:
488 *param_doublep = DOUBLE_SECS(data->progress.t_starttransfer);
489 break;
490 case CURLINFO_SIZE_UPLOAD:
491 *param_doublep = (double)data->progress.uploaded;
492 break;
493 case CURLINFO_SIZE_DOWNLOAD:
494 *param_doublep = (double)data->progress.downloaded;
495 break;
496 case CURLINFO_SPEED_DOWNLOAD:
497 *param_doublep = (double)data->progress.dlspeed;
498 break;
499 case CURLINFO_SPEED_UPLOAD:
500 *param_doublep = (double)data->progress.ulspeed;
501 break;
502 case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
503 *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN)?
504 (double)data->progress.size_dl:-1;
505 break;
506 case CURLINFO_CONTENT_LENGTH_UPLOAD:
507 *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN)?
508 (double)data->progress.size_ul:-1;
509 break;
510 case CURLINFO_REDIRECT_TIME:
511 *param_doublep = DOUBLE_SECS(data->progress.t_redirect);
512 break;
513
514 default:
515 return CURLE_UNKNOWN_OPTION;
516 }
517
518 return CURLE_OK;
519 }
520
getinfo_slist(struct Curl_easy * data,CURLINFO info,struct curl_slist ** param_slistp)521 static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info,
522 struct curl_slist **param_slistp)
523 {
524 union {
525 struct curl_certinfo *to_certinfo;
526 struct curl_slist *to_slist;
527 } ptr;
528
529 switch(info) {
530 case CURLINFO_SSL_ENGINES:
531 *param_slistp = Curl_ssl_engines_list(data);
532 break;
533 case CURLINFO_COOKIELIST:
534 *param_slistp = Curl_cookie_list(data);
535 break;
536 case CURLINFO_CERTINFO:
537 /* Return the a pointer to the certinfo struct. Not really an slist
538 pointer but we can pretend it is here */
539 ptr.to_certinfo = &data->info.certs;
540 *param_slistp = ptr.to_slist;
541 break;
542 case CURLINFO_TLS_SESSION:
543 case CURLINFO_TLS_SSL_PTR:
544 {
545 struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **)
546 param_slistp;
547 struct curl_tlssessioninfo *tsi = &data->tsi;
548 #ifdef USE_SSL
549 struct connectdata *conn = data->conn;
550 #endif
551
552 *tsip = tsi;
553 tsi->backend = Curl_ssl_backend();
554 tsi->internals = NULL;
555
556 #ifdef USE_SSL
557 if(conn && tsi->backend != CURLSSLBACKEND_NONE) {
558 tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0);
559 }
560 #endif
561 }
562 break;
563 default:
564 return CURLE_UNKNOWN_OPTION;
565 }
566
567 return CURLE_OK;
568 }
569
getinfo_socket(struct Curl_easy * data,CURLINFO info,curl_socket_t * param_socketp)570 static CURLcode getinfo_socket(struct Curl_easy *data, CURLINFO info,
571 curl_socket_t *param_socketp)
572 {
573 switch(info) {
574 case CURLINFO_ACTIVESOCKET:
575 *param_socketp = Curl_getconnectinfo(data, NULL);
576 break;
577 default:
578 return CURLE_UNKNOWN_OPTION;
579 }
580
581 return CURLE_OK;
582 }
583
Curl_getinfo(struct Curl_easy * data,CURLINFO info,...)584 CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...)
585 {
586 va_list arg;
587 long *param_longp = NULL;
588 double *param_doublep = NULL;
589 curl_off_t *param_offt = NULL;
590 const char **param_charp = NULL;
591 struct curl_slist **param_slistp = NULL;
592 curl_socket_t *param_socketp = NULL;
593 int type;
594 CURLcode result = CURLE_UNKNOWN_OPTION;
595
596 if(!data)
597 return CURLE_BAD_FUNCTION_ARGUMENT;
598
599 va_start(arg, info);
600
601 type = CURLINFO_TYPEMASK & (int)info;
602 switch(type) {
603 case CURLINFO_STRING:
604 param_charp = va_arg(arg, const char **);
605 if(param_charp)
606 result = getinfo_char(data, info, param_charp);
607 break;
608 case CURLINFO_LONG:
609 param_longp = va_arg(arg, long *);
610 if(param_longp)
611 result = getinfo_long(data, info, param_longp);
612 break;
613 case CURLINFO_DOUBLE:
614 param_doublep = va_arg(arg, double *);
615 if(param_doublep)
616 result = getinfo_double(data, info, param_doublep);
617 break;
618 case CURLINFO_OFF_T:
619 param_offt = va_arg(arg, curl_off_t *);
620 if(param_offt)
621 result = getinfo_offt(data, info, param_offt);
622 break;
623 case CURLINFO_SLIST:
624 param_slistp = va_arg(arg, struct curl_slist **);
625 if(param_slistp)
626 result = getinfo_slist(data, info, param_slistp);
627 break;
628 case CURLINFO_SOCKET:
629 param_socketp = va_arg(arg, curl_socket_t *);
630 if(param_socketp)
631 result = getinfo_socket(data, info, param_socketp);
632 break;
633 default:
634 break;
635 }
636
637 va_end(arg);
638
639 return result;
640 }
641