/*
* treewalk.h - written by ale in milano on 27 May 2024
* Look up a domain and its parents as described in DMARCbis.

Copyright (C) 2024 Alessandro Vesely

This file is part of zdkimfilter

zdkimfilter is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

zdkimfilter is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License version 3
along with zdkimfilter.  If not, see <http://www.gnu.org/licenses/>.

Additional permission under GNU GPLv3 section 7:

If you modify zdkimfilter, or any covered part of it, by linking or combining
it with OpenSSL, OpenDKIM, Sendmail, or any software developed by The Trusted
Domain Project or Sendmail Inc., containing parts covered by the applicable
licence, the licensor of zdkimfilter grants you additional permission to convey
the resulting work.
*/
#if ! defined TREEWALK_H_INCLUDED

#define MAX_SUBDOMAINS 8
#define MAX_NAMESERVERS 8
#define RESOLV_CONF "/etc/resolv.conf"

// copied from ipqbdb's util.h
#include <stdint.h>
union base_ip_fields
{
	unsigned char ip_data[16];
	uint32_t ipv4l;
	unsigned char ipv4[4];
	unsigned char ipv6[16];
	uint64_t ipv6l[2];
};

#define BASE_STRUCT_FIELDS                  \
	int8_t ip;    /* 4 or 6               */ \
	int8_t ipv4_mapped; /* was IPv6       */ \
	int8_t nu[6];                            \
	union  base_ip_fields u;

typedef struct ip_u
{
	BASE_STRUCT_FIELDS
} ip_u;

#include <resolv.h>
#include "parm.h"

typedef enum identifier_type
{
	identifier_dkim,
	identifier_spf
} identifier_type;

typedef struct resolver_state
{
	parm_t *parm;
	res_state statep;
	unsigned timeout, attempts, nservers;
	int af;
	int good_ns, cur_ns;
	char nu[4]; // 8-bit alignment
	ip_u servers[];
} resolver_state;

typedef struct dmarc_rec
{
	char *rua;         // malloc'd with sentinel
	unsigned opt_adkim: 2;
	unsigned opt_aspf: 2;
	unsigned opt_p: 2;
	unsigned opt_sp: 2;
	unsigned opt_np: 2;
	unsigned opt_pct: 2;
	unsigned opt_t: 2;
	unsigned opt_psd: 2;
	int8_t found; // 1 if valid record found
	int8_t adkim, aspf, p, sp, np, pct, t, psd;
	int8_t nu[7];
} dmarc_rec;

typedef struct domain_data
{
	char const *domain; // subdomain pointing into caller's arg
	dmarc_rec dr;
	enum dmarc_status
	{
		dmarc_none,
		dmarc_ok,
		dmarc_garbled
	} status;
} domain_data;

typedef struct tree_walk
{
	char *free_domain; // domain arg, free'd by clear_treewalk()
	char *different_od; // org domain by PSL, also free'd
	unsigned ndomains;
	unsigned nrecords;
	unsigned policy, org;
	int8_t effective_p;
	int8_t top_domain_exists; // -1 not tested, 0 not exist, 1 exists
	uint8_t attempt;  // 0 on first attempt
	int8_t nu[5];
	domain_data dd[];
} tree_walk;

resolver_state *init_resolv(parm_t *parm, char const *resolv_conf);
void clear_resolv(resolver_state *rs);
void clear_treewalk(tree_walk *tw);
tree_walk *treewalk(resolver_state *rs, char *domain);
int is_aligned(resolver_state *rs, tree_walk *tw, identifier_type id, char const *domain);
int top_domain_exists(resolver_state *rs, tree_walk *tw);
int parse_dmarc(char *record, dmarc_rec *dmarc);
char* write_dmarc_rec(dmarc_rec const *dmarc, int all);
dmarc_rec *get_policy_record(tree_walk *tw);
int check_remove_sentinel(char *rua);
int check_domain_exists(resolver_state *rs, char const *domain);
char* adjust_rua(char**, char**);
void set_treewalk_faked(void);
int is_subdomain(char const *domain, char const *other_domain);
#define TREEWALK_H_INCLUDED
#endif

