#ifndef __ZSERVER_H__
#define __ZSERVER_H__
/* This file is part of the Project Athena Zephyr Notification System.
 * It contains declarations for use in the server.
 *
 *	Created by:	John T. Kohl
 *
 *	$Id: b1b4bc836e0891e8529a97de5154291be6098781 $
 *
 *	Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
 *	For copying and distribution information, see the file
 *	"mit-copyright.h".
 */

#include <zephyr/mit-copyright.h>

#include <internal.h>
#ifdef HAVE_ARES
#include <ares.h>
#endif

#include <arpa/inet.h>

#include "zsrv_err.h"

#include "timer.h"
#include "zsrv_conf.h"			/* configuration params */

#include "zstring.h"
#include "access.h"
#include "acl.h"
#include "utf8proc.h"


/* Kerberos-specific library interfaces used only by the server. */
#ifdef HAVE_KRB5
extern krb5_keyblock *__Zephyr_keyblock;
#define ZGetSession() (__Zephyr_keyblock)
void ZSetSession(krb5_keyblock *keyblock);
krb5_error_code Z_krb5_init_keyblock(krb5_context, krb5_enctype, size_t,
        krb5_keyblock **);
#endif

#ifdef HAVE_KRB4
void ZSetSessionDES(C_Block *key);

Code_t ZFormatAuthenticNotice(ZNotice_t*, char*, int, int*, C_Block);

#ifndef HAVE_KRB5
extern C_Block __Zephyr_session;
#define ZGetSession() (__Zephyr_session)
#endif
#endif

/* For krb_rd_req prototype and definition. */
#ifndef KRB_INT32
#define KRB_INT32 ZEPHYR_INT32
#endif

/* Current time as cached by main(); use instead of time(). */
#define NOW t_local.tv_sec

#ifdef HAVE_KRB4
/* Kerberos shouldn't stick us with array types... */
typedef struct {
    des_key_schedule s;
} Sched;
#endif

enum _ZRealm_state {
    REALM_NEW,				/* New realm; no servers yet */
    REALM_UP,				/* ZRealm is up */
    REALM_TARDY,			/* ZRealm due for a hello XXX */
    REALM_DEAD,				/* ZRealm is considered dead */
    REALM_STARTING			/* ZRealm is between dead and up */
};

enum _Server_state {
    SERV_UP,				/* Server is up */
    SERV_TARDY,				/* Server due for a hello */
    SERV_DEAD,				/* Server is considered dead */
    SERV_STARTING			/* Server is between dead and up */
};

enum _Sent_type {
    NOT_SENT,				/* message was not xmitted */
    SENT,				/* message was xmitted */
    AUTH_FAILED,			/* authentication failed */
    NOT_FOUND				/* user not found for uloc */
};

typedef struct _Destination Destination;
typedef struct _Destlist Destlist;
typedef struct _ZRealm ZRealm;
typedef struct _ZRealmname ZRealmname;
typedef enum _ZRealm_state ZRealm_state;
typedef struct _ZRealm_server ZRealm_server;
typedef struct _Client Client;
typedef struct _Triplet Triplet;
typedef enum _Server_state Server_state;
typedef struct _Unacked Unacked;
typedef struct _Pending Pending;
typedef struct _Server Server;
typedef enum _Sent_type Sent_type;
typedef struct _Statistic Statistic;

struct _Destination {
    String		*classname;
    String		*inst;
    String		*recip;
};

struct _Destlist {
    Destination	dest;
    struct _Destlist	*next, **prev_p;
};

struct _ZRealm_server {
    String *name;			/* server's hostname */
    struct sockaddr_in addr;		/* server's address */
    ZRealm *realm;			/* realm this server belongs to */
    Timer *timer;			/* timer for name lookup */
    unsigned int dontsend :1;		/* private server, do not send */
    unsigned int got_addr :1;		/* IP address is valid */
    unsigned int deleted :1;		/* server no longer exists */
};

