/* Distributed Checksum Clearinghouse server daemon definitions
 *
 * 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.120 $Revision$
 */

#ifndef DCCD_DEFS_H
#define DCCD_DEFS_H

#include "srvr_defs.h"
#include "dcc_xhdr.h"

extern DCC_EMSG dcc_emsg;

extern u_char grey;
extern u_char background;
extern int stopint;

extern DCC_SRVR_ID my_srvr_id;

extern const char *brand;		/* our brand name */

extern u_char use_ipv6;
extern u_int16_t def_port;
typedef struct srvr_soc {
    struct srvr_soc *fwd;
    DCC_SOCKU	su;
    union {
	struct dcc_in6_addr in6;
	struct in_addr	    in4;
    } arg_addr;
    int		udp;
    int		listen;
    u_int16_t	arg_port;
    u_char	arg_family;
    u_char	flags;
#    define	 SRVR_SOC_ADDR	    0x01    /* explicit IP address */
#    define	 SRVR_SOC_IF	    0x02    /* port on all interfaces */
#    define	 SRVR_SOC_LISTEN    0x04    /* need a listen socket for port */
#    define	 SRVR_SOC_MARK	    0X08
} SRVR_SOC;
extern SRVR_SOC *srvr_socs;
extern int srvr_rcvbuf;

extern DB_RCD host_id_rcd;		/* our host's globally unique ID */
extern time_t host_id_next, host_id_last;
#define DCC_SRVR_ID_SEC	    (24*60*60)	/* defend server-IDs this often */
#define DCC_SRVR_ID_SEC0    (5*60)	/* defend after daemon is running */

extern time_t boot_ok_time;		/* suppress flooding errors at first */

/* normal keepalive interval */
#define	KEEPALIVE_IN	    (60*15)
#define	KEEPALIVE_OUT	    (KEEPALIVE_IN+FLODS_CK_SECS)
/* things should be quicker while we are shutting down flooding */
#define KEEPALIVE_IN_STOP   15
#define	KEEPALIVE_OUT_STOP  (KEEPALIVE_IN_STOP+FLODS_CK_SECS)
/* be really quick if stopping the daemon */
#define SHUTDOWN_DELAY	    2


extern int summarize_delay_secs;	/* delay summaries by this */
#define MAX_SUMMARIZE_DELAY_SECS (DCC_NEW_SPAM_SECS/2)


extern int queue_max;

extern u_char anon_off;			/* turn off anonymous access */
extern u_char query_only;		/* 1=treat reports as queries */

extern time_t anon_delay_us;		/* anonymous client delay */
extern u_int anon_delay_inflate;

extern struct timeval wake_time;	/* when we awoke from select() */

extern u_char grey_weak_body;		/* 1=ignore bodies for greylisting */
extern u_char grey_weak_ip;		/* 1=one good triple whitelists addr */

extern int grey_embargo;
extern int grey_window;
extern int grey_white;


/* rate limiting
 *	One of these structures is maintained for every recent client,
 *	where "recent" is at least one day */
typedef struct rl {
    struct rl	*fwd, *bak, **hash;
    struct rl	*older, *newer;
    DCC_CLNT_ID	clnt_id;
    struct in6_addr clnt_addr;
    time_t	last_used;
    time_t	requests_avg_date;	/* end of "today" for averaging */
#    define	 RL_AVG_DAY	(24*60*60 - 30*60)
    int		requests_cur_avg;	/* yesterday's total */
    int		nops_cur_avg;
    int		requests_next_avg;	/* growing total for tomorrow */
    int		nops_next_avg;
#    define	 RL_REQUESTS_AVG(rl) max((rl)->requests_cur_avg,	\
					 (rl)->requests+(rl)->requests_next_avg)
#    define	 RL_NOPS_AVG(rl) max((rl)->nops_cur_avg,		\
				     (rl)->nops+(rl)->nops_next_avg)
    int		requests;
    int		nops;
    int		request_credits;	/* operations */
    int		bug_credits;		/* complaints about this client */
    short	ref_cnt;		/* in use by entry in queue */
    u_char	flags;
