Logo Search packages:      
Sourcecode: dcc version File versions  Download package

rl.c

/* Distributed Checksum Clearinghouse
 *
 * server rate limiting
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.63 $Revision$
 */

#include "dccd_defs.h"


RL_RATE rl_sub_rate;
RL_RATE rl_anon_rate;
RL_RATE rl_all_anon_rate;
RL_RATE rl_bugs_rate;

static RL rl_all_anon;              /* limit all or anonymous clients */

static RL *rl_newest, *rl_oldest;
static RL **rl_hash;
static int rl_hash_len;
static time_t rl_last_recycled;

time_t clients_cleared;

typedef struct ip_bl {
    struct ip_bl *fwd;
    struct in6_addr addr;
    struct in6_addr mask;
} IP_BL;
IP_BL *ip_bl;



/* See if this IP address should still be blacklisted
 * This should be sped up if there are ever more than a very few entries */
static inline void
ck_bl(RL *rl)
{
      const IP_BL *bl;

      if (!(rl->flags & RL_CK_BLACKLIST)) {
            rl->flags |= RL_CK_BLACKLIST;
            for (bl = ip_bl; bl; bl = bl->fwd) {
                  if (DCC_IN_BLOCK(rl->clnt_addr, bl->addr, bl->mask)) {
                        rl->flags |= RL_BLACKLISTED;
                        return;
                  }
            }
            rl->flags &= ~RL_BLACKLISTED;
      }
}



static void
clear_bl(void)
{
      IP_BL *bl;
      RL *rl;

      while ((bl = ip_bl) != 0) {
            ip_bl = ip_bl->fwd;
            dcc_free(bl);
      }

      for (rl = rl_newest; rl != 0; rl = rl->older) {
            rl->flags  &= ~RL_CK_BLACKLIST;
      }
}



void
check_blacklist_file(u_char force)
{
#define BL_NM "blacklist"
      static time_t prev_mtime;
      static int serrno, syntax;
      struct stat sb;
      FILE *f;
      char buf[120];
      IP_BL *bl;
      struct in6_addr addr, mask;
      char *p;
      int lineno, i;

      /* see if the file has changed */
      if (0 > stat(BL_NM, &sb)) {
            if (errno != ENOENT) {
                  if (serrno != errno) {
                        serrno = errno;
                        syntax = 0;
                        dcc_error_msg("stat(%s): %s",
                                    DCC_NM2PATH(BL_NM), ERROR_STR());
                  }
            } else if (ip_bl) {
                  prev_mtime = 0;
                  clear_bl();
            }
            return;
      }
      if (!force && prev_mtime == sb.st_mtime)
            return;

      /* it has changed, so parse it */
      f = fopen(BL_NM, "r");
      if (!f) {
            if (serrno != errno) {
                  serrno = errno;
                  syntax = 0;
                  dcc_error_msg("fopen(%s): %s",
                              DCC_NM2PATH(BL_NM), ERROR_STR());
            }
            if (ip_bl) {
                  clear_bl();
            }
      }
      if (0 > fstat(fileno(f), &sb)) {
            dcc_error_msg("fstat(%s): %s",
                        DCC_NM2PATH(BL_NM), ERROR_STR());
            return;
      }
      prev_mtime = sb.st_mtime;

      clear_bl();
      for (lineno = 1; ; ++lineno) {
            if (!fgets(buf, sizeof(buf), f)) {
                  if (ferror(f)
                      && serrno != errno) {
                        serrno = errno;
                        syntax = 0;
                        dcc_error_msg("fgets(%s): %s",
                                    DCC_NM2PATH(BL_NM),
                                    ERROR_STR());
                  }
                  break;
            }
            /* reject lines that are too long */
            i = strlen(buf);
            if (buf[i-1] != '\n') {
                  if (syntax != lineno) {
                        serrno = 0;
                        syntax = lineno;
                        dcc_error_msg("syntax error%s",
                                    fnm_lineno(BL_NM, lineno));
                  }
                  break;
            }

            /* ignore leading blanks, comments, and blank lines */
            p = strchr(buf, '#');
            if (p)
                  *p = '\0';
            i = strspn(buf, DCC_WHITESPACE);
            if (buf[i] == '\0')
                  continue;

            i = dcc_str2cidr(dcc_emsg, &addr, &mask, &buf[i],
                         BL_NM, lineno);
            if (i <= 0) {
                  if (syntax != lineno) {
                        serrno = 0;
                        syntax = lineno;
                        if (i < 0)
                              dcc_error_msg("%s", dcc_emsg);
                        else
                              dcc_error_msg("syntax error%s",
                                          fnm_lineno(BL_NM,lineno));
                  }
                  break;
            }

            bl = dcc_malloc(sizeof(*bl));
            bl->addr = addr;
            bl->mask = mask;
            bl->fwd = ip_bl;
            ip_bl = bl;
      }
      fclose(f);
}