struct _ZRealm {
    String *namestr;			/* realm's name */
    char *name;				/* always namestr->string */
    int count;
    ZRealm_server **srvrs;
    int idx;				/* which server we are connected to */
    Destlist *subs;                     /* what their clients sub to */
    Destlist *remsubs;                  /* our subs on their end */
    Client *client;
    int child_pid;
    int have_tkt;
    ZRealm_state state;
};

struct _ZRealmname {
    String *name;
    struct _ZRealm_server *servers;
    int nused;
    int nservers;
};

struct _Client {
    struct sockaddr_in	addr;		/* ipaddr/port of client */
    Destlist		*subs	;	/* subscriptions */
#ifdef HAVE_KRB5
    krb5_keyblock       *session_keyblock;
#else
#ifdef HAVE_KRB4
    C_Block		session_key;	/* session key for this client */
#endif /* HAVE_KRB4 */
#endif
    String		*principal;	/* krb principal of user */
    int			last_send;	/* Counter for last sent packet. */
    time_t		last_ack;	/* Time of last received ack */
    ZRealm		*realm;
    struct _Client	*next, **prev_p;
};

struct _Triplet {
    Destination		dest;
    Acl			*acl;
    Client		**clients;
    int			clients_size;
    struct _Triplet	*next, **prev_p;
};

struct _Unacked {
    Timer		*timer;		/* timer for retransmit */
    Client		*client;	/* responsible client, or NULL */
    short		rexmits;	/* number of retransmits */
    short		packsz;		/* size of packet */
    char		*packet;	/* ptr to packet */
    ZUnique_Id_t	uid;		/* uid of packet */
    struct sockaddr_in	ack_addr;
    union {				/* address to send to */
	struct sockaddr_in addr;	/* client address */
	int	srv_idx;		/* index of server */
	struct {
	    ZRealm *realm;		/* pointer to realm */
	    int rlm_srv_idx;		/* index of server in realm */
	} rlm;
    } dest;
    struct _Unacked *next, **prev_p;
};

struct _Pending {
    char		*packet;	/* the notice (in pkt form) */
    short		len;		/* len of pkt */
    unsigned int	auth;		/* whether it is authentic */
    struct sockaddr_in who;		/* the addr of the sender */
    struct _Pending *next;
};

struct _Server {
    Server_state	state;		/* server's state */
    struct sockaddr_in	addr;		/* server's address */
    long		timeout;	/* Length of timeout in sec */
    Timer		*timer;		/* timer for this server */
    Pending		*queue;		/* queue of packets to send
					   to this server when done dumping */
    Pending		*queue_last;	/* last packet on queue */
    short		num_hello_sent;	/* number of hello's sent */
    unsigned int	dumping;	/* 1 if dumping, so we should queue */
    char		addr_str[16];	/* text version of address */
};

/* statistics gathering */
struct _Statistic {
    int			val;
    char		*str;
};

typedef enum _Exposure_type {
    NONE,
    OPSTAFF_VIS,
    REALM_VIS,
    REALM_ANN,
    NET_VIS,
    NET_ANN
} Exposure_type;

typedef struct _Location {
    String *user;
    String *machine;
    char *time;			/* in ctime format */
    String *tty;
    struct sockaddr_in addr;	/* IP address and port of location */
    Exposure_type exposure;
} Location;

/* Function declarations */

/* These macros instantiate inline functions that do the work of the formder
   LIST_INSERT and LIST_DELETE functions, which unfortunately triggered gcc's
   pedanticism.  The comment before the *former* macros was: */
/* These macros are for insertion into and deletion from a singly-linked list
 * with back pointers to the previous element's next pointer.  In order to
 * make these macros act like expressions, they use the comma operator for
 * sequenced evaluations of assignment, and "a && b" for "evaluate assignment
 * b if expression a is true". */

#define MAKE_LIST_INSERT(type) inline static void type##_insert(type **head, type *elem) \
    {\
	(elem)->next = *(head);					\
	if(*head) (*(head))->prev_p = &(elem)->next;		\
	(*head) = (elem);					\
	(elem)->prev_p = (head);				\
    }