#    define	 RL_BLACKLISTED	    0x01
#    define	 RL_CK_BLACKLIST    0x02
#    define	 RL_MARKED	    0x04
} RL;


typedef struct {
    int	    sec, lo, hi;
} RL_RATE;

extern RL_RATE rl_sub_rate;		/* X/sec/paying customer */
extern RL_RATE rl_anon_rate;		/* X/sec/freeloader */
extern RL_RATE rl_all_anon_rate;	/* X/sec for all freeloaders */
extern RL_RATE rl_bugs_rate;		/* X complaints/sec */

#define	RL_MIN_MAX	10000		/* freely create this many blocks */
#define RL_MAX_MAX	(RL_MIN_MAX*8)	/* absolute limit */
#define RL_AVG_SECS	10		/* average for this many seconds */
#define	RL_LIFE_SECS    (RL_AVG_SECS*2)	/* lifetime of rate limit block */

#define RL_OVF_CREDITS	0x7fffffff	/* fit {bug,request}_credits */
#define RL_SCALE	10
#define RL_MAX_CREDITS	(RL_OVF_CREDITS/RL_AVG_SECS/RL_SCALE/2)


extern time_t clients_cleared;


/* report cache used to detect duplicate reports
 *	One of these structures is maintained for every current operation */
typedef struct ridc {
    struct ridc *fwd, *bak, **hash;
    struct ridc *older, *newer;
    time_t	last_used;
    DCC_HDR	hdr;
    u_int16_t	clnt_port;
    int		len;
    u_char	op;
    union {
	DCC_QUERY_RESP_BODY body;
	DCC_ADMN_RESP_ANON_DELAY anon_delay;
	char	    msg[DCC_ERROR_MSG_LEN];
    } result;
} RIDC;

/* entry in main job queue */
typedef union {
    DCC_HDR	hdr;
    DCC_QUERY_REPORT r;
    DCC_DELETE	d;
    DCC_GREY_SPAM gs;
    DCC_ADMN_REQ a;
} QUEUE_PKT;

typedef struct dccd_queue {
    struct dccd_queue *later, *earlier;
    RL		*rl;
    RIDC	*ridc;
    SRVR_SOC	*sp;
    DCC_CLNT_ID	clnt_id;		/* validated client-ID */
    DCC_SOCKU	clnt_su;		/* send answer here */
    u_int	pkt_len;
    time_t	delay_us;		/* how long to delay the answer */
    struct timeval answer;		/* when it should be answered */
    u_char	flags;
#    define	 Q_FLG_RPT_OK	0x01	/* override dccd -Q */
    DCC_PASSWD	passwd;			/* sign answers with this */
    QUEUE_PKT	pkt;
} QUEUE;


#define BAD_VER_MSG	"unrecognized flod version "
#define BAD_ID_MSG	"unauthorized ID "
#define BAD_AUTH_MSG	"bad authentication for ID "


typedef struct {
    DCC_SRVR_ID from_lo;
    DCC_SRVR_ID from_hi;
    u_char	result;
} OFLOD_SRVR_ID_MAP;
typedef enum {
    ID_MAP_NO, ID_MAP_REJ, ID_MAP_SELF
} ID_MAP_RESULT;

typedef struct {
    int	    val;
    int	    limit;
} FLOD_LIMCNT;
#define FLOD_CNTERR(lc) (++(lc)->val - ((lc)->limit + 10))
#define FLOD_LIM_CLEAR_SECS	(60*60)

typedef struct {
    u_int	flags;
#    define	 FLOD_OPT_OFF		0x0001
#    define	 FLOD_OPT_DEL_OK	0x0002
#    define	 FLOD_OPT_NO_LOG_DEL	0x0004
#    define	 FLOD_OPT_TRAPS		0x0008
#    define	 FLOD_OPT_PASSIVE	0x0010
#    define	 FLOD_OPT_SOCKS		0x0020
#    define	 FLOD_OPT_LOC_SU	0x0040
#    define	 FLOD_OPT_IPv4		0x0080
#    define	 FLOD_OPT_IPv6		0x0100
    OFLOD_SRVR_ID_MAP srvr_map[10];
    u_char	num_maps;
    u_char	path_len;
} OFLOD_OPTS;

