/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2020 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include "private-lib-core.h" /* requires context->lock */ static void __lws_peer_remove_from_peer_wait_list(struct lws_context *context, struct lws_peer *peer) { struct lws_peer *df; lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) { if (*p == peer) { df = *p; *p = df->peer_wait_list; df->peer_wait_list = NULL; if (!context->peer_wait_list) lws_sul_cancel(&context->pt[0].sul_peer_limits); return; } } lws_end_foreach_llp(p, peer_wait_list); } void lws_sul_peer_limits_cb(lws_sorted_usec_list_t *sul) { struct lws_context_per_thread *pt = lws_container_of(sul, struct lws_context_per_thread, sul_peer_limits); lws_peer_cull_peer_wait_list(pt->context); lws_sul_schedule(pt->context, 0, &pt->context->pt[0].sul_peer_limits, lws_sul_peer_limits_cb, 10 * LWS_US_PER_SEC); } /* requires context->lock */ static void __lws_peer_add_to_peer_wait_list(struct lws_context *context, struct lws_peer *peer) { __lws_peer_remove_from_peer_wait_list(context, peer); peer->peer_wait_list = context->peer_wait_list; context->peer_wait_list = peer; if (!context->pt[0].sul_peer_limits.list.owner) lws_sul_schedule(context, 0, &context->pt[0].sul_peer_limits, lws_sul_peer_limits_cb, 10 * LWS_US_PER_SEC); } struct lws_peer * lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd) { struct lws_context *context = vhost->context; struct lws_peer *peer; lws_sockaddr46 sa46; socklen_t rlen = 0; uint32_t hash = 0; uint8_t *q8; void *q; int n; if (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK) return NULL; rlen = sizeof(sa46); if (getpeername(sockfd, (struct sockaddr*)&sa46, &rlen)) /* eg, udp doesn't have to have a peer */ return NULL; #ifdef LWS_WITH_IPV6 if (sa46.sa4.sin_family == AF_INET6) { q = &sa46.sa6.sin6_addr; rlen = sizeof(sa46.sa6.sin6_addr); } else #endif { q = &sa46.sa4.sin_addr; rlen = sizeof(sa46.sa4.sin_addr); } q8 = q; for (n = 0; n < (int)rlen; n++) hash = (uint32_t)((((hash << 4) | (hash >> 28)) * (uint32_t)n) ^ q8[n]); if (!context->pl_hash_elements) return NULL; hash = hash % context->pl_hash_elements; lws_context_lock(context, "peer search"); /* <======================= */ lws_start_foreach_ll(struct lws_peer *, peerx, context->pl_hash_table[hash]) { if (peerx->sa46.sa4.sin_family == sa46.sa4.sin_family) { #if defined(LWS_WITH_IPV6) if (sa46.sa4.sin_family == AF_INET6 && !memcmp(q, &peerx->sa46.sa6.sin6_addr, rlen)) goto hit; #endif if (sa46.sa4.sin_family == AF_INET && !memcmp(q, &peerx->sa46.sa4.sin_addr, rlen)) { #if defined(LWS_WITH_IPV6) hit: #endif lws_context_unlock(context); /* === */ return peerx; } } } lws_end_foreach_ll(peerx, next); lwsl_info("%s: creating new peer\n", __func__); peer = lws_zalloc(sizeof(*peer), "peer"); if (!peer) { lws_context_unlock(context); /* === */ lwsl_err("%s: OOM for new peer\n", __func__); return NULL; } context->count_peers++; peer->next = context->pl_hash_table[hash]; peer->hash = hash; peer->sa46 = sa46; context->pl_hash_table[hash] = peer; time(&peer->time_created); /* * On creation, the peer has no wsi attached, so is created on the * wait list. When a wsi is added it is removed from the wait list. */ time(&peer->time_closed_all); __lws_peer_add_to_peer_wait_list(context, peer); lws_context_unlock(context); /* ====================================> */ return peer; } /* requires context->lock */ static int __lws_peer_destroy(struct lws_context *context, struct lws_peer *peer) { lws_start_foreach_llp(struct lws_peer **, p, context->pl_hash_table[peer->hash]) { if (*p == peer) { struct lws_peer *df = *p; *p = df->next; lws_free(df); context->count_peers--; return 0; } } lws_end_foreach_llp(p, next); return 1; } void lws_peer_cull_peer_wait_list(struct lws_context *context) { struct lws_peer *df; time_t t; time(&t); if (context->next_cull && t < context->next_cull) return; lws_context_lock(context, "peer cull"); /* <========================= */ context->next_cull = t + 5; lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) { if (t - (*p)->time_closed_all > 10) { df = *p; /* remove us from the peer wait list */ *p = df->peer_wait_list; df->peer_wait_list = NULL; __lws_peer_destroy(context, df); continue; /* we already point to next, if any */ } } lws_end_foreach_llp(p, peer_wait_list); lws_context_unlock(context); /* ====================================> */ } void lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer, struct lws *wsi) { if (!peer) return; lws_context_lock(context, "peer add"); /* <========================== */ peer->count_wsi++; wsi->peer = peer; __lws_peer_remove_from_peer_wait_list(context, peer); lws_context_unlock(context); /* ====================================> */ } void lws_peer_dump_from_wsi(struct lws *wsi) { struct lws_peer *peer; if (!wsi || !wsi->peer) return; peer = wsi->peer; #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) lwsl_notice("%s: %s: created %llu: wsi: %d/%d, ah %d/%d\n", __func__, lws_wsi_tag(wsi), (unsigned long long)peer->time_created, peer->count_wsi, peer->total_wsi, peer->http.count_ah, peer->http.total_ah); #else lwsl_notice("%s: %s: created %llu: wsi: %d/%d\n", __func__, lws_wsi_tag(wsi), (unsigned long long)peer->time_created, peer->count_wsi, peer->total_wsi); #endif } void lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer) { if (!peer) return; lws_context_lock(context, "peer wsi close"); /* <==================== */ assert(peer->count_wsi); peer->count_wsi--; if (!peer->count_wsi #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) && !peer->http.count_ah #endif ) { /* * in order that we can accumulate peer activity correctly * allowing for periods when the peer has no connections, * we don't synchronously destroy the peer when his last * wsi closes. Instead we mark the time his last wsi * closed and add him to a peer_wait_list to be reaped * later if no further activity is coming. */ time(&peer->time_closed_all); __lws_peer_add_to_peer_wait_list(context, peer); } lws_context_unlock(context); /* ====================================> */ } #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) int lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer) { if (!peer) return 0; if (context->ip_limit_ah && peer->http.count_ah >= context->ip_limit_ah) { lwsl_info("peer reached ah limit %d, deferring\n", context->ip_limit_ah); return 1; } return 0; } void lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer) { if (!peer) return; lws_context_lock(context, "peer ah detach"); /* <==================== */ assert(peer->http.count_ah); peer->http.count_ah--; lws_context_unlock(context); /* ====================================> */ } #endif