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