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