typedef struct {
    FLOD_MMAP	*mp;
    int		s;			/* outgoing socket */
    int		lineno;
    char	portname[sizeof(flod_mmaps->mmaps[0].portname)];
    char	hostname[sizeof(flod_mmaps->mmaps[0].hostname)];
    DCC_SRVR_ID	rem_id, in_passwd_id, out_passwd_id;
    u_int16_t	port;
    DCC_SOCKU	su;			/* target of the flood */
    DCC_SOCKU	loc_su;
    time_t	out_try_again;		/* don't connect() before then */
    int		out_try_secs;		/*	backoff */
    time_t	in_try_again;		/* no SOCKS connect() before then */
    int		in_try_secs;		/*	backoff */
    time_t	limit_reset;		/* when to reset complaint limits */
    time_t	keep_out_time;		/* when last sent to peer */
    time_t	keep_in_time;		/* when last heard from peer */
    struct {
	time_t	    out_start;
	int	    out_reports;	/* total reports send */
	time_t	    in_start;
	int	    total;		/* total reports received */
	int	    accepted;		/* acceptable received reports */
	FLOD_LIMCNT stale;		/* bad timestamp */
	FLOD_LIMCNT dup;		/* already received */
	FLOD_LIMCNT ok2;		/* whitelisted */
	FLOD_LIMCNT not_deleted;	/* delete commands ignored */
	FLOD_LIMCNT bad_id;		/* unrecognized server-IDs */
	FLOD_LIMCNT complaint;		/* output complaint from peer */
    } cnts;
    DB_PTR	xmit_pos;		/* last transmitted position */
    DB_PTR	recv_pos;		/* heard this from target */
    DB_PTR	cur_pos;		/* completed to here */
    DB_PTR	rewind_pos;		/* will have rewound by here */
    u_int	ibuf_len;
    union {
	DCC_FLOD_POS pos;
	FLOD_END    end;
	FLOD_NOTE   note;
	u_char	    b[sizeof(DCC_FLOD_POS)*2+DCC_FLOD_MAX_RESP+1];
    } ibuf;
    u_int	obuf_len, obuf_off;
    union {
	DCC_FLOD_VERSION_HDR vers;
	DCC_FLOD    f[3];
	char	    c[2048];
    } obuf;
    OFLOD_OPTS	i_opts;
    OFLOD_OPTS	o_opts;
    time_t	ids_mtime;
    u_char	flags;
#    define	 OFLOD_FG_CONNECTED	0x01	/* connect() complete */
#    define	 OFLOD_FG_SHUTDOWN	0x02	/* brakes applied */
#    define	 OFLOD_FG_SHUTDOWN_REQ	0x04
#    define	 OFLOD_FG_PASSWD_NEXT	0x08	/* have a 2nd password */
#    define	 OFLOD_FG_TOO_BUSY	0x10
    u_char	iversion, oversion;
} OFLOD_INFO;

typedef struct {
    int		total;
    int		active;
    OFLOD_INFO  infos[DCCD_MAX_FLOODS];
} OFLODS;
extern OFLODS oflods;
extern DB_PTR oflods_max_pos;

extern enum FLODS_ST {
	FLODS_ST_OFF, FLODS_ST_RESTART, FLODS_ST_ON
} flods_st;

extern DCC_TGTS oflod_thold[DCC_DIM_CKS];

typedef struct {
    int		s;			/* incoming socket */
    DCC_SOCKU	su;			/* sender of the flood */
    char	hostname[sizeof(flod_mmaps->mmaps[0].hostname)];
    DCC_FLOD_POS pos, pos_sent;
    OFLOD_INFO	*ofp;
    time_t	quit_connect;		/* when to stop waiting for password */
#    define	 IFLOD_CONNECT_SECS KEEPALIVE_IN
    u_int	ibuf_len, ibuf_off;
    u_char	flags;
#    define	 IFLOD_FG_CONNECTED	0x01
#    define	 IFLOD_FG_VERS_CK	0x02
#    define	 IFLOD_FG_END_REQ	0x04
#    define	 IFLOD_FG_FAST_LINGER	0x08
    union {
	DCC_FLOD_VERSION_HDR vers;
	DCC_FLOD    f[3];
	char	    c[1024];
    } ibuf;
} IFLOD_INFO;

