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