/**
    limit proxy speed, number of sockets and cpu load. limit incoming connections per source ip address

    Copyright (c) 2020-2023 The Creators of Simphone

    See the file COPYING.LESSER.txt for copying permission.
**/

#include "config.h"
#include "spth.h"

#include "logger.h"
#include "table.h"
#include "error.h"
#include "utils.h"
#include "system.h"
#include "crypto.h"
#include "socket.h"
#include "network.h"
#include "contact.h"
#include "param.h"
#include "proto.h"
#include "limit.h"
#include "proxy.h"
#include "client.h"
#include "msg.h"
#include "audio.h"
#include "api.h"

#include <string.h>
#include <stdlib.h>

#define SIM_MODULE "limit"

#define LIMIT_STAT_SERVER 0 /* saved traffic statistics for talking client or for all clients if nobody's talking */
#define LIMIT_STAT_CLIENT 1 /* saved traffic statistics for non-talking clients */
#define LIMIT_STAT_UDP 2    /* data for UDP proxy control packets */
#define LIMIT_STAT_REPORT 3 /* time to send speed limit report */

simnumber limit_cpu[LIMIT_CPU_MAX + 1];  /* values of LIMIT_CPU_xxx parameters */
static int limit_cpu_add = 0;            /* value of limit.cpu.add */
static int limit_ip_count;               /* number of incoming connections since limit_ip_tick */
static simnumber limit_ip_cpu;           /* number of milliseconds of cpu time that has been used */
static simnumber limit_ip_tick;          /* last time when limit_ip_cpu was measured and limit_ip_count was reset to zero */
static simnumber limit_ip_cycles;        /* number of milliseconds of cpu time that have already been reported */
static simtype limit_ip_table;           /* incoming connections since limit_ip_tick (keyed by ip) */
static int limit_error;                  /* error code of last sent limit event */
static simtype limit_blacklist_table;    /* keyed by ip, value is a counter */
static unsigned limit_blacklist_count;   /* number of ip addresses in limit_blacklist_table */
static unsigned limit_blacklist_max;     /* maximal number of ip addresses in limit_blacklist_table */
static simunsigned limit_blacklist_tick; /* tick of last addition of ip address to blacklist or zero if running normally */
static unsigned limit_blacklist_timeout; /* number of milliseconds to clearing blacklist (limit.blacklist * 1000) */
static unsigned limit_reverse_timeout;   /* minimal time between rechecking backwards connection (proxy.reverse * 1000) */
static int limit_udp_speed;              /* maximal allowed UDP speed (value of limit.udp) */

static simsocket limit_ping_sock = NULL;  /* socket which is currently doing a speed measurement */
static simcontact limit_ping_contact;     /* contact which was last doing a speed measurement or NULL if no contact */
static simunsigned limit_ping_tick = 0;   /* tick when maximal internet speed has to be measured */
static int limit_ping_count;              /* index into limit_ping_sizes/limit_ping_ticks and second index into limit_ping_data */
static unsigned limit_ping_sizes[3];      /* sizes of currently sent ping packets */
static simnumber limit_ping_ticks[3];     /* current speed measurements (number of milliseconds) */
static simnumber limit_ping_data[2][3];   /* number of bytes received at pong time */
static int *limit_average_buffer = NULL;  /* last measured internet speed from non-contacts or NULL if measuring from contacts */
static int limit_average_size = 0;        /* maximal size of limit_average_buffer (0 - never allocated, < 0 - deallocated) */
static int limit_average_index;           /* number of used elements of limit_average_buffer */
static int limit_measured_speed_contacts; /* maximal measured bidirectional internet speed from contacts (bytes/sec) */
static int limit_measured_speed;          /* maximal allowed bidirectional internet speed (bytes/sec) */

static int limit_speed_number;    /* client number to send speed allocation request for or negative if none */
static unsigned limit_speed_send; /* speed allocation request (bytes/sec) or zero to deallocate */
static int limit_speed_recv = -1; /* allocated speed (bytes/sec) confirmed by proxy (zero if deallocation confirmed) */
static int limit_speed_reply;     /* maximal send speed allowed by proxy (or zero if unknown) */
static int limit_speed_request;   /* LIMIT_SPEED_SEND to do nothing or else LIMIT_SPEED_STOP or LIMIT_SPEED_START */

/* check whether speed limit should NOT be enforced for the given customer */
#define LIMIT_CHECK_LOCAL(customer) ((customer)->flags & PROXY_FLAG_LOCAL && ! param_get_number ("limit.local"))

#define LIMIT_CHECK_CONTACT(contact) ((contact) && (contact)->auth >= CONTACT_AUTH_ACCEPTED)

#define LIMIT_GET_STAT(server, client) \
  (SOCKET_GET_STAT_RECEIVED ((server)->sock) + SOCKET_GET_STAT_RECEIVED ((client)->sock))
#define LIMIT_GET_IP_LOCAL(local) (PROXY_CHECK_LOCAL ((local)->realip) ? 0 : (local)->realip)
#define LIMIT_GET_IP_CUSTOMER(customer)                        \
  (! socket_check_server ((customer)->sock) ? (customer)->ip : \
                                              LIMIT_GET_IP_LOCAL (socket_check_server ((customer)->sock)))

static simbool limit_check_anonymous (simcustomer customer) {
  int type = customer->type;
  simbool anonymous = type == PROXY_TYPE_NEW || type == PROXY_TYPE_ANONYMOUS || type == PROXY_TYPE_REVERSE;
#ifdef _WIN32
  return anonymous && customer->sock->err == SIM_OK;
#else
  return anonymous || (customer->sock->err != SIM_OK && (type == PROXY_TYPE_CLIENT || type == PROXY_TYPE_SERVER));
#endif
}

static int limit_count_socket (int *cancelled, simcustomer *oldest) {
  int count = 0;
  simnumber tick = 0;
  simcustomer customer;
  if (oldest)
    *oldest = NULL;
  for (customer = proxy_customer_list; customer; customer = customer->next)
    if (limit_check_anonymous (customer)) {
      if (cancelled && customer->sock->err != SIM_OK)
        ++*cancelled;
      if ((! tick || customer->sock->created < tick) && oldest) {
        tick = customer->sock->created;
        *oldest = customer;
      }
      count++;
    }
  return count;
}

static int limit_count_client (const char *address) {
  int count = 0;
  simcustomer customer;
  for (customer = proxy_customer_list; customer; customer = customer->next)
    if (customer->sock->err == SIM_OK) {
      if (address || customer->type != PROXY_TYPE_SERVER)
        if (customer->type != PROXY_TYPE_CLIENT || (address && string_check_diff (customer->sock->master->to, address)))
          continue;
      count++;
    }
  return count;
}

int limit_count_customer (int type) {
  int count = 0;
  simcustomer customer;
  for (customer = proxy_customer_list; customer; customer = customer->next)
    if (customer->type == type)
      count++;
  return count;
}

static int limit_count_server (int contacts[2], simbool local) {
  int count = 0;
  simcustomer customer;
  for (customer = proxy_customer_list; customer; customer = customer->next)
    if (customer->type == PROXY_TYPE_SERVER && customer->sock->err == SIM_OK)
      if (local || ! LIMIT_CHECK_LOCAL (customer)) {
        if (contacts && (customer->flags & PROXY_FLAG_SPEED_MORE || customer->speed))
          contacts[LIMIT_CHECK_CONTACT (customer->contact)]++;
        count++;
      }
  return count;
}

static int limit_count_ip (unsigned ip, int type) {
  simcustomer customer;
  int count = 0;
  for (customer = proxy_customer_list; customer; customer = customer->next)
    if (customer->type == type && customer->sock->err == SIM_OK && customer->ip == ip)
      if (type != PROXY_TYPE_SERVER || ! LIMIT_CHECK_CONTACT (customer->contact))
        count++;
  return count;
}

simunsigned limit_set_cpu (simunsigned oldcpu, simunsigned newcpu) {
  if (oldcpu) {
    if (newcpu) {
      if ((oldcpu = newcpu - oldcpu) == 0) {
        oldcpu = limit_cpu[LIMIT_CPU_HASH];
      } else if (limit_cpu_add)
        limit_cpu[LIMIT_CPU_TIME] += oldcpu;
    } else if (limit_cpu_add)
      limit_cpu[LIMIT_CPU_MAX] += oldcpu;
  }
  return oldcpu;
}

