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 | */ |
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 | |
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->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 | |
175 | static 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 | |
328 | static 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 | |
407 | static 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 | |
481 | static 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 | |
536 | static 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 | |
550 | CURLcode 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 | |