1/*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2020 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
25#include <libwebsockets.h>
26#include "private-lib-core.h"
27
28/* requires context->lock */
29static void
30__lws_peer_remove_from_peer_wait_list(struct lws_context *context,
31				      struct lws_peer *peer)
32{
33	struct lws_peer *df;
34
35	lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) {
36		if (*p == peer) {
37			df = *p;
38
39			*p = df->peer_wait_list;
40			df->peer_wait_list = NULL;
41
42			if (!context->peer_wait_list)
43				lws_sul_cancel(&context->pt[0].sul_peer_limits);
44
45			return;
46		}
47	} lws_end_foreach_llp(p, peer_wait_list);
48}
49
50void
51lws_sul_peer_limits_cb(lws_sorted_usec_list_t *sul)
52{
53	struct lws_context_per_thread *pt = lws_container_of(sul,
54			struct lws_context_per_thread, sul_peer_limits);
55
56	lws_peer_cull_peer_wait_list(pt->context);
57
58	lws_sul_schedule(pt->context, 0, &pt->context->pt[0].sul_peer_limits,
59			 lws_sul_peer_limits_cb, 10 * LWS_US_PER_SEC);
60}
61
62/* requires context->lock */
63static void
64__lws_peer_add_to_peer_wait_list(struct lws_context *context,
65				 struct lws_peer *peer)
66{
67	__lws_peer_remove_from_peer_wait_list(context, peer);
68
69	peer->peer_wait_list = context->peer_wait_list;
70	context->peer_wait_list = peer;
71
72	if (!context->pt[0].sul_peer_limits.list.owner)
73		lws_sul_schedule(context, 0, &context->pt[0].sul_peer_limits,
74				lws_sul_peer_limits_cb, 10 * LWS_US_PER_SEC);
75}
76
77
78struct lws_peer *
79lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd)
80{
81	struct lws_context *context = vhost->context;
82	struct lws_peer *peer;
83	lws_sockaddr46 sa46;
84	socklen_t rlen = 0;
85	uint32_t hash = 0;
86	uint8_t *q8;
87	void *q;
88	int n;
89
90	if (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK)
91		return NULL;
92
93	rlen = sizeof(sa46);
94	if (getpeername(sockfd, (struct sockaddr*)&sa46, &rlen))
95		/* eg, udp doesn't have to have a peer */
96		return NULL;
97
98#ifdef LWS_WITH_IPV6
99	if (sa46.sa4.sin_family == AF_INET6) {
100		q = &sa46.sa6.sin6_addr;
101		rlen = sizeof(sa46.sa6.sin6_addr);
102	} else
103#endif
104	{
105		q = &sa46.sa4.sin_addr;
106		rlen = sizeof(sa46.sa4.sin_addr);
107	}
108
109	q8 = q;
110	for (n = 0; n < (int)rlen; n++)
111		hash = (uint32_t)((((hash << 4) | (hash >> 28)) * (uint32_t)n) ^ q8[n]);
112
113	if (!context->pl_hash_elements)
114		return NULL;
115
116	hash = hash % context->pl_hash_elements;
117
118	lws_context_lock(context, "peer search"); /* <======================= */
119
120	lws_start_foreach_ll(struct lws_peer *, peerx,
121			     context->pl_hash_table[hash]) {
122		if (peerx->sa46.sa4.sin_family == sa46.sa4.sin_family) {
123#if defined(LWS_WITH_IPV6)
124			if (sa46.sa4.sin_family == AF_INET6 &&
125			    !memcmp(q, &peerx->sa46.sa6.sin6_addr, rlen))
126				goto hit;
127#endif
128			if (sa46.sa4.sin_family == AF_INET &&
129			    !memcmp(q, &peerx->sa46.sa4.sin_addr, rlen)) {
130#if defined(LWS_WITH_IPV6)
131hit:
132#endif
133				lws_context_unlock(context); /* === */
134
135				return peerx;
136			}
137		}
138	} lws_end_foreach_ll(peerx, next);
139
140	lwsl_info("%s: creating new peer\n", __func__);
141
142	peer = lws_zalloc(sizeof(*peer), "peer");
143	if (!peer) {
144		lws_context_unlock(context); /* === */
145		lwsl_err("%s: OOM for new peer\n", __func__);
146		return NULL;
147	}
148
149	context->count_peers++;
150	peer->next = context->pl_hash_table[hash];
151	peer->hash = hash;
152	peer->sa46 = sa46;
153	context->pl_hash_table[hash] = peer;
154	time(&peer->time_created);
155	/*
156	 * On creation, the peer has no wsi attached, so is created on the
157	 * wait list.  When a wsi is added it is removed from the wait list.
158	 */
159	time(&peer->time_closed_all);
160	__lws_peer_add_to_peer_wait_list(context, peer);
161
162	lws_context_unlock(context); /* ====================================> */
163
164	return peer;
165}
166
167/* requires context->lock */
168static int
169__lws_peer_destroy(struct lws_context *context, struct lws_peer *peer)
170{
171	lws_start_foreach_llp(struct lws_peer **, p,
172			      context->pl_hash_table[peer->hash]) {
173		if (*p == peer) {
174			struct lws_peer *df = *p;
175			*p = df->next;
176			lws_free(df);
177			context->count_peers--;
178
179			return 0;
180		}
181	} lws_end_foreach_llp(p, next);
182
183	return 1;
184}
185
186void
187lws_peer_cull_peer_wait_list(struct lws_context *context)
188{
189	struct lws_peer *df;
190	time_t t;
191
192	time(&t);
193
194	if (context->next_cull && t < context->next_cull)
195		return;
196
197	lws_context_lock(context, "peer cull"); /* <========================= */
198
199	context->next_cull = t + 5;
200
201	lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) {
202		if (t - (*p)->time_closed_all > 10) {
203			df = *p;
204
205			/* remove us from the peer wait list */
206			*p = df->peer_wait_list;
207			df->peer_wait_list = NULL;
208
209			__lws_peer_destroy(context, df);
210			continue; /* we already point to next, if any */
211		}
212	} lws_end_foreach_llp(p, peer_wait_list);
213
214	lws_context_unlock(context); /* ====================================> */
215}
216
217void
218lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer,
219		 struct lws *wsi)
220{
221	if (!peer)
222		return;
223
224	lws_context_lock(context, "peer add"); /* <========================== */
225
226	peer->count_wsi++;
227	wsi->peer = peer;
228	__lws_peer_remove_from_peer_wait_list(context, peer);
229
230	lws_context_unlock(context); /* ====================================> */
231}
232
233void
234lws_peer_dump_from_wsi(struct lws *wsi)
235{
236	struct lws_peer *peer;
237
238	if (!wsi || !wsi->peer)
239		return;
240
241	peer = wsi->peer;
242
243#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
244	lwsl_notice("%s: %s: created %llu: wsi: %d/%d, ah %d/%d\n",
245			__func__, lws_wsi_tag(wsi),
246			(unsigned long long)peer->time_created,
247			peer->count_wsi, peer->total_wsi,
248			peer->http.count_ah, peer->http.total_ah);
249#else
250	lwsl_notice("%s: %s: created %llu: wsi: %d/%d\n", __func__,
251			lws_wsi_tag(wsi),
252			(unsigned long long)peer->time_created,
253			peer->count_wsi, peer->total_wsi);
254#endif
255}
256
257void
258lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer)
259{
260	if (!peer)
261		return;
262
263	lws_context_lock(context, "peer wsi close"); /* <==================== */
264
265	assert(peer->count_wsi);
266	peer->count_wsi--;
267
268	if (!peer->count_wsi
269#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
270			&& !peer->http.count_ah
271#endif
272	) {
273		/*
274		 * in order that we can accumulate peer activity correctly
275		 * allowing for periods when the peer has no connections,
276		 * we don't synchronously destroy the peer when his last
277		 * wsi closes.  Instead we mark the time his last wsi
278		 * closed and add him to a peer_wait_list to be reaped
279		 * later if no further activity is coming.
280		 */
281		time(&peer->time_closed_all);
282		__lws_peer_add_to_peer_wait_list(context, peer);
283	}
284
285	lws_context_unlock(context); /* ====================================> */
286}
287
288#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
289int
290lws_peer_confirm_ah_attach_ok(struct lws_context *context,
291			      struct lws_peer *peer)
292{
293	if (!peer)
294		return 0;
295
296	if (context->ip_limit_ah &&
297	    peer->http.count_ah >= context->ip_limit_ah) {
298		lwsl_info("peer reached ah limit %d, deferring\n",
299				context->ip_limit_ah);
300
301		return 1;
302	}
303
304	return 0;
305}
306
307void
308lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer)
309{
310	if (!peer)
311		return;
312
313	lws_context_lock(context, "peer ah detach"); /* <==================== */
314	assert(peer->http.count_ah);
315	peer->http.count_ah--;
316	lws_context_unlock(context); /* ====================================> */
317}
318#endif
319