simnumber limit_get_cpu (void) {
#ifdef _WIN32
  return limit_cpu[LIMIT_CPU_TIME] / limit_cpu[LIMIT_CPU_RATE] + limit_cpu[LIMIT_CPU_MAX] / 1000000;
#else
  return (limit_cpu[LIMIT_CPU_TIME] + limit_cpu[LIMIT_CPU_MAX]) / 1000000;
#endif
}

unsigned limit_get_param (int param) {
  if (param == LIMIT_PARAM_SEND)
    return limit_speed_reply;
  if (param == LIMIT_PARAM_RECV)
    return limit_speed_recv;
  if (param == LIMIT_PARAM_REVERSE)
    return limit_reverse_timeout;
  if (param == LIMIT_PARAM_MEASURE) {
    int measure, minmeasure = abs (param_get_default_number ("limit.measure", 0)) / 2;
    return (measure = abs (param_get_number ("limit.measure"))) != 0 && measure < minmeasure ? minmeasure : measure;
  }
  return param == LIMIT_PARAM_MAX && param_get_number ("limit.measure") ? (unsigned) limit_measured_speed : 0;
}

int limit_get_speed_limit (int minlimit) {
  int limit = param_get_number_min ("limit.maxspeed", minlimit >> (10 - 1)) << 10, maxlimit = limit_measured_speed;
  minlimit = audio_status.client && audio_status.client != AUDIO_CLIENT_TEST ? 0 : minlimit << 1;
  if (! param_get_number ("limit.measure"))
    maxlimit = (param_get_max ("limit.maxspeed", 128) + 1) << 10;
  limit = (limit <= 0 || limit > maxlimit ? maxlimit : limit) * 100 / (param_get_number ("limit.handshakes") + 100);
  return limit < minlimit ? minlimit : limit;
}

static int limit_get_sockets (void) {
  int servers = limit_count_server (NULL, true);
  int minlimit = param_get_number ("limit.server") << 10, maxlimit = limit_get_speed_limit (minlimit) / minlimit;
  int minsockets = servers > maxlimit && servers ? servers : maxlimit ? maxlimit : 1;
  return param_get_number_min ("limit.sockets", socket_max_limit[SOCKET_MAX_PROXY] / minsockets);
}

static int limit_get_speed (simcustomer server, int flags) {
  simnumber minlimit = param_get_number ("limit.server") << 10;
  int limit = limit_get_speed_limit ((int) minlimit);
  int contacts[2] = { 0, 0 }, servers, restlimit, oldflags = server->flags;
  server->flags = flags;
  servers = limit_count_server (contacts, false);
  server->flags = oldflags;
  restlimit = (int) (limit > servers * minlimit ? limit - servers * minlimit : 0);
  if (LIMIT_CHECK_CONTACT (server->contact)) {
    if ((flags & PROXY_FLAG_SPEED_MORE || server->speed) && contacts[true])
      minlimit += restlimit / contacts[true];
    limit = param_get_number ("limit.contact") << 10;
  } else {
    if (contacts[true])
      if ((restlimit -= contacts[true] * ((param_get_number ("limit.contact") << 10) - (int) minlimit)) < 0)
        restlimit = 0;
    if ((flags & PROXY_FLAG_SPEED_MORE || server->speed) && contacts[false])
      minlimit += restlimit / contacts[false];
    limit = param_get_number ("limit.stranger") << 10;
  }
  return minlimit > limit ? limit : (int) minlimit;
}

int limit_get_speed_server (simcustomer server) {
  return server->number ? server->speed : LIMIT_CHECK_LOCAL (server) ? 0 : limit_get_speed (server, server->flags);
}

void limit_reset (simunsigned cpu, simunsigned tick) {
  limit_ip_count = 0;
  limit_ip_cpu = cpu;
  limit_ip_tick = tick;
}

static void limit_init_blacklisted (void) {
  limit_ip_cycles = limit_get_cpu ();
  limit_reset (sim_system_cpu_get (SYSTEM_CPU_TIME_PROCESS, NULL) / 1000000, system_get_tick ());
  limit_blacklist_tick = 0;
  limit_blacklist_timeout = param_get_number ("limit.blacklist") * 1000 / 2;
  limit_reverse_timeout = param_get_number_min ("proxy.reverse", param_get_default_number ("proxy.reverse", 0)) * 1000;
}

#ifndef HAVE_LIBPTH
#define LIMIT_CALC_CPULOAD(cpu, tick) \
  (100 * ((cpu) - (limit_ip_cpu)) / (((tick) - (limit_ip_tick)) * sim_system_cpu_count () + 1))
#else
#define LIMIT_CALC_CPULOAD(cpu, tick) (100 * ((cpu) - (limit_ip_cpu)) / ((tick) - (limit_ip_tick) + 1))
#endif

int limit_check_blacklisted (simcustomer customer, unsigned ip) {
  if (limit_blacklist_tick) {
    simunsigned tick = system_get_tick () - limit_blacklist_tick;
    if (tick >= limit_blacklist_timeout && limit_blacklist_count) {
      LOG_WARN_ ("socket $%d (%u unblacklisted)\n", customer->sock->fd, limit_blacklist_count);
      table_free (limit_blacklist_table);
      limit_blacklist_table = table_new_rand (limit_blacklist_max = param_get_number ("limit.blacklisted"));
      limit_blacklist_count = 0;
      if (limit_error != SIM_OK)
        event_send_name_number (NULL, SIM_EVENT_NET, SIM_EVENT_NET_LIMIT, limit_error = SIM_OK);
    }
    if (tick >= limit_blacklist_timeout << 1) {
      TABLE_FREE (&limit_ip_table, table_new_rand (limit_blacklist_max / 2));
      limit_init_blacklisted ();
      LOG_WARN_ ("socket $%d blacklist %u seconds\n", customer->sock->fd, limit_blacklist_timeout / 1000);
    }
  }
  if (limit_blacklist_count < limit_blacklist_max)
    if (! table_get_key_number (limit_blacklist_table, pointer_new_len (&ip, sizeof (ip))))
      return SIM_OK;
  return SIM_LIMIT_BLOCKED;
}

static int limit_blacklist (simcustomer customer, unsigned ip, int error) {
  int count = 0;
  if (limit_blacklist_timeout) {
    if (! customer) {
      for (customer = proxy_customer_list; customer; customer = customer->next)
        if (limit_check_anonymous (customer))
          count += limit_blacklist (customer, LIMIT_GET_IP_CUSTOMER (customer), error);
      LOG_WARN_ ("sockets not available (%d blacklisted)\n", count);
    } else if (customer->type != PROXY_TYPE_REVERSE) {
      if (customer->type == PROXY_TYPE_SERVER)
        proxy_cancel_server (customer, error);
      socket_cancel (customer->sock, error);
      limit_check_blacklisted (customer, ip);
      if (limit_blacklist_count >= limit_blacklist_max) {
        LOG_WARN_ ("socket $%d blacklist overflow\n", customer->sock->fd);
        if (socket_check_server (customer->sock))
          client_cancel_proxy (SIM_PROXY_BLACKLISTED);
      } else if (! PROXY_CHECK_LOCAL (ip)) {
        simtype key = string_copy_len (&ip, sizeof (ip));
        simnumber val = table_get_key_number (limit_blacklist_table, key) + 1;
        if (! limit_blacklist_count) {
          limit_blacklist_timeout <<= 1;
          LOG_WARN_ ("socket $%d blacklist %u seconds\n", customer->sock->fd, limit_blacklist_timeout / 1000);
        }
        if (table_set_key_number (limit_blacklist_table, key, val).typ == SIMNIL) {
          LOG_NOTE_ ("socket $%d blacklist %s\n", customer->sock->fd, network_convert_ip (ip));
          if (++limit_blacklist_count == limit_blacklist_max && ! param_get_number ("limit.cpu.event"))
            event_send_name_number (NULL, SIM_EVENT_NET, SIM_EVENT_NET_LIMIT, limit_error = SIM_LIMIT_CPU_LOAD);
          limit_blacklist_tick = system_get_tick ();
          return 1;
        }
      } else
        LOG_WARN_ ("socket $%d blacklist white ip %s\n", customer->sock->fd, network_convert_ip (ip));
    } else {
      if ((int) limit_reverse_timeout > 0)
        limit_reverse_timeout <<= 1;
      LOG_NOTE_ ("socket $%d blacklisted %s:%d (%u seconds)\n", customer->sock->fd,
                 network_convert_ip (customer->ip), customer->port, limit_reverse_timeout / 1000);
    }
  }
  return count;
}