static RL **
rl_hash_fnc(DCC_CLNT_ID id, const struct in6_addr *addr)
{
      u_int sum;

      sum = id;
      sum += addr->s6_addr32[0];
      sum += addr->s6_addr32[1];
      sum += addr->s6_addr32[2];
      sum += addr->s6_addr32[3];
      return &rl_hash[mhash(sum, rl_hash_len)];
}



static void
rl_expand(void)
{
      RL *rl, *rl2, **rlh, **rl_hash_old;
      int old_len, new_len, j;

      old_len = rl_hash_len;
      new_len = max(64, queue_max);

      if (old_len != 0)
            dcc_trace_msg("increase from %d to %d RL blocks",
                        old_len, old_len+new_len);

      rl = dcc_malloc(new_len*sizeof(*rl));
      if (!rl)
            dcc_logbad(EX_OSERR, "malloc(%d RL's) failed", new_len);
      memset(rl, 0, new_len*sizeof(*rl));
      j = 0;
      if (!rl_oldest) {
            rl_oldest = rl;
            rl_newest = rl;
            ++rl;
            ++j;
      }
      while (j < new_len) {   /* make the new blocks oldest */
            rl_oldest->older = rl;
            rl->newer = rl_oldest;
            rl_oldest = rl;
             ++rl;
             ++j;
      }

      /* rebuild and expand the hash table */
      rl_hash_len += new_len;
      rl_hash_old = rl_hash;
      rl_hash = dcc_malloc(rl_hash_len*sizeof(*rl_hash));
      if (!rl_hash)
            dcc_logbad(EX_OSERR, "malloc(%d RL hash table) failed",
                     rl_hash_len);
      memset(rl_hash, 0, rl_hash_len*sizeof(*rlh));
      if (old_len != 0) {
            do {
                  for (rl = rl_hash_old[--old_len];
                       rl != 0;
                       rl = rl2) {
                        rlh = rl_hash_fnc(rl->clnt_id, &rl->clnt_addr);
                        rl2 = rl->fwd;
                        rl->bak = 0;
                        rl->hash = rlh;
                        if ((rl->fwd = *rlh) != 0)
                              rl->fwd->bak = rl;
                        *rlh = rl;
                  }
            } while (old_len > 0);
            dcc_free(rl_hash_old);
      }
}



/* age a rate limit */
static void
rl_age(RL *rl, const RL_RATE *credits)
{
      time_t secs;

      secs = (db_time.tv_sec - rl->last_used);

      /* only prevent overflow if no time has passed */
      if (secs != 0) {
            rl->last_used = db_time.tv_sec;

            /* reset idle counters */
            if (secs >= RL_AVG_SECS || secs < 0) {
                  rl->bug_credits = rl_bugs_rate.hi;
                  rl->request_credits = credits->hi;
                  return;
            }

            rl->request_credits += secs*credits->sec;
            if (rl->request_credits > credits->hi)
                  rl->request_credits = credits->hi;

            rl->bug_credits += secs*rl_bugs_rate.sec;
            if (rl->bug_credits > rl_bugs_rate.hi)
                  rl->bug_credits = rl_bugs_rate.hi;
      }
      if (rl->request_credits < credits->lo)
            rl->request_credits = credits->lo;
      if (rl->bug_credits < rl_bugs_rate.lo)
            rl->bug_credits = rl_bugs_rate.lo;
}