#define MAKE_LIST_DELETE(type) inline static void type##_delete(type *elem) \
    {\
	*(elem)->prev_p = (elem)->next;				\
	if((elem)->next) (elem)->next->prev_p = (elem)->prev_p;	\
    }

MAKE_LIST_INSERT(Destlist)
MAKE_LIST_DELETE(Destlist)
MAKE_LIST_INSERT(Client)
MAKE_LIST_DELETE(Client)
MAKE_LIST_INSERT(Triplet)
MAKE_LIST_DELETE(Triplet)
MAKE_LIST_INSERT(Unacked)
MAKE_LIST_DELETE(Unacked)

/* found in bdump.c */
void bdump_get(ZNotice_t *notice, int auth, struct sockaddr_in *who,
		    Server *server);
void bdump_send(void);
void bdump_offer(struct sockaddr_in *who);
Code_t bdump_send_list_tcp(ZNotice_Kind_t kind, struct sockaddr_in *addr,
				char *class_name, char *inst, char *opcode,
				char *sender, char *recip, char **lyst,
				int num);
int get_tgt(void);

/* found in class.c */
extern String *class_control, *class_admin, *class_hm;
extern String *class_ulogin, *class_ulocate;
int ZDest_eq(Destination *d1, Destination *d2);
Code_t triplet_register(Client *client, Destination *dest, ZRealm *realm);
Code_t triplet_deregister(Client *client, Destination *dest,
			       ZRealm *realm);
Code_t class_restrict(char *class_name, Acl *acl);
Code_t class_setup_restricted(char *class_name, Acl *acl);
Client **triplet_lookup(Destination *dest);
Acl *class_get_acl(String *class_name);
int dest_eq(Destination *d1, Destination *d2);
int order_dest_strings(Destination *d1, Destination *d2);
void triplet_dump_subs(FILE *fp);

/* found in client.c */
Code_t client_register(ZNotice_t *notice, struct in_addr *host,
			    Client **client_p, int wantdefaults);
void client_deregister(Client *client, int flush);
void client_flush_host(struct in_addr *host);
void client_flush_princ(char *target);
void client_dump_clients(FILE *fp);
Client *client_find(struct in_addr *host, unsigned int port);
Code_t client_send_clients(void);

/* found in common.c */
char *strsave(const char *str);
unsigned long hash (const char *);
void dump_quote(char *p, FILE *fp);
void notice_extract_address(ZNotice_t *notice, struct sockaddr_in *addr);
int packets_waiting(void);

/* found in dispatch.c */
void handle_packet(void);
void clt_ack(ZNotice_t *notice, struct sockaddr_in *who, Sent_type sent);
void nack_release(Client *client);
void sendit(ZNotice_t *notice, int auth, struct sockaddr_in *who,
		 int external);
void rexmit(void *);
void xmit(ZNotice_t *notice, struct sockaddr_in *dest, int auth,
	       Client *client);
Code_t hostm_dispatch(ZNotice_t *notice, int auth,
			   struct sockaddr_in *who, Server *server);
Code_t control_dispatch(ZNotice_t *notice, int auth,
			     struct sockaddr_in *who, Server *server);
Code_t xmit_frag(ZNotice_t *notice, char *buf, int len, int waitforack);
void hostm_shutdown(void);

/* found in kstuff.c */
Code_t ZCheckSrvAuthentication(ZNotice_t *notice, struct sockaddr_in *from, char *realm);
#if defined(HAVE_KRB4) || defined(HAVE_KRB5)
Code_t ReadKerberosData(int, int *, char **, int *);
void sweep_ticket_hash_table(void *);
#endif
#ifdef HAVE_KRB4
int GetKerberosData (int, struct in_addr, AUTH_DAT *, char *, char *);
Code_t SendKerberosData (int, KTEXT, char *, char *);
#endif
#ifdef HAVE_KRB5
Code_t SendKrb5Data(int, krb5_data *);
Code_t GetKrb5Data(int, krb5_data *);
#endif