static simcustomer limit_cancel_server (int error, simbool local) {
  simcustomer customer, newest = NULL;
  simnumber lasttick = 0;
  for (customer = proxy_customer_list; customer; customer = customer->next)
    if (customer->type == PROXY_TYPE_SERVER && customer->sock->err == SIM_OK)
      if (local || ! LIMIT_CHECK_LOCAL (customer))
        if (! LIMIT_CHECK_CONTACT (customer->contact) && customer->sock->created > lasttick)
          lasttick = (newest = customer)->sock->created;
  if (newest)
    proxy_cancel_server (newest, error);
  return newest;
}

int limit_test_socket_ (simcustomer server) {
  int oldsocket = param_get_number ("limit.oldsocket") * 1000, oldcount = 0, oldcancelled = 0, retry = 0;
  simcustomer customer = NULL;
  while (simself.state != CLIENT_STATE_STOPPED && (! server || server->sock->err == SIM_OK) && retry++ <= 1) {
    while (simself.state != CLIENT_STATE_STOPPED && (! server || server->sock->err == SIM_OK)) {
      int cancelled = 0, count = limit_count_socket (&cancelled, &customer);
      if (count < socket_max_limit[SOCKET_MAX_NEW])
        return SIM_OK;
      if (cancelled != oldcancelled || count != oldcount) {
        oldcancelled = cancelled;
        oldcount = count;
        LOG_NOTE_ ("sockets %d/%d cancelled\n", cancelled, count);
      }
      if (! cancelled)
        break;
      pth_usleep_ (100000);
    }
    if (server) {
      customer = server;
    } else if (oldsocket < 0 || (simnumber) system_get_tick () - customer->sock->created < oldsocket)
      customer = NULL;
    limit_blacklist (customer, ! customer ? 0 : LIMIT_GET_IP_CUSTOMER (customer), SIM_LIMIT_NO_SOCKETS);
  }
  return SIM_LIMIT_NO_SOCKETS;
}

int limit_test_client_ (simsocket sock, simcontact contact) {
  int oldcount = 0, oldcancelled = 0;
  simclient client = NULL;
  while (simself.state != CLIENT_STATE_STOPPED && (! sock || sock->err == SIM_OK)) {
    while (simself.state != CLIENT_STATE_STOPPED && (! sock || sock->err == SIM_OK)) {
      int cancelled = 0, count = client_count (&cancelled, contact, &client);
      if (count < socket_max_limit[SOCKET_MAX_CLIENTS])
        return SIM_OK;
      if (cancelled != oldcancelled || count != oldcount) {
        oldcancelled = cancelled;
        oldcount = count;
        LOG_NOTE_ ("sockets $%d %d/%d cancelled\n", sock ? sock->fd : -2, cancelled, count);
      }
      if (! cancelled)
        break;
      pth_usleep_ (100000);
    }
    if (! client)
      return SIM_LIMIT_NO_CLIENTS;
    client_cancel (client, SIM_LIMIT_NO_CLIENTS);
  }
  return sock ? sock->err : SIM_OK;
}

void limit_test_customer_ip (simcustomer customer, unsigned ip) {
  int load, count, cancelled, cpuevent = 0;
  simnumber tick;
  if ((count = param_get_number ("limit.cpu.load")) <= 0)
    return;
  if (++limit_ip_count > param_get_number ("limit.cpu.connections") && (load = audio_get_param (AUDIO_PARAM_CPU)) >= 0)
    if ((tick = system_get_tick ()) - limit_ip_tick >= param_get_number ("limit.cpu.time")) {
      simtype key;
      simwalker ctx;
      simnumber cpuload = limit_get_cpu (), cputime = sim_system_cpu_get (SYSTEM_CPU_TIME_PROCESS, NULL) / 1000000;
      simnumber cpu = cputime - (cpuload > limit_ip_cycles ? cpuload - limit_ip_cycles : cpuload);
      limit_ip_cycles = cpuload;
      load *= (int) ((tick - limit_ip_tick + 1) / 1000);
      cpuload = LIMIT_CALC_CPULOAD (cpu - load, tick - load);
      LOG_XTRA_ ("socket $%d cpu %lld%% tick=%lld-%lld cpu=%lld-%lld-%lld load=%d:%d\n", customer->sock->fd, cpuload,
                 tick, limit_ip_tick, cputime, cputime - cpu, limit_ip_cpu, audio_get_param (AUDIO_PARAM_CPU), load);
      if (tick - limit_ip_tick < load || (cpu - limit_ip_cpu >= load && cpuload >= count)) {
        count = cancelled = 0;
        for (table_walk_first (&ctx, limit_ip_table); table_walk_next (&ctx, &key).typ != SIMNIL;) {
          simcustomer tmp;
          unsigned oldip = *(unsigned *) key.ptr;
          count += limit_blacklist (customer, oldip, customer->sock->err);
          for (tmp = proxy_customer_list; tmp; tmp = tmp->next) {
            int t = tmp->type;
            if (t == PROXY_TYPE_NEW || t == PROXY_TYPE_ANONYMOUS || t == PROXY_TYPE_LOCAL)
              if ((t != PROXY_TYPE_LOCAL ? tmp->ip : LIMIT_GET_IP_LOCAL (tmp)) == oldip && tmp->sock->err == SIM_OK) {
                cancelled++;
                socket_cancel (tmp->sock, SIM_LIMIT_CPU_LOAD);
              }
          }
        }
        if (count && limit_error != SIM_LIMIT_CPU_LOAD && (cpuevent = param_get_number ("limit.cpu.event")) > 0)
          event_send_name_number (NULL, SIM_EVENT_NET, SIM_EVENT_NET_LIMIT, limit_error = SIM_LIMIT_CPU_LOAD);
        LOG_ANY_ (cpuevent > 1 ? SIM_LOG_ERROR : SIM_LOG_WARN, "socket $%d cpu %lld%% (%d blacklisted, %d cancelled)\n",
                  customer->sock->fd, cpuload, count, cancelled);
        LOG_CODE_DEBUG_ (pth_log_threads (SIM_MODULE, SIM_LOG_DEBUG));
      } else
        LOG_CODE_XTRA_ (pth_log_threads (SIM_MODULE, SIM_LOG_XTRA));
      LOG_DEBUG_ ("socket $%d cpu %lld%% (%u forgotten)\n", customer->sock->fd,
                  cpuload, table_count (limit_blacklist_table));
      if (cpuload > 100 || (cpuload < 0 && cpu - limit_ip_cpu >= load))
        LOG_ANY_ (cpuload > 150 ? SIM_LOG_ERROR : SIM_LOG_WARN,
                  "socket $%d cpu %lld%% tick=%lld-%lld-%lld cpu=%lld-%lld load=%d:%d\n", customer->sock->fd, cpuload,
                  tick, limit_ip_tick, cputime, cputime - cpu, limit_ip_cpu, audio_get_param (AUDIO_PARAM_CPU), load);
      TABLE_FREE (&limit_ip_table, table_new_rand (limit_blacklist_max / 2));
      limit_reset (cputime, tick);
    }
  table_add_key_number (limit_ip_table, string_copy_len (&ip, sizeof (ip)), true);
}

static int limit_test_customer (simcustomer client, simcustomer server) {
  while (limit_count_client (NULL) >= socket_max_limit[SOCKET_MAX_PROXY]) {
    simnumber lasttick[2] = { 0, 0 };
    simcustomer customer, newest[2] = { NULL, NULL }, owner;
    int clients = limit_count_client (server->sock->master->from.ptr), minsockets = limit_get_sockets ();
    LOG_DEBUG_ ("socket $%d dropping %d/%d clients '%s'\n", client->sock->fd, clients, limit_count_client (NULL),
                CONTACT_GET_NICK (server->sock->master->from.ptr));
    for (customer = proxy_customer_list; customer; customer = customer->next)
      if (customer->type == PROXY_TYPE_CLIENT && customer->sock->err == SIM_OK) {
        owner = proxy_find_server (customer->sock->master->to.ptr);
        if (owner && limit_count_client (owner->sock->master->from.ptr) >= minsockets) {
          int stranger = ! LIMIT_CHECK_CONTACT (owner->contact);
          if (owner != server && customer->sock->created > lasttick[stranger] && customer->number != owner->number)
            lasttick[stranger] = (newest[stranger] = customer)->sock->created;
        }
      }
    if (newest[true]) {
      newest[false] = newest[true];
    } else if (! newest[false] || (! LIMIT_CHECK_CONTACT (server->contact) && clients + 1 >= minsockets))
      return SIM_LIMIT_NO_CUSTOMERS;
    owner = proxy_find_server (newest[false]->sock->master->to.ptr);
    LOG_DEBUG_ ("socket $%d dropping %s $%d #%d (%d/%d) '%s'\n", client->sock->fd, newest[true] ? "client" : "contact",
                newest[false]->sock->fd, newest[false]->number, limit_count_client (owner->sock->master->from.ptr),
                minsockets, CONTACT_GET_NICK (owner->sock->master->from.ptr));
    socket_cancel (newest[false]->sock, SIM_LIMIT_NO_CUSTOMERS);
  }
  if (! LIMIT_CHECK_LOCAL (server))
    socket_set_buffer (client->sock, param_get_number ("socket.tcp.proxy"), true);
  client->limit = param_get_number ("limit.server") << 10;
  return SIM_OK;
}

