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 */
29 static void
__lws_peer_remove_from_peer_wait_list(struct lws_context * context,struct lws_peer * peer)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 
50 void
lws_sul_peer_limits_cb(lws_sorted_usec_list_t * sul)51 lws_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 */
63 static void
__lws_peer_add_to_peer_wait_list(struct lws_context * context,struct lws_peer * peer)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 
78 struct lws_peer *
lws_get_or_create_peer(struct lws_vhost * vhost,lws_sockfd_type sockfd)79 lws_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)
131 hit:
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 */
168 static int
__lws_peer_destroy(struct lws_context * context,struct lws_peer * peer)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 
186 void
lws_peer_cull_peer_wait_list(struct lws_context * context)187 lws_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 
217 void
lws_peer_add_wsi(struct lws_context * context,struct lws_peer * peer,struct lws * wsi)218 lws_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 
233 void
lws_peer_dump_from_wsi(struct lws * wsi)234 lws_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 
257 void
lws_peer_track_wsi_close(struct lws_context * context,struct lws_peer * peer)258 lws_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)
289 int
lws_peer_confirm_ah_attach_ok(struct lws_context * context,struct lws_peer * peer)290 lws_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 
307 void
lws_peer_track_ah_detach(struct lws_context * context,struct lws_peer * peer)308 lws_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