static void
rl_unref(RL *rl)
{
      if (rl->newer) {
            rl->newer->older = rl->older;
      } else if (rl_newest == rl) {
            rl_newest = rl->older;
      }

      if (rl->older) {
            rl->older->newer = rl->newer;
      } else if (rl_oldest == rl) {
            rl_oldest = rl->newer;
      }

      rl->newer = 0;
      rl->older = 0;
}



static void
rl_ref(RL *rl)
{
      if (rl_newest == rl)
            return;

      rl_unref(rl);
      rl->older = rl_newest;
      rl_newest->newer = rl;
      rl_newest = rl;
}



/* get a free rate limit block */
static RL *
rl_get_free(void)
{
      RL *rl;
      time_t stale;

      for (rl = rl_oldest; rl != 0; rl = rl->newer) {
            if (!rl->ref_cnt) {
                  /* Found oldest free block */
                  if (rl->last_used != 0) {
                        /* it has been used */
                        stale = db_time.tv_sec;
                        /* keep a day's worth of blocks until
                         * we get enough to worry about a denial
                         * of service attack */
                        if (rl_hash_len < RL_MIN_MAX)
                              stale -= CLIENTS_AGE;
                        else
                              stale -= RL_LIFE_SECS;
                        /* make more if the oldest is new */
                        if (rl->last_used >= stale
                            && rl_hash_len <= RL_MAX_MAX)
                              return 0;
                        /* Remember how old it was */
                        rl_last_recycled = rl->last_used;
                  }

                  /* recycle the oldest free block */
                  if (rl->fwd)
                        rl->fwd->bak = rl->bak;
                  if (rl->bak)
                        rl->bak->fwd = rl->fwd;
                  else if (rl->hash)
                        *rl->hash = rl->fwd;
                  rl_unref(rl);
                  memset(rl, 0, sizeof(*rl));

                  rl->requests_avg_date = db_time.tv_sec + RL_AVG_DAY;
                  rl_ref(rl);
                  return rl;
            }
      }

      /* there are no free blocks that are old enough to recycle */
      return 0;
}



/* update the number of requests made yesterday if it is time */
static inline void
rl_avg_requests_age(RL *rl,
                u_char force) /* sync to daily `cdcc "stats clear"` */
{
      if (rl->requests_avg_date <= db_time.tv_sec
          || rl->requests_avg_date + RL_AVG_DAY < db_time.tv_sec
          || force) {
            rl->requests_cur_avg = rl->requests_next_avg + rl->requests;
            rl->nops_cur_avg = rl->nops_next_avg + rl->nops;

            /* these biases effectively zero tomorrow's averages */
            rl->requests_next_avg = -rl->requests;
            rl->nops_next_avg = -rl->nops;

            rl->requests_avg_date = db_time.tv_sec + RL_AVG_DAY;
      }
}



/* get a rate limit block based on the IP address of the sender */
static RL *
rl_get(QUEUE *q)
{
      RL *rl, **rlh;
      struct in6_addr clnt_addr;
      const struct in6_addr *cap;

      if (!rl_hash_len)
            rl_expand();

      if (q->clnt_su.sa.sa_family == AF_INET6) {
            cap = &q->clnt_su.ipv6.sin6_addr;
      } else {
            dcc_ipv4toipv6(&clnt_addr, q->clnt_su.ipv4.sin_addr);
            cap = &clnt_addr;
      }

      rlh = rl_hash_fnc(q->clnt_id, cap);
      for (rl = *rlh; rl; rl = rl->fwd) {
            if (rl->clnt_id != q->clnt_id
                || memcmp(&rl->clnt_addr, cap, sizeof(rl->clnt_addr)))
                  continue;
            rl_ref(rl);       /* found it, so make it newest */
            rl_avg_requests_age(rl, 0);
            q->rl = rl;
            ++rl->ref_cnt;
            ck_bl(rl);
            return rl;
      }

      rl = rl_get_free();
      if (!rl) {
            /* when we are out of rate limiting blocks, make more */
            rl_expand();
            /* which makes a new rate limit hash table */
            rlh = rl_hash_fnc(q->clnt_id, cap);
            rl = rl_get_free();
      }
      rl->clnt_addr = *cap;
      rl->clnt_id = q->clnt_id;
      rl->hash = rlh;
      rl->fwd = *rlh;
      if (rl->fwd)
            rl->fwd->bak = rl;
      *rlh = rl;

      q->rl = rl;
      ++rl->ref_cnt;
      ck_bl(rl);
      return rl;
}



