1/*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2019 - 2021 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 *
24 * Client Connection Latency and DNS reporting
25 */
26
27/*
28 * We want to allocate copies for and append DNS results that we don't already
29 * have.  We take this approach because a) we may be getting duplicated results
30 * from multiple DNS servers, and b) we may be getting results stacatto over
31 * time.
32 *
33 * We capture DNS results from either getaddrinfo or ASYNC_DNS the same here,
34 * before they are sorted and filtered.
35 *
36 * Because this is relatively expensive, we only do it on client wsi that
37 * explicitly indicated that they want it with the LCCSCF_CONMON flag.
38 */
39
40#include <private-lib-core.h>
41
42int
43lws_conmon_append_copy_new_dns_results(struct lws *wsi,
44				       const struct addrinfo *cai)
45{
46	if (!(wsi->flags & LCCSCF_CONMON))
47		return 0;
48
49	/*
50	 * Let's go through the incoming guys, seeing if we already have them,
51	 * or if we want to take a copy
52	 */
53
54	while (cai) {
55		struct addrinfo *ai = wsi->conmon.dns_results_copy;
56		char skip = 0;
57
58		/* do we already have this guy? */
59
60		while (ai) {
61
62			if (ai->ai_family != cai->ai_family &&
63			    ai->ai_addrlen != cai->ai_addrlen &&
64			    ai->ai_protocol != cai->ai_protocol &&
65			    ai->ai_socktype != cai->ai_socktype &&
66			    /* either ipv4 or v6 address must match */
67			    ((ai->ai_family == AF_INET &&
68			      ((struct sockaddr_in *)ai->ai_addr)->
69							     sin_addr.s_addr ==
70			      ((struct sockaddr_in *)cai->ai_addr)->
71							     sin_addr.s_addr)
72#if defined(LWS_WITH_IPV6)
73					    ||
74			    (ai->ai_family == AF_INET6 &&
75			     !memcmp(((struct sockaddr_in6 *)ai->ai_addr)->
76							     sin6_addr.s6_addr,
77				     ((struct sockaddr_in6 *)cai->ai_addr)->
78						     sin6_addr.s6_addr, 16))
79#endif
80			    )) {
81				/* yes, we already got a copy then */
82				skip = 1;
83				break;
84			}
85
86			ai = ai->ai_next;
87		}
88
89		if (!skip) {
90			/*
91			 * No we don't already have a copy of this one, let's
92			 * allocate and append it then
93			 */
94			size_t al = sizeof(struct addrinfo) +
95				    (size_t)cai->ai_addrlen;
96			size_t cl = cai->ai_canonname ?
97					strlen(cai->ai_canonname) + 1 : 0;
98
99			ai = lws_malloc(al + cl + 1, __func__);
100			if (!ai) {
101				lwsl_wsi_warn(wsi, "OOM");
102				return 1;
103			}
104			*ai = *cai;
105			ai->ai_addr = (struct sockaddr *)&ai[1];
106			memcpy(ai->ai_addr, cai->ai_addr, (size_t)cai->ai_addrlen);
107
108			if (cl) {
109				ai->ai_canonname = ((char *)ai->ai_addr) +
110							cai->ai_addrlen;
111				memcpy(ai->ai_canonname, cai->ai_canonname,
112				       cl + 1);
113			}
114			ai->ai_next = wsi->conmon.dns_results_copy;
115			wsi->conmon.dns_results_copy = ai;
116		}
117
118		cai = cai->ai_next;
119	}
120
121	return 0;
122}
123
124void
125lws_conmon_addrinfo_destroy(struct addrinfo *ai)
126{
127	while (ai) {
128		struct addrinfo *ai1 = ai->ai_next;
129
130		lws_free(ai);
131		ai = ai1;
132	}
133}
134
135void
136lws_conmon_wsi_take(struct lws *wsi, struct lws_conmon *dest)
137{
138	memcpy(dest, &wsi->conmon, sizeof(*dest));
139	dest->peer46 = wsi->sa46_peer;
140
141	/* wsi no longer has to free it... */
142	wsi->conmon.dns_results_copy = NULL;
143	wsi->perf_done = 1;
144}
145
146void
147lws_conmon_release(struct lws_conmon *conmon)
148{
149	if (!conmon)
150		return;
151
152	lws_conmon_addrinfo_destroy(conmon->dns_results_copy);
153	conmon->dns_results_copy = NULL;
154}
155