/* found in server.c */
void server_timo(void *which);
void server_dump_servers(FILE *fp);
void server_init(void);
void server_shutdown(void);
void server_forward(ZNotice_t *notice, int auth,
			 struct sockaddr_in *who);
void server_kill_clt(Client *client);
void server_pending_free(Pending *pending);
void server_self_queue(ZNotice_t *, int, struct sockaddr_in *);
void server_send_queue(Server *);
void server_reset(void);
Server *server_which_server(struct sockaddr_in *who);
Pending *server_dequeue(Server *server);
Code_t server_dispatch(ZNotice_t *notice, int auth,
			    struct sockaddr_in *who);
Code_t server_adispatch(ZNotice_t *notice, int auth,
			     struct sockaddr_in *who, Server *server);

/* found in subscr.c */
Code_t subscr_foreign_user(ZNotice_t *, struct sockaddr_in *, Server *, ZRealm *);
Code_t subscr_cancel(struct sockaddr_in *sin, ZNotice_t *notice);
Code_t subscr_subscribe(Client *who, ZNotice_t *notice, Server *server);
Code_t subscr_send_subs(Client *client);
void subscr_cancel_client(Client *client);
void subscr_sendlist(ZNotice_t *notice, int auth,
			  struct sockaddr_in *who);
void subscr_dump_subs(FILE *fp, Destlist *subs);
void subscr_reset(void);
Code_t subscr_def_subs(Client *who);
Code_t subscr_realm(ZRealm *, ZNotice_t *);
Code_t subscr_send_realm_subs(ZRealm *);
Code_t subscr_realm_subs(ZRealm *);
Code_t subscr_realm_cancel(struct sockaddr_in *, ZNotice_t *, ZRealm *);

/* found in uloc.c */
void uloc_hflush(struct in_addr *addr);
void uloc_flush_client(struct sockaddr_in *sin);
void uloc_dump_locs(FILE *fp);
Code_t ulogin_dispatch(ZNotice_t *notice, int auth,
			    struct sockaddr_in *who, Server *server);
Code_t ulocate_dispatch(ZNotice_t *notice, int auth,
			     struct sockaddr_in *who, Server *server);
Code_t uloc_send_locations(void);
void ulogin_relay_locate(ZNotice_t *, struct sockaddr_in *);
void ulogin_realm_locate(ZNotice_t *, struct sockaddr_in *, ZRealm *);

/* found in realm.c */
int realm_sender_in_realm(const char *realm, char *sender);
int realm_bound_for_realm(const char *realm, char *recip);
ZRealm *realm_which_realm(struct sockaddr_in *who);
ZRealm *realm_get_realm_by_name(char *name);
ZRealm *realm_get_realm_by_name_string(String *namestr);
ZRealm *realm_get_realm_by_pid(int);
void realm_handoff(ZNotice_t *, int, struct sockaddr_in *, ZRealm *, int);
const char *realm_expand_realm(char *);
void realm_init(void);
Code_t ZCheckZRealmAuthentication(ZNotice_t *, struct sockaddr_in *,
				      char *);
Code_t realm_control_dispatch(ZNotice_t *, int, struct sockaddr_in *,
				   Server *, ZRealm *);
void realm_shutdown(void);
void realm_deathgram(Server *);
Code_t realm_send_realms(void);
Code_t realm_dispatch(ZNotice_t *, int, struct sockaddr_in *, Server *);
void kill_realm_pids(void);
void realm_dump_realms(FILE *);

/* found in version.c */
char *get_version(void);

/* found in access.c */
int access_check(char *, struct sockaddr_in *, Acl *, Access);
int opstaff_check(char *);

/* global identifiers */

/* found in global.c */
extern struct sockaddr_in srv_addr;	/* server socket address */
extern unsigned short hm_port;		/* host manager receiver port */
extern unsigned short hm_srv_port;	/* host manager server sending port */
extern int srv_socket;			/* dgram sockets for clients
					   and other servers */
extern int bdump_socket;		/* brain dump socket
					   (closed most of the time) */