typedef struct {
    int		active;
    IFLOD_INFO	infos[DCCD_MAX_FLOODS];
} IFLODS;
extern IFLODS iflods;

extern int flods_off;			/* # of reasons flooding is off */
extern int flod_db_sick;		/* # of flooding DB sicknesses */
extern u_int complained_many_iflods;

extern u_char too_busy;			/* too busy to flood */
extern time_t next_flods_ck;
#define FLODS_CK_SECS	    30
#define GREY_FLODS_CK_SECS   5
#define MISC_CK_SECS	    (FLODS_CK_SECS-1)

#define FLOD_RETRY_SECS		(60*5)	/* try to connect once in 5 minutes */
#define FLOD_SLOW_RETRY_SECS (60*60*4)	/* 4 hours after bad authentication */

extern time_t got_hosts;
#define FLOD_NAMES_RESOLVE_SECS	(60*15)	/* resolve hostnames once/15 minutes */
extern pid_t resolve_hosts_pid;

extern const char *need_del_dbclean;
extern time_t del_dbclean_next;
#define DEL_DBCLEAN_SECS	(60*30)	/* limit dbclean if not urgent */
extern time_t dbclean_limit;
#define DBCLEAN_LIMIT_SECS	15	/* not too often for any reason */
extern time_t dbclean_limit_secs;

extern DCCD_STATS dccd_stats;


/* Avoid the costs of generating and passing the args to syslog() by
 * checking bits in the caller.
 * If the server ran only on modern Unix, we could use gcc's macro varargs. */
#define TMSG(t,p) do {					\
	if (DCC_TRACE_##t##_BIT & dccd_tracemask)	\
		dcc_trace_msg(p);} while (0)
#define TMSG1(t,p,arg) do {				\
	if (DCC_TRACE_##t##_BIT & dccd_tracemask)	\
		dcc_trace_msg(p,arg);} while (0)
#define TMSG2(t,p,arg1,arg2) do {			\
	if (DCC_TRACE_##t##_BIT & dccd_tracemask)	\
		dcc_trace_msg(p,arg1,arg2);} while (0)
#define TMSG3(t,p,arg1,arg2,arg3) do {			\
	if (DCC_TRACE_##t##_BIT & dccd_tracemask)	\
		dcc_trace_msg(p,arg1,arg2,arg3);} while (0)
#define TMSG4(t,p,arg1,arg2,arg3,arg4) do {		\
	if (DCC_TRACE_##t##_BIT & dccd_tracemask)	\
		dcc_trace_msg(p,arg1,arg2,arg3,arg4);} while (0)
#define TMSG5(t,p,arg1,arg2,arg3,arg4,arg5) do {	\
	if (DCC_TRACE_##t##_BIT & dccd_tracemask)	\
		dcc_trace_msg(p,arg1,arg2,arg3,arg4,arg5);} while (0)
#define TMSG6(t,p,arg1,arg2,arg3,arg4,arg5,arg6) do {	\
	if (DCC_TRACE_##t##_BIT & dccd_tracemask)	\
		dcc_trace_msg(p,arg1,arg2,arg3,arg4,arg5,arg6);} while (0)

#define Q_CIP(q) dcc_su2str(&(q)->clnt_su)


static inline void
db_ptr2flod_pos(DCC_FLOD_POS bp, DB_PTR pos)
{
	bp[7] = pos;     bp[6] = pos>>8;
	bp[5] = pos>>16; bp[4] = pos>>24;
	bp[3] = pos>>32; bp[2] = pos>>40;
	bp[1] = pos>>48; bp[0] = pos>>56;
}