int limit_test_client_ip (simcustomer client, simcustomer server) {
  int err = limit_test_customer (client, server);
  if (err == SIM_OK && ! PROXY_CHECK_LOCAL (client->ip) && ! LIMIT_CHECK_LOCAL (client)) {
    int count = param_get_number ("limit.connections"), num;
    if (count > (num = socket_max_limit[SOCKET_MAX_PROXY] / param_get_number ("limit.sockets")) || ! count)
      count = num ? num : 1;
    if (limit_count_ip (client->ip, PROXY_TYPE_CLIENT) >= count)
      err = SIM_LIMIT_NO_CONNECTIONS;
  }
  return err;
}

int limit_test_server (simcustomer server) {
  int n, sockets = param_get_number ("limit.sockets");
  simbool stranger = true;
  if (server && LIMIT_CHECK_CONTACT (server->contact))
    stranger = false;
  if (! server || ! LIMIT_CHECK_LOCAL (server)) {
    simnumber minlimit = param_get_number ("limit.server") << 10;
    int limit = limit_get_speed_limit ((int) minlimit);
    int maxlimit = param_get_number_max (stranger ? "limit.stranger" : "limit.contact", (int) minlimit >> 10) << 10;
    for (n = limit_count_server (NULL, false); limit - n * minlimit < maxlimit; n--)
      if (stranger || ! limit_cancel_server (SIM_LIMIT_NO_BANDWIDTH, false))
        return SIM_LIMIT_NO_BANDWIDTH;
  }
  for (n = limit_count_server (NULL, true); sockets * (n + 1) > socket_max_limit[SOCKET_MAX_PROXY]; n--)
    if (stranger || ! limit_cancel_server (SIM_LIMIT_NO_SERVERS, true))
      return SIM_LIMIT_NO_SERVERS;
  return server ? limit_test_customer (server, server) : SIM_OK;
}

int limit_test_server_ip (simcustomer server) {
  int count = param_get_number ("limit.servers");
  if (count > 0 && ! LIMIT_CHECK_CONTACT (server->contact) && ! PROXY_CHECK_LOCAL (server->ip))
    if (! LIMIT_CHECK_LOCAL (server) && limit_count_ip (server->ip, PROXY_TYPE_SERVER) >= count)
      return SIM_LIMIT_NO_CONNECTIONS;
  return SIM_OK;
}

int limit_test_handshake (simcustomer client, simcustomer server, simcustomer stats) {
  int clients = LIMIT_CHECK_LOCAL (server) ? 0 : limit_count_client (server->sock->master->from.ptr);
  if (clients * param_get_number ("limit.client") >= limit_get_speed (server, server->flags) - server->speed) {
    LOG_DEBUG_ ("socket $%d:$%d clients %d*%d >= %d-%d bytes/sec\n", client->sock->fd, server->sock->fd,
                param_get_number ("limit.client"), clients, limit_get_speed (server, server->flags), server->speed);
    return SIM_LIMIT_NO_HANDSHAKE;
  }
#ifndef DONOT_DEFINE
  if (stats) {
    unsigned i;
    for (i = CONTACT_STAT_RECEIVED; i <= CONTACT_STAT_SENT; i++) {
      simnumber bytes = socket_get_stat (client->sock, i, false);
      if (! server->contact) {
        proxy_set_stat (proxy_get_stat (PROXY_STATS_INPUT, i) + bytes, PROXY_STATS_INPUT, i);
      } else
        server->contact->stats[CONTACT_STATS_INPUT][i] += bytes;
    }
    SOCKET_ADD_STAT (&stats[PROXY_STAT_TOTAL].sock->old, client->sock);
    SOCKET_ADD_STAT (&stats[PROXY_STAT_HANDSHAKES].sock->old, client->sock);
    SOCKET_INIT_STAT (client->sock);
  }
#endif
  return SIM_OK;
}

int limit_test_udp (simcustomer customer, int length) {
  int err = SIM_OK;
  if (customer->type != PROXY_TYPE_PROXY ? limit_udp_speed > 0 : limit_udp_speed) {
    int check = param_get_number ("limit.time");
    simnumber tick = system_get_tick (), elapsed;
    if (customer->type == PROXY_TYPE_CLIENT && (customer = proxy_find_server (customer->sock->master->to.ptr)) == NULL)
      return SIM_LIMIT_NO_SPEED;
    elapsed = tick - customer->measured[LIMIT_STAT_UDP];
    if (length <= 0) {
      length = -length;
    } else if (customer->bytes[LIMIT_STAT_UDP] > abs (limit_udp_speed) * (elapsed / (check * 1000) + 1) * check) {
      LOG_XTRA_ ("speed $%d %lld > %d bytes/sec (%lld ms)\n", customer->sock->fd,
                 customer->bytes[LIMIT_STAT_UDP] * 1000 / (elapsed + 1), abs (limit_udp_speed), elapsed);
      err = SIM_LIMIT_NO_SPEED;
    } else if (tick - customer->measured[LIMIT_STAT_UDP] >= check * 1000) {
      customer->measured[LIMIT_STAT_UDP] = tick;
      customer->bytes[LIMIT_STAT_UDP] = 0;
    }
    customer->bytes[LIMIT_STAT_UDP] += length + SOCKET_CALC_OVERHEAD_UDP ((unsigned) length);
  } else if (customer->type != PROXY_TYPE_PROXY)
    err = SIM_LIMIT_NO_SPEED;
  return err;
}

static int limit_check_socket (simcustomer client) {
  int err;
  if (! client) {
    err = simself.state != CLIENT_STATE_STOPPED ? SIM_OK : SIM_CLIENT_OFFLINE;
  } else if ((err = client->sock->err) == SIM_OK && (err = socket_get_peer (client->sock, NULL, NULL)) != SIM_OK)
    socket_cancel (client->sock, err);
  return err;
}

static void limit_sleep_ (simnumber mseconds) {
  if (mseconds < 1000) {
    pth_usleep_ (((unsigned) mseconds + 1) * 1000 - 1);
  } else
    pth_sleep_ (1);
}

