| /* |
| * libwebsockets - small server side websockets and web server implementation |
| * |
| * Copyright (C) 2010 - 2019 Andy Green <[email protected]> |
| * |
| * 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 <libwebsockets.h> |
| #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; |
| |
| return; |
| } |
| } lws_end_foreach_llp(p, peer_wait_list); |
| } |
| |
| /* 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; |
| } |
| |
| |
| struct lws_peer * |
| lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd) |
| { |
| struct lws_context *context = vhost->context; |
| socklen_t rlen = 0; |
| void *q; |
| uint8_t *q8; |
| struct lws_peer *peer; |
| uint32_t hash = 0; |
| int n, af = AF_INET; |
| struct sockaddr_storage addr; |
| |
| if (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK) |
| return NULL; |
| |
| #ifdef LWS_WITH_IPV6 |
| if (LWS_IPV6_ENABLED(vhost)) { |
| af = AF_INET6; |
| } |
| #endif |
| rlen = sizeof(addr); |
| if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen)) |
| /* eg, udp doesn't have to have a peer */ |
| return NULL; |
| |
| #ifdef LWS_WITH_IPV6 |
| if (af == AF_INET) |
| #endif |
| { |
| struct sockaddr_in *s = (struct sockaddr_in *)&addr; |
| q = &s->sin_addr; |
| rlen = sizeof(s->sin_addr); |
| } |
| #ifdef LWS_WITH_IPV6 |
| else { |
| struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; |
| q = &s->sin6_addr; |
| rlen = sizeof(s->sin6_addr); |
| } |
| #endif |
| |
| q8 = q; |
| for (n = 0; n < (int)rlen; n++) |
| hash = (((hash << 4) | (hash >> 28)) * n) ^ q8[n]; |
| |
| 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->af == af && !memcmp(q, peerx->addr, rlen)) { |
| 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->af = af; |
| context->pl_hash_table[hash] = peer; |
| memcpy(peer->addr, q, rlen); |
| 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: wsi %p: created %llu: wsi: %d/%d, ah %d/%d\n", |
| __func__, |
| 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: wsi %p: created %llu: wsi: %d/%d\n", __func__, |
| 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 |