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