int limit_wait_server_ (simcustomer server, simcustomer client) {
  int err = SIM_OK, limit, check = 0, burst, factor;
  simnumber tick, elapsed, rcvd, sent, bytes, talkingbytes = 0;
  simcustomer customer = NULL, tmp = client ? client : server;
  if (LIMIT_CHECK_LOCAL (server))
    return SIM_OK;
#ifdef DONOT_DEFINE
  while (server->waiters[LIMIT_STAT_SERVER] && (err = limit_check_socket (client)) == SIM_OK)
    pth_usleep_ (100000);
  server->waiters[LIMIT_STAT_SERVER]++;
  while (server->flags & PROXY_FLAG_SPEED_STOP && (err = limit_check_socket (client)) == SIM_OK)
    pth_usleep_ (100000);
  server->waiters[LIMIT_STAT_SERVER]--;
#endif
  proxy_get_stat_server (server, &rcvd, &sent);
  if ((bytes = rcvd + sent - server->bytes[LIMIT_STAT_SERVER]) < 0)
    LOG_WARN_ ("speed $%d %lld %s\n", server->sock->fd, bytes, customer ? "BYTES" : "bytes");
  if (server->speed)
    if ((customer = proxy_find_client (server->sock->master->from.ptr, server->number, PROXY_TYPE_CLIENT)) != NULL)
      bytes = ((talkingbytes = LIMIT_GET_STAT (server, customer)) - customer->bytes[LIMIT_STAT_SERVER]) << 1;
  elapsed = (tick = system_get_tick ()) - server->measured[LIMIT_STAT_SERVER];
  if ((limit = limit_get_speed (server, server->flags)) != tmp->limit) {
    int minlimit = param_get_number ("limit.server") << 10;
    LOG_DEBUG_ ("speed $%d limit %d -> %d %s/sec\n", tmp->sock->fd, tmp->limit, limit, customer ? "BYTES" : "bytes");
    tmp->limit = limit;
    socket_set_buffer (tmp->sock, (int) ((simnumber) limit * param_get_number ("socket.tcp.proxy") / minlimit), true);
  } else if (elapsed < (check = param_get_number ("limit.time")) * 1000) {
    if (bytes < limit * check)
      return err;
  } else if (server->flags & PROXY_FLAG_SPEED_MORE) {
    burst = limit_get_speed (server, server->flags & ~PROXY_FLAG_SPEED_MORE) * param_get_number ("limit.burst") / 100;
    if (bytes >= burst * elapsed / 1000) {
      server->flags &= ~PROXY_FLAG_SPEED_LESS;
    } else if (server->flags & PROXY_FLAG_SPEED_LESS) {
      server->flags &= ~(PROXY_FLAG_SPEED_MORE | PROXY_FLAG_SPEED_LESS);
      LOG_DEBUG_ ("speed $%d %lld < %d %s/sec (%lld ms)\n", server->sock->fd,
                  elapsed ? bytes * 1000 / elapsed : 0, burst, customer ? "BYTES" : "bytes", elapsed);
    } else
      server->flags |= PROXY_FLAG_SPEED_LESS;
  }
  if (bytes >= limit * elapsed / 1000 && check) {
    int delay = (int) (bytes * 1000 / limit - elapsed);
    LOG_DEBUG_ ("speed $%d %lld > %d %s/sec (%lld ms): sleep %d ms\n", server->sock->fd,
                elapsed ? bytes * 1000 / elapsed : 0, limit, customer ? "BYTES" : "bytes", elapsed, delay);
    if ((factor = param_get_number ("limit.blackfactor")) > 1 && delay >= check * factor * 1000) {
      LOG_WARN_ ("speed $%d %lld+%lld >= %lld %s\n", server->sock->fd,
                 rcvd, sent, server->bytes[LIMIT_STAT_SERVER], customer ? "BYTES" : "bytes");
      limit_blacklist (server, server->ip, SIM_LIMIT_NO_SPEED);
      return err;
    }
    server->flags = (server->flags | PROXY_FLAG_SPEED_STOP | PROXY_FLAG_SPEED_MORE) & ~PROXY_FLAG_SPEED_LESS;
    while ((err = limit_check_socket (client)) == SIM_OK && (elapsed = system_get_tick () - tick) < delay)
      limit_sleep_ (delay - elapsed);
    server->flags &= ~PROXY_FLAG_SPEED_STOP;
  } else {
    server->measured[LIMIT_STAT_SERVER] = tick;
    server->bytes[LIMIT_STAT_SERVER] = rcvd + sent;
    if (customer)
      customer->bytes[LIMIT_STAT_SERVER] = talkingbytes;
  }
  return err;
}

int limit_wait_client_ (simcustomer client, simcustomer server) {
  int err = SIM_OK, limit, check, speed = server->speed, burst;
  simnumber tick, elapsed, rcvd, sent, bytes, talkingbytes;
  simcustomer customer = NULL;
  if (server->flags & PROXY_FLAG_SPEED_SEND)
    if ((burst = limit_get_speed (server, server->flags | PROXY_FLAG_SPEED_MORE)) != server->burst)
      if (burst < server->burst || system_get_tick () >= (simunsigned) server->measured[LIMIT_STAT_REPORT]) {
        simtype limits, reply = limit_new_cmd (LIMIT_CMD_REPLY);
        reply.arr[1] = reply.arr[2];
        reply.arr[2] = number_new (server->burst = burst);
        table_add_pointer (limits = table_new_long (2), SIM_SERVER_CMD, SIM_SERVER_CMD_SHAKEHAND_REPLY);
        table_add (limits, SIM_SERVER_CMD_SHAKEHAND_SPEED, reply);
        server->measured[LIMIT_STAT_REPORT] = system_get_tick () + param_get_number ("proxy.report") * 1000;
        err = socket_send_table_ (server->sock, limits, SOCKET_SEND_TCP, NULL);
        table_free (limits);
        if (err != SIM_OK)
          return err;
      }
  if (speed)
    if ((customer = proxy_find_client (server->sock->master->from.ptr, server->number, PROXY_TYPE_CLIENT)) == NULL)
      LOG_WARN_ ("speed $%d:$%d #%d:#%d\n", server->sock->fd, client->sock->fd, server->number, client->number);
  if (customer && customer != client) {
#ifdef DONOT_DEFINE
    while (server->waiters[LIMIT_STAT_CLIENT] && (err = limit_check_socket (client)) == SIM_OK)
      pth_usleep_ (100000);
    server->waiters[LIMIT_STAT_CLIENT]++;
    while (server->flags & PROXY_FLAG_SPEED_TALK && (err = limit_check_socket (client)) == SIM_OK)
      pth_usleep_ (100000);
    server->waiters[LIMIT_STAT_CLIENT]--;
#endif
    if (server->speed) {
      proxy_get_stat_server (server, &rcvd, &sent);
      bytes = ((talkingbytes = LIMIT_GET_STAT (server, customer)) - customer->bytes[LIMIT_STAT_CLIENT]) << 1;
      elapsed = (tick = system_get_tick ()) - server->measured[LIMIT_STAT_CLIENT];
      if (bytes > server->speed * elapsed / 1000) {
        LOG_XTRA_ ("speed $%d:$%d %lld > %lld bytes (%lld ms)\n", server->sock->fd, client->sock->fd,
                   bytes, server->speed * elapsed / 1000, elapsed);
        bytes = server->speed * elapsed / 1000;
      }
      bytes = rcvd + sent - server->bytes[LIMIT_STAT_CLIENT] - bytes;
      limit = limit_get_speed (server, server->flags) - server->speed;
      if (elapsed < (check = param_get_number ("limit.time")) * 1000 && bytes < limit * check)
        return err;
      if (bytes >= limit * elapsed / 1000) {
        int delay = limit > 0 ? (int) (bytes * 1000 / limit - elapsed) : 1 << 30;
        LOG_XTRA_ ("speed $%d:$%d %lld > %d bytes/sec (%lld ms): sleep %d ms\n", server->sock->fd, client->sock->fd,
                   elapsed ? bytes * 1000 / elapsed : 0, limit, elapsed, delay);
        for (server->flags |= PROXY_FLAG_SPEED_TALK; server->flags & PROXY_FLAG_SPEED_TALK;) {
          if ((err = limit_check_socket (client)) != SIM_OK || (elapsed = system_get_tick () - tick) >= delay)
            break;
          limit_sleep_ (delay - elapsed);
        }
        if (server->flags & PROXY_FLAG_SPEED_TALK) {
          server->flags &= ~PROXY_FLAG_SPEED_TALK;
        } else
          LOG_NOTE_ ("speed $%d:$%d #%d:#%d\n", server->sock->fd, client->sock->fd, server->number, client->number);
      } else {
        server->measured[LIMIT_STAT_CLIENT] = tick;
        server->bytes[LIMIT_STAT_CLIENT] = rcvd + sent;
        customer->bytes[LIMIT_STAT_CLIENT] = talkingbytes;
      }
      return err;
    }
  }
  return err == SIM_OK ? limit_wait_server_ (server, client) : err;
}