static inline DB_PTR
flod_pos2db_ptr(const DCC_FLOD_POS pos)
{
	return ((DB_PTR)pos[7]		 + (((DB_PTR)pos[6])<<8)
		+ (((DB_PTR)pos[5])<<16) + (((DB_PTR)pos[4])<<24)
		+ (((DB_PTR)pos[3])<<32) + (((DB_PTR)pos[2])<<40)
		+ (((DB_PTR)pos[1])<<48) + (((DB_PTR)pos[0])<<56));
}



/* dccd.c */
extern void free_q(QUEUE *);
extern void close_srvr_socs(void);
extern void set_dbclean_timer(void);

/* iflod.c */
const char *flod_stats_str(int *, int, enum FLOD_ERR_OP);
extern void rpt_err(LAST_ERROR *, u_char, enum FLOD_ERR_OP, int,
		    const char *, ...) PATTRIB(5,6);
extern u_char set_flod_socket(int, const char *, const DCC_SOCKU *);
extern u_char flod_names_resolve_ck(void);
extern u_char flod_names_resolve_start(void);
extern ID_MAP_RESULT id_map(DCC_SRVR_ID, const OFLOD_OPTS *);
extern void iflod_stop(IFLOD_INFO *, const char *);
extern void iflods_stop(const char *, u_char);
extern void iflod_start(SRVR_SOC *);
extern void iflods_start(void);
extern void iflod_socks_start(OFLOD_INFO *);
extern void save_flod_cnt(OFLOD_INFO *, u_char);
extern u_char dccd_db_open(u_char);
extern void iflod_close(IFLOD_INFO *, u_char, enum FLOD_ERR_OP, int,
			const char *, ...)  PATTRIB(5,6);
extern void iflod_read(IFLOD_INFO *);
extern u_char iflod_send_pos(IFLOD_INFO *, u_char);
extern int flods_list(char *, int, u_char);
extern int flod_stats(char *, int, u_int32_t, u_char);
extern void new_peer(OFLOD_INFO *);

/* oflod.c */
extern void oflod_open(OFLOD_INFO *);
extern void oflods_unmap(void);
extern u_char oflods_load(void);
extern void oflod_close(OFLOD_INFO *, u_char, enum FLOD_ERR_OP, int);
extern void oflod_read(OFLOD_INFO *);
extern void oflod_write(OFLOD_INFO *);
extern void oflods_stop(u_char);
extern u_char oflod_connect_fin(OFLOD_INFO *);
extern void PATTRIB(1,2) db_broken(const char *, ...);
extern void flods_restart(const char *);
extern void flods_ck(u_char);
extern void flods_init(void);

/* rl.c */
extern int clients_get(DCC_ADMN_RESP_VAL *, int *, u_int, int, u_char);
extern void clients_get_id(DCC_ADMN_RESP_VAL *, int *, u_int, int, u_char);
#define CLIENTS_AGE (24*60*60)
extern void clients_clear(u_char);
extern u_char ck_sign(ID_TBL **, DCC_PASSWD, DCC_CLNT_ID, const void *, u_int);
extern u_char ck_id(QUEUE *, DCC_CLNT_ID);
extern u_char ck_clnt_srvr_id(QUEUE *);
extern u_char ck_clnt_id(QUEUE *);
extern void check_blacklist_file(u_char);

/* trace.c */
extern u_long dccd_tracemask;
extern void vanon_msg(const char *, va_list);
extern void anon_msg(const char *, ...) PATTRIB(1,2);
extern void clnt_msg(const QUEUE *, const char *, ...) PATTRIB(2,3);

/* work.c */
extern u_char ridc_get(QUEUE *);
extern void update_q_delay(QUEUE *);
extern void cycle_q_delay(void);
extern void stats_clear(void);
extern u_char summarize_dly(void);
extern u_char add_dly_rcd(DCC_EMSG, DB_RCD *);
extern void do_work(QUEUE *);
extern void do_grey(QUEUE *);
extern void do_grey_spam(QUEUE *);
extern void do_nop(QUEUE *);
extern void do_admn(QUEUE *);
extern void do_delete(QUEUE *);
extern void discard_error(const QUEUE *, const char *, ...) PATTRIB(2,3);
extern void send_ok(QUEUE *);


#endif /* DCCD_DEFS_H */