/* get the most recent clients */
int                           /* +/- number of clients */
clients_get(DCC_ADMN_RESP_VAL *val,
          int *lenp,                /* buffer length */
          u_int offset,       /* skip this many newer entries */
          int thold,                /* skip clients with fewer requests */
          u_char avg)               /* 1=get average requests */
{
#define CPY2(t,s) (t[0] = s>>8, t[1] = s)
#define CPY3(t,s) (t[0] = s>>16, t[1] = s>>8, t[2] = s)
#define CPY4(t,s) (t[0] = s>>24, t[1] = s>>16, t[2] = s>>8, t[3] = s)
      RL *rl;
      DCC_ADMN_RESP_CLIENTS *cl, *cl_skip;
      int requests, nops;
      int skipped, total, len, len_lim;

      if (!val || !lenp) {
            len_lim = 0;
      } else {
            len_lim = *lenp;
      }
      if (!thold)
            thold = 1;
      cl_skip = 0;
      skipped = 0;
      total = 0;
      len = 0;
      for (rl = rl_newest; rl != 0; rl = rl->older) {
            if (rl->last_used == 0)
                  continue;

            if (rl->requests != 0)
                  ++total;
            /* get the part of the list that cdcc wants */
            if (offset != 0) {
                  --offset;
                  continue;
            }

            if (len+ISZ(*cl) > len_lim) {
                  if (len_lim != 0)
                        break;
                  continue;
            }

            if (avg) {
                  rl_avg_requests_age(rl, 0);
                  requests = RL_REQUESTS_AVG(rl);
                  nops = RL_NOPS_AVG(rl);
            } else {
                  requests = rl->requests;
                  nops = rl->nops;
            }
            if (requests < thold
                && !(rl->flags & RL_BLACKLISTED)) {
                  /* insert a fake entry for skipped records */
                  if (!cl_skip) {
                        cl_skip = (DCC_ADMN_RESP_CLIENTS *
                                 )((char *)val->clients + len);
                        memset(cl_skip, 0, sizeof(*cl_skip));
                        cl_skip->flags = DCC_ADMN_RESP_CLIENTS_SKIP;
                        len += (sizeof(*cl_skip)
                              - sizeof(cl_skip->addr)
                              + sizeof(cl_skip->addr.ipv4));
                  }
                  ++skipped;
                  CPY3(cl_skip->requests, skipped);
                  continue;
            }

            cl = (DCC_ADMN_RESP_CLIENTS *)((char *)val->clients + len);
            cl->flags = 0;
            CPY4(cl->last_used, rl->last_used);
            CPY3(cl->requests, requests);
            CPY2(cl->nops, nops);
            CPY4(cl->id, rl->clnt_id);
            if (rl->flags & RL_BLACKLISTED)
                  cl->flags |= DCC_ADMN_RESP_CLIENTS_BL;
            if (DCC_IN6_ADDR_V4MAPPED(&rl->clnt_addr)) {
                  memcpy(&cl->addr.ipv4,
                         &rl->clnt_addr.s6_addr32[3],
                         sizeof(cl->addr.ipv4));
                  len += (sizeof(*cl) - sizeof(cl->addr)
                        + sizeof(cl->addr.ipv4));
            } else {
                  cl->flags |= DCC_ADMN_RESP_CLIENTS_IPV6;
                  memcpy(&cl->addr.ipv6,
                         rl->clnt_addr.s6_addr32,
                         sizeof(cl->addr.ipv6));
                  len += (sizeof(*cl) - sizeof(cl->addr)
                        + sizeof(cl->addr.ipv6));
            }
      }
      if (lenp)
            *lenp = len;

      /* return negative total if we have more clients than RL blocks */
      if (rl_last_recycled > clients_cleared)
            return -total;
      return total;
#undef CPY2
#undef CPY3
#undef CPY4
}