void limit_wait_handshake_ (simcustomer customer, simcustomer stats) {
  int handshakes = param_get_number ("limit.handshakes");
  if (handshakes) {
    if (stats[PROXY_STAT_HANDSHAKES].flags & PROXY_FLAG_SPEED_STOP || LIMIT_CHECK_LOCAL (customer)) {
      while (stats[PROXY_STAT_HANDSHAKES].flags & PROXY_FLAG_SPEED_STOP && customer->sock->err == SIM_OK)
        pth_sleep_ (1);
    } else {
      int limit = limit_get_speed_limit (param_get_number ("limit.server") << 10);
      simnumber rcvd, sent, created = proxy_get_stat_server (&stats[PROXY_STAT_HANDSHAKES], &rcvd, &sent);
      simnumber tick = system_get_tick (), duration = created ? tick - created + 1 : 1;
      simnumber bytes = PROXY_GET_STAT (CONTACT_STAT_RECEIVED) + PROXY_GET_STAT (CONTACT_STAT_SENT);
      simnumber hsbytes = rcvd + sent - *stats[PROXY_STAT_HANDSHAKES].bytes, hspeed = hsbytes * 1000 / duration;
      simnumber speed = (bytes - *stats[PROXY_STAT_TOTAL].bytes - hsbytes) * 1000 / duration;
      simnumber maxspeed = (limit > speed ? limit - speed : 0) + limit * handshakes / 100;
      if (hspeed > maxspeed) {
        int delay = (int) (hsbytes * 1000 / (maxspeed ? maxspeed : 1) - duration);
        LOG_DEBUG_ ("speed $%d %lld+%lld > %lld bytes/sec: sleep %d ms\n",
                    customer->sock->fd, speed, hspeed, maxspeed, delay);
        stats[PROXY_STAT_HANDSHAKES].flags |= PROXY_FLAG_SPEED_STOP;
        while (customer->sock->err == SIM_OK && (simnumber) system_get_tick () - tick < delay)
          pth_sleep_ (1);
        stats[PROXY_STAT_HANDSHAKES].flags &= ~PROXY_FLAG_SPEED_STOP;
      } else {
        *stats[PROXY_STAT_TOTAL].bytes = bytes;
        stats[PROXY_STAT_HANDSHAKES].sock->created = tick;
        *stats[PROXY_STAT_HANDSHAKES].bytes = rcvd + sent;
      }
    }
  }
}

static simtype limit_new_ping (simsocket sock, const char *cmd, const char *key, simtype value) {
  if (sock->client)
    return client_new_cmd (SIM_CMD_PING, key, value, NULL, nil ());
  return proxy_new_cmd (SIM_SERVER_CMD_PING, cmd, value, true);
}

simbool limit_start_ping (simsocket sock, simcontact contact, simunsigned tick,
                          simunsigned received, simunsigned sent, int measure) {
  if (limit_ping_sock != sock) {
    simbool ok;
    if (LIMIT_CHECK_CONTACT (contact)) {
      ok = tick >= limit_ping_tick || (limit_average_size >= 0 && (! limit_ping_sock || ! limit_ping_contact));
    } else
      ok = tick >= limit_ping_tick && limit_average_size >= 0;
    if (ok) {
      simtype table = limit_new_ping (sock, NULL, NULL, nil ());
      int limit = param_get_number ("limit.maxspeed");
      if (limit_ping_sock)
        LOG_INFO_ ("speed $%d -> $%d\n", limit_ping_sock->fd, sock->fd);
      limit_ping_sock = sock;
      limit_ping_contact = contact;
      limit_ping_tick = tick + measure;
      limit_ping_ticks[limit_ping_count = 0] = tick;
      limit_ping_sizes[0] = table_size (table, 0);
      limit_ping_data[0][0] = received;
      limit_ping_data[1][0] = sent;
      table_free (table);
      if (limit_measured_speed_contacts < (limit > 0 ? limit : param_get_max ("limit.maxspeed", 128) + 1) << 10) {
        LOG_DEBUG_ ("speed $%d ping0\n", sock->fd);
        return true;
      }
      limit_ping_sock = NULL;
    }
  }
  return false;
}

int limit_ping_ (simsocket sock, simcontact contact, simunsigned tick, unsigned size) {
  int err = SIM_OK;
  if (limit_ping_sock == sock) {
    int i, n = limit_ping_count++, m = n > 1 ? 2 : 0;
    simbool measure = param_get_number ("limit.measure") > 0;
    simnumber rcvd = SOCKET_GET_STAT_RECEIVED (sock), sent = SOCKET_GET_STAT_SENT (sock);
    limit_ping_ticks[n] = (tick - limit_ping_ticks[n]) + 1;
    limit_ping_data[0][n] = rcvd - limit_ping_data[0][n];
    limit_ping_data[1][n] = sent - limit_ping_data[1][n];
    LOG_DEBUG_ ("speed $%d ping%d %llu:%llu/%llu bytes/ms\n", sock->fd, n + 1,
                limit_ping_data[0][n], limit_ping_data[1][n], limit_ping_ticks[n]);
    if (size && sim_crypt_auth_size (sock->crypt.encrypt) * 2 + SOCKET_OVERHEAD_SSL + limit_ping_sizes[m] > size) {
      LOG_NOTE_ ("speed $%d bad pong (wanted %u, got %u bytes)\n", sock->fd, limit_ping_sizes[n], size);
      limit_ping_sock = NULL;
      err = SIM_NO_ERROR;
    } else if (++n <= measure + 1) {
      int oldmaxsize = sock->crypt.maxsize;
      int maxsize = param_get_min ("socket.packet", SIM_MAX_PACKET_SIZE) - SOCKET_OVERHEAD_SSL;
      const char *pad = n == 1 ? SIM_CMD_PING_PAD : SIM_CMD_PONG_PAD;
      simtype table = nil (), pingpongpad = string_buffer_new (maxsize);
      random_get (random_public, pointer_new_len (pingpongpad.str, sock->crypt.maxsize = maxsize));
      do {
        simtype p = pointer_new_len (pingpongpad.str, --maxsize);
        TABLE_FREE (&table, limit_new_ping (sock, n == 1 ? SIM_SERVER_CMD_PING_PAD : SIM_SERVER_CMD_PONG_PAD, pad, p));
      } while (! socket_size_table (sock, table, SOCKET_SEND_TCP));
      sock->crypt.maxsize = oldmaxsize;
      limit_ping_sizes[n] = table_size (table, 0);
      limit_ping_ticks[n] = system_get_tick ();
      limit_ping_data[0][n] = rcvd;
      limit_ping_data[1][n] = sent;
      if (sock->client) {
        table_set (table, pad, string_buffer_truncate (pingpongpad, maxsize));
        err = client_send_ (sock->client, table);
      } else {
        err = socket_send_table_ (sock, table, SOCKET_SEND_TCP, NULL);
        table_free (table);
        string_buffer_free (pingpongpad);
      }
      if (err == SIM_OK)
        err = SIM_LIMIT_NO_ERROR;
    } else {
      simnumber speed[2], t;
      /*limit_ping_data[0][1] = limit_ping_data[0][0];*/
      speed[0] = speed[1] = 0;
      for (i = 1; i >= 1 - measure; i--) {
        if ((t = limit_ping_ticks[2 - i] - limit_ping_ticks[1 - i]) < limit_ping_ticks[1 - i])
          t = limit_ping_ticks[1 - i];
        speed[i] = ((limit_ping_data[i][2 - i] - limit_ping_data[i][1 - i]) * 1000 / t);
      }
      LOG_DEBUG_ ("speed $%d recv %lld bytes/sec, send %lld bytes/sec\n", sock->fd, speed[0], speed[1]);
      t = (measure && speed[0] < speed[1] ? speed[0] : speed[1]) * param_get_number ("limit.factor") / 50;
      if ((t -= param_get_number ("limit.reserved") << 10) > param_get_number ("limit.server") << (10 + 1)) {
        int maxlimit = (param_get_max ("limit.maxspeed", 128) + 1) << 10;
        if (t > maxlimit)
          t = maxlimit;
        if (LIMIT_CHECK_CONTACT (contact)) {
          if ((int) t > limit_measured_speed_contacts) {
            limit_measured_speed = limit_measured_speed_contacts = (int) t;
            LOG_INFO_ ("speed $%d max %lld bytes/sec\n", sock->fd, t);
          }
          SIM_FREE (&limit_average_buffer, sizeof (*limit_average_buffer) * limit_average_size, NULL);
          limit_average_size = -1;
        } else if (limit_average_size > 0) {
          int *avgbuf = limit_average_buffer;
          if (limit_average_index == limit_average_size)
            memmove (avgbuf, avgbuf + 1, --limit_average_index * sizeof (*avgbuf));
          avgbuf[limit_average_index++] = (int) (t += (param_get_number ("limit.reserved") << 10));
          for (i = 0; i < limit_average_index - 1; i++)
            t += avgbuf[i];
          limit_measured_speed = (int) (t / limit_average_index) - (param_get_number ("limit.reserved") << 10);
          LOG_INFO_ ("speed $%d average %d bytes/sec\n", sock->fd, limit_measured_speed);
        } else if (! limit_average_size && (int) t > limit_measured_speed) {
          limit_measured_speed = (int) t;
          LOG_INFO_ ("speed $%d max %lld bytes/sec\n", sock->fd, t);
        }
      }
      limit_ping_sock = NULL;
      err = SIM_NO_ERROR;
    }
  }
  return err;
}

