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