void
clients_get_id(DCC_ADMN_RESP_VAL *val,
             int *lenp,       /* buffer length */
             u_int offset,          /* skip this many newer entries */
             int thold,       /* skip clients with fewer requests */
             u_char avg)            /* 1=get average requests */
{
#define CPY2(t,s) (t[0] = s>>8, t[1] = s)
#define CPY3(t,s) (t[0] = s>>16, t[1] = s>>8, t[2] = s)
#define CPY4(t,s) (t[0] = s>>24, t[1] = s>>16, t[2] = s>>8, t[3] = s)
      RL *rl, *rl2;
      DCC_ADMN_RESP_CLIENTS *cl, *cl_skip;
      int requests, nops;
      u_int skipped;
      int len, len_lim;

      len_lim = *lenp;
      cl_skip = 0;
      skipped = 0;
      len = 0;
      if (!thold)
            thold = 1;
      for (rl = rl_newest; rl != 0; rl = rl->older) {
            rl->flags &= ~RL_MARKED;
      }
      for (rl = rl_newest; rl != 0; rl = rl->older) {
            if (rl->last_used == 0)
                  continue;

            /* get the part of the list that cdcc wants */
            if (offset != 0) {
                  --offset;
                  continue;
            }

            requests = 0;
            nops = 0;
            if (!(rl->flags & RL_MARKED)) {
                  for (rl2 = rl; rl2 != 0; rl2 = rl2->older) {
                        if (rl2->clnt_id == rl->clnt_id) {
                              if (avg) {
                                  rl_avg_requests_age(rl2, 0);
                                  requests += RL_REQUESTS_AVG(rl2);
                                  nops += RL_NOPS_AVG(rl2);
                              } else {
                                  requests += rl2->requests;
                                  nops += rl2->nops;
                              }
                              rl2->flags |= RL_MARKED;
                        }
                  }
            }
            if (requests < thold) {
                  /* insert a fake entry for skipped records */
                  if (!cl_skip) {
                        cl_skip = (DCC_ADMN_RESP_CLIENTS *
                                 )((char *)val->clients + len);
                        memset(cl_skip, 0, sizeof(*cl_skip));
                        cl_skip->flags = DCC_ADMN_RESP_CLIENTS_SKIP;
                        len += (sizeof(*cl_skip)
                              - sizeof(cl_skip->addr)
                              + sizeof(cl_skip->addr.ipv4));
                  }
                  ++skipped;
                  CPY3(cl_skip->requests, skipped);
                  continue;
            }

            cl = (DCC_ADMN_RESP_CLIENTS *)((char *)val->clients + len);
            cl->flags = 0;
            CPY4(cl->last_used, rl->last_used);
            CPY3(cl->requests, requests);
            CPY2(cl->nops, nops);
            CPY4(cl->id, rl->clnt_id);
            memset(&cl->addr.ipv4, 0, sizeof(cl->addr.ipv4));
            len += (sizeof(*cl) - sizeof(cl->addr)
                  + sizeof(cl->addr.ipv4));

            if (len+ISZ(*cl) > len_lim)
                  break;
      }
      *lenp = len;
#undef CPY2
#undef CPY3
#undef CPY4
}



/* forget old clients */
void
clients_clear(u_char force_rl_age)
{
      RL *rl;

      for (rl = rl_oldest; rl != 0; rl = rl->newer) {
            rl->requests_next_avg += rl->requests;
            rl->requests = 0;
            rl->nops_next_avg += rl->nops;
            rl->nops = 0;
            rl_avg_requests_age(rl, force_rl_age);
      }

      clients_cleared = db_time.tv_sec;
}