void limit_stop_ping (simsocket sock) {
  if (limit_ping_sock == sock)
    limit_ping_sock = NULL;
}

simtype limit_new_cmd (int cmd) {
  simtype table = nil ();
  if (cmd == LIMIT_CMD_REPLY) {
    table = array_new_types (2, number_new (param_get_number ("limit.client")));
    table.arr[2] = number_new (param_get_number ("limit.server") << 10);
  } else if (cmd == LIMIT_CMD_STOP) {
    table_add_pointer (table = table_new_long (2), SIM_SERVER_CMD, SIM_SERVER_CMD_SHAKEHAND);
    table_add_number (table, SIM_SERVER_CMD_SHAKEHAND_NUMBER, -1);
    table_add_number (table, SIM_SERVER_CMD_SHAKEHAND_SPEED, -1);
  } else if (cmd == LIMIT_CMD_START && limit_speed_request == LIMIT_SPEED_START) {
    table_add_pointer (table = table_new_long (2), SIM_SERVER_CMD, SIM_SERVER_CMD_SHAKEHAND);
    table_add_number (table, SIM_SERVER_CMD_SHAKEHAND_SPEED, -1);
    limit_speed_request = LIMIT_SPEED_SEND;
  } else if (cmd == LIMIT_CMD_REQUEST && limit_speed_number >= 0) {
    table_add_pointer (table = table_new_long (2), SIM_SERVER_CMD, SIM_SERVER_CMD_SHAKEHAND);
    table_add_number (table, SIM_SERVER_CMD_SHAKEHAND_NUMBER, limit_speed_number);
    table_add_number (table, SIM_SERVER_CMD_SHAKEHAND_SPEED, limit_speed_send);
    limit_speed_number = -1;
  }
  return table;
}

static simnumber limit_parse_cmd (const simtype limits) {
  simnumber speed = limits.len >= 1 ? limits.arr[1].num : 0;
  return limits.len >= 2 && limits.arr[2].num > speed ? limits.arr[2].num : speed;
}

simcustomer limit_exec_request (simcustomer server, simtype *table) {
  int maxspeed = param_get_number ("limit.server") << 10, flags = SOCKET_FLAG_PING, report;
  simtype num = table_get_type_number (*table, SIM_SERVER_CMD_SHAKEHAND_NUMBER), limits;
  simnumber speed = limit_parse_cmd (limits = table_get_array_number (*table, SIM_SERVER_CMD_SHAKEHAND_SPEED));
  simcustomer client = NULL;
  if (num.typ != SIMNIL) {
    if (num.num && (client = proxy_find_client (server->sock->master->from.ptr, num.num, PROXY_TYPE_CLIENT)) == NULL) {
      limits = nil ();
    } else {
      LOG_DEBUG_SIMTYPE_ (limits, 0, "speed $%d request #%lld ", server->sock->fd, num.num);
      if (num.num) {
        if ((server->speed = ! SIM_CHECK_POSITIVE (speed) ? 0 : speed > maxspeed ? maxspeed : (int) speed) != 0) {
          client->sock->flags = (flags = client->sock->flags) | SOCKET_FLAG_PING | SOCKET_FLAG_PING_PONG;
        } else
          client->sock->flags &= ~SOCKET_FLAG_PING_PONG;
        if (LIMIT_CHECK_LOCAL (server)) {
          speed = server->speed = 0;
        } else if (server->speed) {
          client->bytes[LIMIT_STAT_SERVER] = client->bytes[LIMIT_STAT_CLIENT] = LIMIT_GET_STAT (server, client);
        } else
          server->flags &= ~PROXY_FLAG_SPEED_TALK;
      }
      if (! speed || num.num)
        server->number = speed ? (unsigned) num.num : 0;
    }
  } else if (limits.typ != SIMNIL || table_get_type_number (*table, SIM_SERVER_CMD_SHAKEHAND_SOCKETS).typ != SIMNIL)
    LOG_DEBUG_SIMTYPE_ (limits, 0, "speed $%d request ", server->sock->fd);
  if (limits.typ != SIMNIL) {
    table_add_pointer (limits = table_new_long (2), SIM_SERVER_CMD, SIM_SERVER_CMD_SHAKEHAND_REPLY);
    if (num.typ != SIMNIL) {
      table_add (limits, SIM_SERVER_CMD_SHAKEHAND_NUMBER, num);
    } else if (LIMIT_CHECK_CONTACT (server->contact) && speed >= 0)
      table_add_number (limits, SIM_SERVER_CMD_SHAKEHAND_SOCKETS, limit_get_sockets ());
    if (! LIMIT_CHECK_LOCAL (server)) {
      if (speed > 0)
        table_add_number (limits, SIM_SERVER_CMD_SHAKEHAND_TIMES, param_get_number ("limit.time") * 1000);
      if (num.typ != SIMNIL || speed != -1) {
        table_add_number (limits, SIM_SERVER_CMD_SHAKEHAND_SPEED, speed ? maxspeed : 0);
      } else if (! (server->flags & PROXY_FLAG_SPEED_SEND) && (report = param_get_number ("proxy.report")) != 0) {
        simtype reply = limit_new_cmd (LIMIT_CMD_REPLY);
        reply.arr[1] = reply.arr[2];
        reply.arr[2] = number_new (server->burst = limit_get_speed (server, server->flags | PROXY_FLAG_SPEED_MORE));
        table_add (limits, SIM_SERVER_CMD_SHAKEHAND_SPEED, reply);
        server->measured[LIMIT_STAT_REPORT] = system_get_tick () + report * 1000;
        server->flags |= PROXY_FLAG_SPEED_SEND;
      } else
        TABLE_FREE (&limits, nil ());
    }
  } else if (num.typ != SIMNIL && num.num == -1)
    server->flags &= ~PROXY_FLAG_SPEED_SEND;
  *table = limits;
  return flags & SOCKET_FLAG_PING ? NULL : client;
}

int limit_exec_reply (simcustomer proxy, const simtype table) {
  int err = SIM_OK;
  simtype limits = table_get_array_number (table, SIM_SERVER_CMD_SHAKEHAND_SPEED), tmp;
  if (limits.typ != SIMNIL) {
    simnumber speed = limit_parse_cmd (limits);
    if ((tmp = table_get_type_number (table, SIM_SERVER_CMD_SHAKEHAND_NUMBER)).typ != SIMNIL) {
      LOG_DEBUG_SIMTYPE_ (limits, 0, "speed $%d report #%lld ", proxy->sock->fd, tmp.num);
      if (speed >= 0 && speed == (unsigned) speed && (limit_speed_recv = (int) speed) != 0)
        if (audio_get_param (AUDIO_PARAM_SPEED) <= 0)
          LOG_WARN_ ("speed $%d too low (%lld bytes/sec)\n", proxy->sock->fd, speed);
    } else {
      LOG_DEBUG_SIMTYPE_ (limits, 0, "speed $%d report ", proxy->sock->fd);
      if (speed < PROXY_SPEED_AUDIO) {
        err = SIM_LIMIT_NO_BANDWIDTH;
      } else if (speed == (int) speed && limits.len >= 2) {
        limit_speed_reply = (int) speed;
        if (limit_speed_request == LIMIT_SPEED_STOP) {
          if ((err = socket_send (proxy->sock, tmp = limit_new_cmd (LIMIT_CMD_STOP), SOCKET_SEND_TCP, NULL)) == SIM_OK)
            limit_speed_reply = 0;
          table_free (tmp);
        }
      }
    }
  }
  if ((limits = table_get_type_number (table, SIM_SERVER_CMD_SHAKEHAND_TIMES)).typ != SIMNIL)
    LOG_DEBUG_SIMTYPE_ (limits, 0, "speed $%d time ", proxy->sock->fd);
  if ((limits = table_get_type_number (table, SIM_SERVER_CMD_SHAKEHAND_SOCKETS)).typ != SIMNIL)
    LOG_DEBUG_SIMTYPE_ (limits, 0, "speed $%d sockets ", proxy->sock->fd);
  return err;
}

int limit_send_request (simclient client, int speed) {
  if (speed == LIMIT_SPEED_STOP || (! limit_speed_reply && socket_check_server (client->sock))) {
    limit_speed_request = speed == LIMIT_SPEED_SEND ? LIMIT_SPEED_START : speed;
    LOG_DEBUG_ ("speed $%d %s\n", client->sock->fd, speed == LIMIT_SPEED_STOP ? "stop" : "start");
    if (speed == LIMIT_SPEED_SEND)
      return client_send_cmd (client, SIM_CMD_PING, NULL, nil (), NULL, nil ());
  } else
    limit_speed_request = LIMIT_SPEED_SEND;
  return SIM_OK;
}

