mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-16 00:00:07 +01:00
290 lines
7 KiB
C
290 lines
7 KiB
C
/*
|
|
* libwebsockets - peer limits tracking
|
|
*
|
|
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation:
|
|
* version 2.1 of the License.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "core/private.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
|