u_char                              /* 0=bad passwd, 1=ok */
ck_sign(ID_TBL **tpp,               /* return ID table entry here */
      DCC_PASSWD passwd,            /* return matching password here */
      DCC_CLNT_ID id,
      const void *buf, u_int buf_len)
{
      ID_TBL *tp;

      tp = find_id_tbl(id);
      if (tpp)
            *tpp = tp;
      if (!tp)
            return 0;

      if (tp->cur_passwd[0] != '\0'
          && dcc_ck_signature(tp->cur_passwd, sizeof(tp->cur_passwd),
                        buf, buf_len)) {
            if (passwd)
                  memcpy(passwd, tp->cur_passwd, sizeof(DCC_PASSWD));
            return 1;
      }
      if (tp->next_passwd[0] != '\0'
          && dcc_ck_signature(tp->next_passwd, sizeof(tp->next_passwd),
                        buf, buf_len)) {
            if (passwd)
                  memcpy(passwd, tp->next_passwd, sizeof(DCC_PASSWD));
            return 1;
      }
      return 0;
}



static void
tp2delay(QUEUE *q, time_t delay_us, u_int delay_inflate)
{
      if (delay_us == 0) {
            q->delay_us = 0;
      } else {
            q->delay_us = RL_REQUESTS_AVG(q->rl);
            q->delay_us = 1 + q->delay_us/delay_inflate;
            if (q->delay_us > DCC_ANON_DELAY_MAX / delay_us) {
                  /* prevent overflow */
                  q->delay_us = DCC_ANON_DELAY_MAX;
                  return;
            }
            q->delay_us *= delay_us;
      }

      /* increase the delay when flooding is off or broken */
      if (flods_st != FLODS_ST_ON
          || (iflods.active == 0 && oflods.total != 0))
            q->delay_us += 400*1000;
      if (q->delay_us > DCC_ANON_DELAY_MAX)
            q->delay_us = DCC_ANON_DELAY_MAX;
}



/* check the message authentication code and rate limit requests */
u_char                              /* 0=forget DOS attack, 1=go ahead */
ck_id(QUEUE *q, DCC_CLNT_ID id)
{
#define RL_CNT2AVG(cur,lims) ((lims.hi - cur) / (RL_SCALE*RL_AVG_SECS*1.0))
      ID_TBL *tp;
      RL *rl;

      if (id == DCC_ID_ANON) {
            tp = 0;
      } else {
            /* authenticate the ID */
            if (!ck_sign(&tp, q->passwd, id, &q->pkt, q->pkt_len)) {
                  if (!tp) {
                        ++dccd_stats.unknown_ids;
                        anon_msg("unknown client-ID %d from %s",
                               id, Q_CIP(q));
                  } else {
                        ++dccd_stats.bad_passwd;
                        anon_msg("bad authentication for client-ID"
                               " %d from %s",
                               id, Q_CIP(q));
                        tp = 0;
                  }
            }
      }

      if (tp) {
            q->clnt_id = id;
            if (!query_only || (tp->flags & ID_FLG_RPT_OK))
                  q->flags |= Q_FLG_RPT_OK;

            rl = rl_get(q);
            ++rl->requests;
            rl_age(rl, &rl_sub_rate);
            rl->request_credits -= RL_SCALE;

            if (rl->flags & RL_BLACKLISTED) {
                  TMSG2(BL, "%d %s blacklisted request",
                        id, Q_CIP(q));
                  return 0;
            }

            if (rl->request_credits <= 0) {
                  clnt_msg(q, "%.1f requests/sec are too many from %d %s",
                         RL_CNT2AVG(rl->request_credits, rl_sub_rate),
                         id, Q_CIP(q));
                  ++dccd_stats.rl;
                  return 0;
            }

            tp2delay(q, tp->delay_us, tp->delay_inflate);

      } else {
            q->clnt_id = DCC_ID_ANON;
            if (!query_only)
                  q->flags |= Q_FLG_RPT_OK;

            rl_age(&rl_all_anon, &rl_all_anon_rate);
            rl_all_anon.request_credits -= RL_SCALE;
            rl = rl_get(q);
            ++rl->requests;
            rl_age(rl, &rl_anon_rate);
            rl->request_credits -= RL_SCALE;

            if (rl->flags & RL_BLACKLISTED) {
                  TMSG1(BL, " anonymous %s blacklisted request",
                        Q_CIP(q));
                  return 0;
            }

            if (rl->request_credits <= 0) {
                  anon_msg("%.1f requests/sec are too many from anonymous"
                         " %s",
                         RL_CNT2AVG(rl->request_credits, rl_anon_rate),
                         Q_CIP(q));
                  ++dccd_stats.anon_rl;
                  return 0;
            }
            if (rl_all_anon.request_credits <= 0) {
                  anon_msg("%s contributed to %.1f"
                         " anonymous requests/sec",
                         Q_CIP(q),
                         RL_CNT2AVG(rl_all_anon.request_credits,
                                  rl_all_anon_rate));
                  ++dccd_stats.anon_rl;
                  return 0;
            }

            tp2delay(q, anon_delay_us, anon_delay_inflate);
      }

      return 1;
}