int limit_send_speed (simclient client, unsigned speed) {
  int err = SIM_OK;
  simcustomer local;
  if (! client) {
    limit_speed_number = -1;
    limit_speed_reply = 0;
    limit_speed_request = LIMIT_SPEED_STOP;
  } else if ((local = socket_check_server (client->sock)) != NULL) {
    if (limit_speed_number >= 0) {
      LOG_ANY_ (local->number ? SIM_LOG_DEBUG : speed ? SIM_LOG_ERROR : SIM_LOG_WARN,
                "speed $%d:$%d #%d (#%d %u -> %u bytes/sec)\n", client->sock->fd,
                local->sock->fd, local->number, limit_speed_number, limit_speed_send, speed);
    } else
      LOG_ANY_ (local->number ? SIM_LOG_DEBUG : speed ? SIM_LOG_ERROR : SIM_LOG_WARN,
                "speed $%d:$%d #%d (%u bytes/sec)\n", client->sock->fd, local->sock->fd, local->number, speed);
    if (local->number || ! speed) {
      limit_speed_number = local->number;
      limit_speed_send = speed;
      limit_speed_recv = -1;
      err = client_send_cmd (client, SIM_CMD_PING, NULL, nil (), NULL, nil ());
    }
  }
  return err;
}

void limit_reinit (simbool init) {
  limit_udp_speed = param_get_number ("limit.udp");
  limit_ping_tick = system_get_tick ();
  SIM_FREE (&limit_average_buffer, sizeof (*limit_average_buffer) * limit_average_size, NULL);
  if ((limit_average_size = param_get_number ("limit.average")) != 0)
    limit_average_buffer = sim_new (sizeof (*limit_average_buffer) * limit_average_size);
  limit_average_index = 0;
  limit_measured_speed = limit_measured_speed_contacts = param_get_number ("limit.server") << (10 + 1);
  LOG_DEBUG_ ("speed max %u bytes/sec\n", limit_measured_speed);
  if (init) {
    if (limit_ip_table.ptr)
      table_free (limit_ip_table);
    limit_ip_table = table_new_rand (limit_blacklist_max / 2);
    limit_init_blacklisted ();
  }
}

void limit_init (void) {
  if (! limit_blacklist_table.ptr) {
    int count = 0;
    const char *cipher = random_get_cipher ();
    simtype saved = random_get_state (random_public);
    simnumber tick = (random_set_cipher (""), system_get_tick ()), ticks;
    simnumber cycles = sim_system_cpu_get (SYSTEM_CPU_TIME_CYCLES, NULL);
    do {
      random_get (random_public, pointer_new_len (limit_cpu, RANDOM_SIZE_BLOCK));
      count++;
    } while ((ticks = system_get_tick () - tick) < 100);
    limit_cpu[LIMIT_CPU_LOAD] = cycles = sim_system_cpu_get (SYSTEM_CPU_TIME_CYCLES, NULL) - cycles + 1;
    limit_cpu[LIMIT_CPU_RATE] = system_version_major > 5 ? cycles / ticks + 1 : 1000000;
    limit_cpu[LIMIT_CPU_HASH] = (cycles * 2 / (limit_cpu[LIMIT_CPU_BOGO] = count) & ~(simunsigned) 1) | 2;
    limit_cpu[LIMIT_CPU_MAX] = limit_cpu[LIMIT_CPU_TIME] = 0;
    limit_cpu_add = param_get_number ("limit.cpu.add");
    random_set_cipher (cipher);
    random_open (random_public, saved, (simbyte *) "", 0);
    table_free (saved);
    limit_error = SIM_OK;
    limit_blacklist_table = table_new_rand (limit_blacklist_max = param_get_number ("limit.blacklisted"));
    limit_blacklist_count = 0;
    limit_ip_table = table_new_rand (limit_blacklist_max / 2);
    limit_reinit (false);
  }
}

void limit_uninit (void) {
  if (limit_blacklist_table.ptr) {
    TABLE_FREE (&limit_ip_table, number_new (0));
    TABLE_FREE (&limit_blacklist_table, number_new (0));
    SIM_FREE (&limit_average_buffer, sizeof (*limit_average_buffer) * limit_average_size, NULL);
    limit_average_size = limit_cpu_add = 0;
    limit_blacklist_count = 0;
  }
}

simbool limit_log_sockets (const char *module, int level) {
  if (contact_list.me) {
    int clients, sockets, servers, cpuload = param_get_number ("limit.cpu.load"), load;
    int locals = limit_count_customer (PROXY_TYPE_LOCAL);
    int maxclients = socket_max_limit[SOCKET_MAX_CLIENTS], maxservers = socket_max_limit[SOCKET_MAX_PROXY];
    simnumber tick = system_get_tick ();
    simwalker ctx;
    log_any_ (module, level, " probes = %d/%d\n", client_probe_count, socket_max_limit[SOCKET_MAX_PROBES]);
    log_any_ (module, level, "clients = %d/%d*%d\n", clients = client_count (NULL, NULL, NULL), maxclients,
              SOCKET_MAX_FACTOR);
    log_any_ (module, level, "sockets = %d/%d\n", sockets = limit_count_socket (NULL, NULL),
              socket_max_limit[SOCKET_MAX_NEW]);
    log_any_ (module, level, "servers = %d/%d\n", (servers = limit_count_client (NULL)) + locals, maxservers);
#ifndef _WIN32
    log_any_ (module, level, "msg_fds = %d/%d\n", msg_fd_count, socket_max_limit[SOCKET_MAX_FDS]);
    log_any_ (module, level, "reserved = %d\n", param_get_number ("limit.maxfds"));
#endif
    log_any_ (module, level, "blocked = %d/%d\n", limit_blacklist_count, limit_blacklist_max);
    if ((load = audio_get_param (AUDIO_PARAM_CPU)) >= 0) {
      if (tick - limit_ip_tick < (load *= (int) ((tick - limit_ip_tick + 1) / 1000))) {
        log_any_ (module, level, "cpuload = 100/%d\n", cpuload);
      } else /*if (sim_system_cpu_get (SYSTEM_CPU_TIME_PROCESS, NULL) / 1000000 - limit_ip_cpu >= load)*/ {
        simnumber cpu = sim_system_cpu_get (SYSTEM_CPU_TIME_PROCESS, NULL) / 1000000;
        log_any_ (module, level, "cpuload = %lld/%d\n", LIMIT_CALC_CPULOAD (cpu - load, tick - load), cpuload);
      }
    }
    log_any_ (module, level, "connect = %d/%d\n", limit_ip_count, param_get_number ("limit.cpu.connections"));
    if (limit_ping_sock)
      log_any_ (module, level, "measure = $%d\n", limit_ping_sock->fd);
    if (limit_blacklist_tick)
      log_any_ (module, level, "time = %d:%d seconds (%d seconds ago)\n", limit_reverse_timeout / 1000,
                limit_blacklist_timeout / 1000, (int) (tick - limit_blacklist_tick) / 1000);
    if (table_count (limit_blacklist_table) != limit_blacklist_count) {
      LOG_FATAL_ (SIM_OK, "sockets %u blacklisted (wanted %u)\n",
                  limit_blacklist_count, table_count (limit_blacklist_table));
    } else if (limit_blacklist_count && table_walk_first (&ctx, limit_blacklist_table)) {
      simtype key, val;
      for (log_any_ (module, level, "blacklist ="); (val = table_walk_next_number (&ctx, &key)).typ != SIMNIL;)
        log_any_ (module, level, " %s(%lld)", network_convert_ip (*(unsigned *) key.ptr), val.num);
      log_any_ (module, level, "\n");
    }
#ifndef _WIN32
    if (clients > maxclients || servers + locals > maxservers || msg_fd_count > socket_max_limit[SOCKET_MAX_FDS] ||
        sockets + servers > socket_max_limit[SOCKET_MAX_NEW] + maxservers) {
      proxy_log_customers (module, level);
      client_log_clients (module, level, false);
      return false;
    }
#endif
    if (client_probe_count > socket_max_limit[SOCKET_MAX_PROBES] + 1) {
      client_log_probes (module, level);
      return false;
    }
  }
  return true;
}