#ifdef HAVE_ARES
extern ares_channel achannel;
#endif

extern fd_set interesting;		/* the file descrips we are listening
					 to right now */
extern int nfds;			/* number to look at in select() */
extern int zdebug;
extern char myname[];			/* domain name of this host */
extern char list_file[];
#ifdef HAVE_KRB5
extern char keytab_file[];
extern krb5_ccache Z_krb5_ccache;
#endif
#ifdef HAVE_KRB4
extern char srvtab_file[];
#endif
extern char acl_dir[];
extern char subs_file[];
extern const char version[];
extern u_long npackets;			/* num of packets processed */
extern time_t uptime;			/* time we started */
extern struct in_addr my_addr;		/* my inet address */
extern struct timeval t_local;		/* current time */
extern char *bdump_version;
extern int bdump_auth_proto;

/* found in bdump.c */
extern int bdumping;			/* are we processing a bdump packet? */
extern int bdump_concurrent;		/* set while processing a packet
					 * concurrently during a braindump. */

/* found in dispatch.c */
extern Statistic i_s_ctls, i_s_logins, i_s_admins, i_s_locates;
extern int rexmit_times[];

/* found in server.c */
extern Server *otherservers;		/* array of servers */
extern int me_server_idx;		/* me (in the array of servers) */
extern int nservers;			/* number of other servers*/

/* found in subscr.c */
extern String *empty;
extern String *wildcard_instance;

extern ZRealm **otherrealms;
extern int nrealms;


#define class_is_control(classname) (classname == class_control)
#define class_is_admin(classname) (classname == class_admin)
#define class_is_hm(classname) (classname == class_hm)
#define class_is_ulogin(classname) (classname == class_ulogin)
#define class_is_ulocate(classname) (classname == class_ulocate)

#define	ADMIN_HELLO	"HELLO"		/* Opcode: hello, are you there */
#define	ADMIN_IMHERE	"IHEARDYOU"	/* Opcode: yes, I am here */
#define	ADMIN_SHUTDOWN	"GOODBYE"	/* Opcode: I am shutting down */
#define ADMIN_BDUMP	"DUMP_AVAIL"	/* Opcode: I will give you a dump */
#define	ADMIN_DONE	"DUMP_DONE"	/* Opcode: brain dump for this server
					   is complete */
#define	ADMIN_NEWCLT	"NEXT_CLIENT"	/* Opcode: this is a new client */
#define	ADMIN_KILL_CLT	"KILL_CLIENT"	/* Opcode: client is dead, remove */
#define	ADMIN_STATUS	"STATUS"	/* Opcode: please send status */

#define ADMIN_NEWREALM	"NEXT_REALM"	/* Opcode: this is a new realm */
#define REALM_REQ_LOCATE "REQ_LOCATE"	/* Opcode: request a location */
#define REALM_ANS_LOCATE "ANS_LOCATE"	/* Opcode: answer to location */
#define REALM_BOOT      "SENDSUBS"	/* Opcode: first server in realm */

/* me_server_idx is the index into otherservers of this server descriptor. */
/* the 'limbo' server is always the first server */

#define	me_server	(&otherservers[me_server_idx])
#define limbo_server_idx()	(0)
#define	limbo_server	(&otherservers[limbo_server_idx()])

#define msgs_queued()	(ZQLength() || otherservers[me_server_idx].queue)

#define	ack(a,b)	clt_ack(a,b,SENT)
#define	nack(a,b)	clt_ack(a,b,NOT_SENT)

#define	min(a,b)	((a) < (b) ? (a) : (b))
#define	max(a,b)	((a) > (b) ? (a) : (b))

#define START_CRITICAL_CODE
#define END_CRITICAL_CODE

/* the instance that matches all instances */
#define	WILDCARD_INSTANCE	"*"

/* debugging macros */
#ifdef DEBUG
#define zdbug(s1)	if (zdebug) syslog s1;
#else /* !DEBUG */
#define zdbug(s1)
#endif /* DEBUG */

#endif /* !__ZSERVER_H__ */