/* check the message authentication code for a client of our server-ID
 * and rate limit its messages */
u_char                              /* 0=forget DOS attack, 1=go ahead */
ck_clnt_srvr_id(QUEUE *q)
{
      DCC_CLNT_ID id;
      u_char result;

      /* require a client-ID, our server-ID, or the anonymous client-ID
       * to allow anonymous `cdcc stats` */
      id = ntohl(q->pkt.hdr.sender);
      if ((id < DCC_SRVR_ID_MIN || id > DCC_CLNT_ID_MAX)
          && id != DCC_ID_ANON) {
            ++dccd_stats.unknown_ids;
            result = ck_id(q, DCC_ID_ANON);
            if (result)
                  anon_msg("bad client or server-ID %d from %s for %s",
                         id, Q_CIP(q), dcc_req_op2str(&q->pkt.a));
            return result;
      }
      return ck_id(q, id);
}



/* check the message authentication code of a request,
 * and rate limit the source */
u_char                              /* 0=forget DOS attack, 1=go ahead */
ck_clnt_id(QUEUE *q)
{
      DCC_CLNT_ID id;
      u_char result;

      /* require a client-ID instead of a server-ID to discourage server
       * operators from leaking server-ID's */
      id = ntohl(q->pkt.hdr.sender);
      if (id < DCC_CLNT_ID_MIN
          && id != DCC_ID_ANON) {
            ++dccd_stats.unknown_ids;
            result = ck_id(q, DCC_ID_ANON);
            if (result)
                  anon_msg("bad client-ID %d from %s for %s",
                         id, Q_CIP(q), dcc_req_op2str(&q->pkt.a));
            return result;
      }
      return ck_id(q, id);
}



/* complain about an anonymous, non-paying client */
void
vanon_msg(const char *p, va_list args)
{
      rl_age(&rl_all_anon, &rl_all_anon_rate);
      if ((DCC_TRACE_ANON_BIT & dccd_tracemask)
          && (rl_all_anon.bug_credits > 0
            || (DCC_TRACE_RLIM_BIT & dccd_tracemask))) {
            rl_all_anon.bug_credits -= RL_SCALE;
            dcc_vtrace_msg(p, args);
      }
}



void
anon_msg(const char *p, ...)
{
      va_list args;

      va_start(args, p);
      vanon_msg(p, args);
      va_end(args);
}



/* complain about an authenticated client */
void
clnt_msg(const QUEUE *q, const char *p, ...)
{
      va_list args;

      if (q->clnt_id == DCC_ID_ANON || !q->rl) {
            va_start(args, p);
            vanon_msg(p, args);
            va_end(args);
            return;
      }

      if (DCC_TRACE_CLNT_BIT & dccd_tracemask) {
            rl_age(q->rl, &rl_sub_rate);
            if (q->rl->bug_credits > 0
                || (DCC_TRACE_RLIM_BIT & dccd_tracemask)) {
                  q->rl->bug_credits -= RL_SCALE;
                  va_start(args, p);
                  dcc_vtrace_msg(p, args);
                  va_end(args);
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index