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