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