#line 1026 "../../src/builtin/snarf.m4"
/* -*- buffer-read-only: t -*- vi: set ro:
   THIS FILE IS GENERATED AUTOMATICALLY.  PLEASE DO NOT EDIT.
*/
#line 1026
#ifdef HAVE_CONFIG_H
#line 1026
# include <config.h>
#line 1026
#endif
#line 1026
#include <sys/types.h>
#line 1026

#line 1026
#include "mailfromd.h"
#line 1026
#include "prog.h"
#line 1026
#include "builtin.h"
#line 1026

#line 1026

#line 1066 "../../src/builtin/snarf.m4"

/* End of snarf.m4 */
#line 1 "dns.bi"
/* This file is part of Mailfromd.             -*- c -*-
   Copyright (C) 2006-2025 Sergey Poznyakoff

   This program 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, or (at your option)
   any later version.

   This program 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
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */



#include <inttypes.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <limits.h>
#include "srvcfg.h"
#include "global.h"
#include <mflib/status.h>

void
#line 29
bi_primitive_hostname(eval_environ_t env)
#line 29

#line 29

#line 29 "dns.bi"
{
#line 29
	
#line 29

#line 29
        char * MFL_DATASEG string;
#line 29
        
#line 29

#line 29
        get_string_arg(env, 0, &string);
#line 29
        
#line 29
        adjust_stack(env, 1);
#line 29

#line 29

#line 29
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 29
		prog_trace(env, "primitive_hostname %s",string);;
#line 29

{
	char *hbuf;
	mf_status stat;

	stat = resolve_ipstr(string, &hbuf);
		if (!(stat == mf_success))
#line 35
		(
#line 35
	env_throw_bi(env, mf_status_to_exception(stat), "primitive_hostname", _("cannot resolve IP %s"),string)
#line 35
)
#line 38
;

	pushs(env, hbuf);
	free(hbuf);
}

#line 43
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 43
	return;
#line 43
}

static int
has_suffix(char const *str, char const *suf)
{
	size_t len = strlen(str);
	size_t slen = strlen(suf);
	return len > slen && memcmp(str + len - slen, suf, slen) == 0;
}

static void
cleanup_reply(void *reply)
{
	dns_reply_free(reply);
}


void
#line 60
bi_primitive_resolve(eval_environ_t env)
#line 60

#line 60

#line 60 "dns.bi"
{
#line 60
	
#line 60

#line 60
        char * MFL_DATASEG string;
#line 60
        char * MFL_DATASEG domain;
#line 60
        long  resolve_family;
#line 60
        
#line 60
        long __bi_argcnt;
#line 60
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 60
        get_string_arg(env, 1, &string);
#line 60
        if (__bi_argcnt > 1)
#line 60
                get_string_arg(env, 2, &domain);
#line 60
        if (__bi_argcnt > 2)
#line 60
                get_numeric_arg(env, 3, &resolve_family);
#line 60
        
#line 60
        adjust_stack(env, __bi_argcnt + 1);
#line 60

#line 60

#line 60
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 60
		prog_trace(env, "primitive_resolve %s %s %lu",string, ((__bi_argcnt > 1) ? domain : ""), ((__bi_argcnt > 2) ? resolve_family : 0));;

{
	struct dns_reply reply;
	if (((__bi_argcnt > 1) ? domain : "")[0]) {
		dns_status status;
		char ipstr[IPMAX_INADDR_BUFSIZE];

		if (strcasecmp(domain, IPV4_INADDR_DOMAIN) == 0 ||
		    strcasecmp(domain, IPV6_INADDR_DOMAIN) == 0) {
			status = dns_reverse_name(string, ipstr, sizeof(ipstr));
				if (!(status == dns_success))
#line 71
		(
#line 71
	env_throw_bi(env, mf_status_to_exception(dns_to_mf_status(status)), "primitive_resolve", _("cannot create reverse name from %s and %s"),string,domain)
#line 71
)
#line 74
;
				if (!(has_suffix(ipstr, domain)))
#line 75
		(
#line 75
	env_throw_bi(env, dns_failure, "primitive_resolve", _("domain %s is not suitable for PTR record %s"),domain,string)
#line 75
)
#line 78
;
			status = ptr_lookup(ipstr, &reply);
				if (!(status == dns_success))
#line 80
		(
#line 80
	env_throw_bi(env, mf_status_to_exception(dns_to_mf_status(status)), "primitive_resolve", _("cannot resolve host name %s"),ipstr)
#line 80
)
#line 82
;
		} else if (dns_str_is_ipv4(string) || dns_str_is_ipv6(string)) {
			char *qname;
			int n = dns_reverse_ipstr(string, ipstr, sizeof(ipstr));
				if (!(n > 0))
#line 86
		(
#line 86
	env_throw_bi(env, dns_failure, "primitive_resolve", _("cannot reverse IP %s"),string)
#line 86
)
#line 89
;
			qname = mf_c_val(heap_tempspace(env, n + strlen(domain)), ptr);
			memcpy(qname, ipstr, n);
			strcpy(qname + n, domain);
			status = a_lookup(qname, &reply);
			if (status == dns_not_found) {
				status = aaaa_lookup(qname, &reply);
			}
				if (!(status == dns_success))
#line 97
		(
#line 97
	env_throw_bi(env, mf_status_to_exception(dns_to_mf_status(status)), "primitive_resolve", _("cannot resolve host name %s"),ipstr)
#line 97
)
#line 99
;
		} else {
			char *qname;
			qname = mf_c_val(heap_tempspace(env, strlen(string) + strlen(domain) + 2), ptr);
			strcpy(qname, string);
			strcat(qname, ".");
			strcat(qname, domain);
			string = qname;
			goto resolve;
		}
		env_function_cleanup_add(env, CLEANUP_ALWAYS, &reply, cleanup_reply);
		switch (reply.type) {
		case dns_reply_str:
			
#line 112
do {
#line 112
  pushs(env, reply.data.str[0]);
#line 112
  goto endlab;
#line 112
} while (0);
		case dns_reply_ip:
			
#line 114
do {
#line 114
  pushs(env, inet_ntop(AF_INET, &reply.data.ip[0], ipstr,
#line 114
					    sizeof(ipstr)));
#line 114
  goto endlab;
#line 114
} while (0);
#line 116
		case dns_reply_ip6:
			
#line 117
do {
#line 117
  pushs(env, inet_ntop(AF_INET6, &reply.data.ip6[0], ipstr,
#line 117
					    sizeof(ipstr)));
#line 117
  goto endlab;
#line 117
} while (0);
#line 119
		default:
			runtime_error(env, _("bad DNS reply type: %d"), reply.type);
		}
	} else {
		char *ipstr;
		mf_status status;
		int res;
resolve:
		res = ((__bi_argcnt > 2) ? resolve_family : _MFL_RESOLVE_DFL);
		if (res == _MFL_RESOLVE_DFL) {
			res = (env_socket_family(env) == AF_INET)
				? resolve_ip4 : resolve_ip6;
		} else if (res != _MFL_RESOLVE_IP4 && res != _MFL_RESOLVE_IP6) {
			(
#line 132
	env_throw_bi(env, mfe_inval, "primitive_resolve", _("invalid resolve family value"))
#line 132
);
#line 134
		}
		status = resolve_hostname(string, res, &ipstr);
			if (!(status == mf_success))
#line 136
		(
#line 136
	env_throw_bi(env, mf_status_to_exception(status), "primitive_resolve", _("cannot resolve %s"),string)
#line 136
)
#line 138
;
		env_function_cleanup_add(env, CLEANUP_ALWAYS, ipstr, free);
		
#line 140
do {
#line 140
  pushs(env, ipstr);
#line 140
  goto endlab;
#line 140
} while (0);
	}
}
endlab:
#line 143
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 143
	return;
#line 143
}

void
#line 145
bi_primitive_hasmx(eval_environ_t env)
#line 145

#line 145

#line 145 "dns.bi"
{
#line 145
	
#line 145

#line 145
        char *  string;
#line 145
        
#line 145

#line 145
        get_string_arg(env, 0, &string);
#line 145
        
#line 145
        adjust_stack(env, 1);
#line 145

#line 145

#line 145
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 145
		prog_trace(env, "primitive_hasmx %s",string);;
#line 145

{
	struct dns_reply repl;
	mf_status mxstat;

	mxstat = dns_to_mf_status(mx_lookup(string, resolve_none, &repl));

		if (!(mxstat == mf_success || mxstat == mf_not_found))
#line 152
		(
#line 152
	env_throw_bi(env, mf_status_to_exception(mxstat), "primitive_hasmx", _("cannot get MX records for %s"),string)
#line 152
)
#line 155
;
	if (mxstat == mf_success) {
		dns_reply_free(&repl);
		
#line 158
do {
#line 158
  push(env, (STKVAL)(mft_number)(1));
#line 158
  goto endlab;
#line 158
} while (0);
	}
	
#line 160
do {
#line 160
  push(env, (STKVAL)(mft_number)(0));
#line 160
  goto endlab;
#line 160
} while (0);
}
endlab:
#line 162
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 162
	return;
#line 162
}

static dns_status
resolve_host(const char *string, int family, struct dns_reply *reply, int *isip)
{
	struct addrinfo *res, hints;

	memset(&hints, 0, sizeof(hints));
	hints.ai_flags = AI_NUMERICHOST;
	hints.ai_family = family;
	if (getaddrinfo(string, NULL, &hints, &res) ==  0) {
		if (isip)
			*isip = 1;
		dns_reply_init(reply,
			       res->ai_family == AF_INET
				 ? dns_reply_ip : dns_reply_ip6, 1);
		switch (res->ai_family) {
		case AF_INET:
			reply->data.ip[0] = ((struct sockaddr_in*)res->ai_addr)->sin_addr;
			break;

		case AF_INET6:
			reply->data.ip6[0] = ((struct sockaddr_in6*)res->ai_addr)->sin6_addr;
			break;
		}
		freeaddrinfo(res);
		return dns_success;
	} else if (isip)
		*isip = 0;

	return (family == AF_INET6 ? aaaa_lookup : a_lookup)(string, reply);
}

static int
dns_replies_intersect(struct dns_reply const *a, struct dns_reply const *b)
{
	int i, j;

	if (a->type == b->type) {
		switch (a->type) {
		case dns_reply_str:
			for (i = 0; i < a->count; i++)
				for (j = 0; j < b->count; j++)
					if (strcmp(a->data.str[i], b->data.str[j]) == 0)
						return 1;
			break;

		case dns_reply_ip:
			for (i = 0; i < a->count; i++)
				for (j = 0; j < b->count; j++)
					if (a->data.ip[i].s_addr == b->data.ip[j].s_addr)
						return 1;
			break;

		case dns_reply_ip6:
			for (i = 0; i < a->count; i++)
				for (j = 0; j < b->count; j++)
					if (memcmp(&a->data.ip6[i],
						   &b->data.ip6[j],
						   sizeof(a->data.ip6[0])) == 0)
						return 1;
			break;
		}
	}
	return 0;
}

void
#line 229
bi_primitive_ismx(eval_environ_t env)
#line 229

#line 229

#line 229 "dns.bi"
{
#line 229
	
#line 229

#line 229
        char *  domain;
#line 229
        char *  ipstr;
#line 229
        
#line 229

#line 229
        get_string_arg(env, 0, &domain);
#line 229
        get_string_arg(env, 1, &ipstr);
#line 229
        
#line 229
        adjust_stack(env, 2);
#line 229

#line 229

#line 229
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 229
		prog_trace(env, "primitive_ismx %s %s",domain, ipstr);;
#line 229

{
	struct dns_reply areply, mxreply;
	dns_status status;
	int ip;
	int rc = 0;
	int not_found = 0;

	/*
	 * FIXME: The approach below results in two DNS MX queries.
	 * Two ways to avoid it: (1) cache hostnames obtained from the
	 * first MX query and reuse them later to query AAAA records,
	 * and (2) combine dns_reply_ip and dns_reply_ip6 into single
	 * reply type and introduce a resolve_any constant, meaning
	 * "resolve to IPv4 or IPv6".
	 */
	status = resolve_host(ipstr, AF_INET, &areply, &ip);
	switch (status) {
	case dns_success:
		status = mx_lookup(domain, resolve_ip4, &mxreply);
		switch (status) {
		case dns_success:
			rc = dns_replies_intersect(&areply, &mxreply);
			break;
		case dns_not_found:
			not_found = 1;
			break;
		default:
			dns_reply_free(&areply);
			(
#line 258
	env_throw_bi(env, mf_status_to_exception(dns_to_mf_status(status)), "primitive_ismx", _("cannot get MXs for %s"),domain)
#line 258
);
#line 260
		}
		dns_reply_free(&areply);
		dns_reply_free(&mxreply);
		break;
	case dns_not_found:
		break;
	default:
		(
#line 267
	env_throw_bi(env, mf_status_to_exception(dns_to_mf_status(status)), "primitive_ismx", _("cannot resolve host name %s"),ipstr)
#line 267
);
#line 269
	}

	if (rc == 0 && !ip) {
		status = resolve_host(ipstr, AF_INET6, &areply, NULL);
		switch (status) {
		case dns_success:
			status = mx_lookup(domain, resolve_ip6, &mxreply);
			switch (status) {
			case dns_success:
				rc = dns_replies_intersect(&areply, &mxreply);
				break;
			case dns_not_found:
				if (not_found)
					(
#line 282
	env_throw_bi(env, mf_status_to_exception(mfe_not_found), "primitive_ismx", _("%s has no MX records"),ipstr)
#line 282
);
#line 285
				break;
			default:
				dns_reply_free(&areply);
				(
#line 288
	env_throw_bi(env, mf_status_to_exception(dns_to_mf_status(status)), "primitive_ismx", _("cannot resolve host name %s"),ipstr)
#line 288
);
#line 290
			}
			dns_reply_free(&areply);
			dns_reply_free(&mxreply);
			break;
		case dns_not_found:
			break;
		default:
			(
#line 297
	env_throw_bi(env, mf_status_to_exception(dns_to_mf_status(status)), "primitive_ismx", _("cannot resolve host name %s"),ipstr)
#line 297
);
#line 299
		}
	}
	
#line 301
do {
#line 301
  push(env, (STKVAL)(mft_number)(rc));
#line 301
  goto endlab;
#line 301
} while (0);
}
endlab:
#line 303
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 303
	return;
#line 303
}

void
#line 305
bi_relayed(eval_environ_t env)
#line 305

#line 305

#line 305 "dns.bi"
{
#line 305
	
#line 305

#line 305
        char *  s;
#line 305
        
#line 305

#line 305
        get_string_arg(env, 0, &s);
#line 305
        
#line 305
        adjust_stack(env, 1);
#line 305

#line 305

#line 305
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 305
		prog_trace(env, "relayed %s",s);;
#line 305

{
	
#line 307
do {
#line 307
  push(env, (STKVAL)(mft_number)(relayed_domain_p(s)));
#line 307
  goto endlab;
#line 307
} while (0);
}
endlab:
#line 309
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 309
	return;
#line 309
}

void
#line 311
bi_ptr_validate(eval_environ_t env)
#line 311

#line 311

#line 311 "dns.bi"
{
#line 311
	
#line 311

#line 311
        char *  s;
#line 311
        
#line 311

#line 311
        get_string_arg(env, 0, &s);
#line 311
        
#line 311
        adjust_stack(env, 1);
#line 311

#line 311

#line 311
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 311
		prog_trace(env, "ptr_validate %s",s);;
#line 311

{
	int rc, res;
	switch (rc = ptr_validate(s, NULL)) {
	case dns_success:
		res = 1;
		break;
	case dns_not_found:
		res = 0;
		break;
	default:
		(
#line 322
	env_throw_bi(env, mf_status_to_exception(dns_to_mf_status(rc)), "ptr_validate", _("failed to get PTR record for %s"),s)
#line 322
);
#line 324
	}
	
#line 325
do {
#line 325
  push(env, (STKVAL)(mft_number)(res));
#line 325
  goto endlab;
#line 325
} while (0);
}
endlab:
#line 327
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 327
	return;
#line 327
}

void
#line 329
bi_primitive_hasns(eval_environ_t env)
#line 329

#line 329

#line 329 "dns.bi"
{
#line 329
	
#line 329

#line 329
        char *  dom;
#line 329
        
#line 329

#line 329
        get_string_arg(env, 0, &dom);
#line 329
        
#line 329
        adjust_stack(env, 1);
#line 329

#line 329

#line 329
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 329
		prog_trace(env, "primitive_hasns %s",dom);;
#line 329

{
	struct dns_reply repl;
	mf_status stat = dns_to_mf_status(ns_lookup(dom, resolve_none, &repl));
		if (!(stat == mf_success || stat == mf_not_found))
#line 333
		(
#line 333
	env_throw_bi(env, mf_status_to_exception(stat), "primitive_hasns", _("cannot get NS records for %s"),dom)
#line 333
)
#line 336
;
	if (stat == mf_success) {
		dns_reply_free(&repl);
		
#line 339
do {
#line 339
  push(env, (STKVAL)(mft_number)(1));
#line 339
  goto endlab;
#line 339
} while (0);
	}
	
#line 341
do {
#line 341
  push(env, (STKVAL)(mft_number)(0));
#line 341
  goto endlab;
#line 341
} while (0);
}
endlab:
#line 343
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 343
	return;
#line 343
}

static int
cmp_ip(void const *a, void const *b)
{
	uint32_t ipa = ntohl(((struct in_addr*)a)->s_addr);
	uint32_t ipb = ntohl(((struct in_addr*)b)->s_addr);
	if (ipa < ipb)
		return -1;
	if (ipa > ipb)
		return 1;
	return 0;
}

static int
cmp_ip6(void const *a, void const *b)
{
	return memcmp(a, b, sizeof(struct in6_addr));
}

static int
cmp_str(void const *a, void const *b)
{
	char * const *stra = a;
	char * const *strb = b;
	return strcmp(*stra, *strb);
}

static int
cmp_hostname(const void *a, const void *b)
{
	return strcasecmp(*(const char**) a, *(const char**) b);
}

typedef long DNS_REPLY_COUNT;
#define DNS_REPLY_MAX LONG_MAX

struct dns_reply_storage {
	DNS_REPLY_COUNT reply_count;
	size_t reply_max;
	struct dns_reply *reply_tab;
};

/*
 * Return true if N is in valid range for accessing reply storage RS and
 * the entry it refers to is in use.
 */
static inline int
dns_reply_entry_ok(struct dns_reply_storage *rs, DNS_REPLY_COUNT n)
{
	return n >= 0 && n < rs->reply_count && rs->reply_tab[n].type != -1;
}

static inline void
dns_reply_entry_release(struct dns_reply_storage *rs, DNS_REPLY_COUNT n)
{
	rs->reply_tab[n].type = -1;
}

static inline struct dns_reply *
dns_reply_entry_locate(struct dns_reply_storage *rs, DNS_REPLY_COUNT n)
{
	return dns_reply_entry_ok(rs, n) ? &rs->reply_tab[n] : NULL;
}

static void *
dns_reply_storage_alloc(void)
{
	struct dns_reply_storage *rs = mu_alloc(sizeof(*rs));
	rs->reply_count = 0;
	rs->reply_max = 0;
	rs->reply_tab = NULL;
	return rs;
}

static void
dns_reply_storage_destroy(void *data)
{
	struct dns_reply_storage *rs = data;
	DNS_REPLY_COUNT i;

	for (i = 0; i < rs->reply_count; i++) {
		if (dns_reply_entry_ok(rs, i)) {
			dns_reply_free(&rs->reply_tab[i]);
			dns_reply_entry_release(rs, i);
		}
	}
	free(rs->reply_tab);
	free(rs);
}


#line 434

#line 434
static int DNS_id;
#line 434 "dns.bi"


static inline DNS_REPLY_COUNT
dns_reply_entry_alloc(struct dns_reply_storage *rs,
		      struct dns_reply **return_reply)
{
	DNS_REPLY_COUNT i;

	for (i = 0; i < rs->reply_count; i++) {
		if (!dns_reply_entry_ok(rs, i)) {
			*return_reply = &rs->reply_tab[i];
			return i;
		}
	}
	if (rs->reply_count == DNS_REPLY_MAX)
		return -1;
	if (rs->reply_count == rs->reply_max) {
		size_t n = rs->reply_max;
		rs->reply_tab = mu_2nrealloc(rs->reply_tab, &rs->reply_max,
					     sizeof(rs->reply_tab[0]));
		for (; n < rs->reply_max; n++)
			dns_reply_entry_release(rs, n);
	}
	*return_reply = &rs->reply_tab[rs->reply_count];
	return rs->reply_count++;
}

void
#line 461
bi_dns_reply_release(eval_environ_t env)
#line 461

#line 461

#line 461 "dns.bi"
{
#line 461
	
#line 461

#line 461
        long  n;
#line 461
        
#line 461

#line 461
        get_numeric_arg(env, 0, &n);
#line 461
        
#line 461
        adjust_stack(env, 1);
#line 461

#line 461

#line 461
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 461
		prog_trace(env, "dns_reply_release %lu",n);;
#line 461

{
	struct dns_reply_storage *repl = env_get_builtin_priv(env,DNS_id);
	if (dns_reply_entry_ok(repl, n)) {
		dns_reply_free(&repl->reply_tab[n]);
		dns_reply_entry_release(repl, n);
	}
}

#line 469
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 469
	return;
#line 469
}

void
#line 471
bi_dns_reply_count(eval_environ_t env)
#line 471

#line 471

#line 471 "dns.bi"
{
#line 471
	
#line 471

#line 471
        long  n;
#line 471
        
#line 471

#line 471
        get_numeric_arg(env, 0, &n);
#line 471
        
#line 471
        adjust_stack(env, 1);
#line 471

#line 471

#line 471
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 471
		prog_trace(env, "dns_reply_count %lu",n);;
#line 471

{
	struct dns_reply_storage *rs = env_get_builtin_priv(env,DNS_id);
	struct dns_reply *reply;

	if (n == -1)
		
#line 477
do {
#line 477
  push(env, (STKVAL)(mft_number)(0));
#line 477
  goto endlab;
#line 477
} while (0);
	if (n < 0)
		(
#line 479
	env_throw_bi(env, mfe_failure, "dns_reply_count", _("invalid DNS reply number: %ld"),n)
#line 479
);
#line 481
	reply = dns_reply_entry_locate(rs, n);
	if (!reply)
		(
#line 483
	env_throw_bi(env, mfe_failure, "dns_reply_count", _("no such reply: %ld"),n)
#line 483
);
#line 485
	
#line 485
do {
#line 485
  push(env, (STKVAL)(mft_number)(reply->count));
#line 485
  goto endlab;
#line 485
} while (0);
}
endlab:
#line 487
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 487
	return;
#line 487
}

void
#line 489
bi_dns_reply_string(eval_environ_t env)
#line 489

#line 489

#line 489 "dns.bi"
{
#line 489
	
#line 489

#line 489
        long  n;
#line 489
        long  i;
#line 489
        
#line 489

#line 489
        get_numeric_arg(env, 0, &n);
#line 489
        get_numeric_arg(env, 1, &i);
#line 489
        
#line 489
        adjust_stack(env, 2);
#line 489

#line 489

#line 489
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 489
		prog_trace(env, "dns_reply_string %lu %lu",n, i);;
#line 489

{
	struct dns_reply_storage *rs = env_get_builtin_priv(env,DNS_id);
	struct dns_reply *reply = dns_reply_entry_locate(rs, n);
	char ipstr[IPMAX_DOTTED_BUFSIZE];

	if (!reply)
		(
#line 496
	env_throw_bi(env, mfe_failure, "dns_reply_string", _("no such reply: %ld"),n)
#line 496
);
#line 498
	if (i < 0 || i >= reply->count)
		(
#line 499
	env_throw_bi(env, mfe_range, "dns_reply_string", _("index out of range: %ld"),i)
#line 499
);
#line 501
	switch (reply->type) {
	case dns_reply_str:
		
#line 503
do {
#line 503
  pushs(env, reply->data.str[i]);
#line 503
  goto endlab;
#line 503
} while (0);
	case dns_reply_ip:
		
#line 505
do {
#line 505
  pushs(env, inet_ntop(AF_INET, &reply->data.ip[i], ipstr,
#line 505
				    sizeof(ipstr)));
#line 505
  goto endlab;
#line 505
} while (0);
#line 507
	case dns_reply_ip6:
		
#line 508
do {
#line 508
  pushs(env, inet_ntop(AF_INET6, &reply->data.ip6[i], ipstr,
#line 508
				    sizeof(ipstr)));
#line 508
  goto endlab;
#line 508
} while (0);
#line 510
	default:
		runtime_error(env, _("bad DNS reply type: %d"), reply->type);
	}
}
endlab:
#line 514
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 514
	return;
#line 514
}

void
#line 516
bi_dns_reply_ip(eval_environ_t env)
#line 516

#line 516

#line 516 "dns.bi"
{
#line 516
	
#line 516

#line 516
        long  n;
#line 516
        long  i;
#line 516
        
#line 516

#line 516
        get_numeric_arg(env, 0, &n);
#line 516
        get_numeric_arg(env, 1, &i);
#line 516
        
#line 516
        adjust_stack(env, 2);
#line 516

#line 516

#line 516
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 516
		prog_trace(env, "dns_reply_ip %lu %lu",n, i);;
#line 516

{
	struct dns_reply_storage *rs = env_get_builtin_priv(env,DNS_id);
	struct dns_reply *reply = dns_reply_entry_locate(rs, n);
	if (!reply)
		(
#line 521
	env_throw_bi(env, mfe_failure, "dns_reply_ip", _("no such reply: %ld"),n)
#line 521
);
#line 523
	if (i < 0 || i >= reply->count)
		(
#line 524
	env_throw_bi(env, mfe_range, "dns_reply_ip", _("index out of range: %ld"),i)
#line 524
);
#line 526
	if (reply->type != dns_reply_ip) {
		(
#line 527
	env_throw_bi(env, mfe_failure, "dns_reply_ip", _("can't use dns_reply_ip on non-IPv4 replies"))
#line 527
);
#line 529
	}
	
#line 530
do {
#line 530
  push(env, (STKVAL)(mft_number)(reply->data.ip[i].s_addr));
#line 530
  goto endlab;
#line 530
} while (0);
}
endlab:
#line 532
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 532
	return;
#line 532
}

enum {
	DNS_TYPE_A = 1,
	DNS_TYPE_NS = 2,
	DNS_TYPE_PTR = 12,
	DNS_TYPE_MX = 15,
	DNS_TYPE_TXT = 16,
	DNS_TYPE_AAAA = 28,
};

static char *dns_type_name[] = {
	[DNS_TYPE_A] = "a",
	[DNS_TYPE_NS] = "ns",
	[DNS_TYPE_PTR] = "ptr",
	[DNS_TYPE_MX] = "mx",
	[DNS_TYPE_TXT] = "txt",
	[DNS_TYPE_AAAA] = "aaaa",
};

void
#line 552
bi_dns_query(eval_environ_t env)
#line 552

#line 552

#line 552 "dns.bi"
{
#line 552
	
#line 552

#line 552
        long  type;
#line 552
        char *  domain;
#line 552
        long  sort;
#line 552
        long  resolve;
#line 552
        
#line 552
        long __bi_argcnt;
#line 552
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 552
        get_numeric_arg(env, 1, &type);
#line 552
        get_string_arg(env, 2, &domain);
#line 552
        if (__bi_argcnt > 2)
#line 552
                get_numeric_arg(env, 3, &sort);
#line 552
        if (__bi_argcnt > 3)
#line 552
                get_numeric_arg(env, 4, &resolve);
#line 552
        
#line 552
        adjust_stack(env, __bi_argcnt + 1);
#line 552

#line 552

#line 552
	if (builtin_module_trace(BUILTIN_IDX_dns))
#line 552
		prog_trace(env, "dns_query %lu %s %lu %lu",type, domain, ((__bi_argcnt > 2) ? sort : 0), ((__bi_argcnt > 3) ? resolve : 0));;

{
	struct dns_reply_storage *rs = env_get_builtin_priv(env,DNS_id);
	dns_status dnsstat;
	mf_status stat;
	DNS_REPLY_COUNT ret;
	struct dns_reply *reply;
	int (*cmpfun)(const void *, const void *) = NULL;
	int res = resolve_none;

	ret = dns_reply_entry_alloc(rs, &reply);
		if (!(ret >= 0))
#line 564
		(
#line 564
	env_throw_bi(env, mfe_failure, "dns_query", _("DNS reply table full"))
#line 564
)
#line 566
;

	res = ((__bi_argcnt > 3) ? resolve : _MFL_RESOLVE_NONE);
	if (res == _MFL_RESOLVE_DFL) {
		res = (env_socket_family(env) == AF_INET)
			? resolve_ip4 : resolve_ip6;
	}

	switch (type) {
	case DNS_TYPE_PTR:
		dnsstat = dns_resolve_ipstr(domain, reply);
		cmpfun = cmp_hostname;
		break;

	case DNS_TYPE_A:
		dnsstat = resolve_host(domain, AF_INET, reply, NULL);
		break;

	case DNS_TYPE_AAAA:
		dnsstat = resolve_host(domain, AF_INET6, reply, NULL);
		break;

	case DNS_TYPE_TXT:
		dnsstat = txt_lookup(domain, reply);
		break;

	case DNS_TYPE_MX:
		dnsstat = mx_lookup(domain, res, reply);
		break;

	case DNS_TYPE_NS:
		dnsstat = ns_lookup(domain, res, reply);
		break;

	default:
		dns_reply_entry_release(rs, ret);
		(
#line 602
	env_throw_bi(env, mfe_failure, "dns_query", _("unsupported type: %ld"),type)
#line 602
);
#line 604
	}

	stat = dns_to_mf_status(dnsstat);

	if (!mf_resolved(stat)) {
		dns_reply_entry_release(rs, ret);
		(
#line 610
	env_throw_bi(env, mf_status_to_exception(stat), "dns_query", _("cannot get %s records for %s"),dns_type_name[type],domain)
#line 610
);
#line 613
	}
	if (stat == mf_not_found) {
		dns_reply_entry_release(rs, ret);
		
#line 616
do {
#line 616
  push(env, (STKVAL)(mft_number)(-1));
#line 616
  goto endlab;
#line 616
} while (0);
	}
	if (((__bi_argcnt > 2) ? sort : 0)) {
		if (!cmpfun) {
			switch (reply->type) {
			case dns_reply_str:
				cmpfun = cmp_str;
				break;
			case dns_reply_ip:
				cmpfun = cmp_ip;
				break;
			case dns_reply_ip6:
				cmpfun = cmp_ip6;
				break;
			}
		}
		qsort(reply->data.ptr, reply->count, dns_reply_elsize(reply), cmpfun);
	}
	
#line 634
do {
#line 634
  push(env, (STKVAL)(mft_number)(ret));
#line 634
  goto endlab;
#line 634
} while (0);
}
endlab:
#line 636
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 636
	return;
#line 636
}
#line 1026 "../../src/builtin/snarf.m4"

#line 1026

#line 1026

#line 1026
void
#line 1026
dns_init_builtin(void)
#line 1026
{
#line 1026
	
#line 1026
	#line 29 "dns.bi"
va_builtin_install_ex("primitive_hostname", bi_primitive_hostname, 0, dtype_string, 1, 0, 0|0, dtype_string);
#line 60 "dns.bi"
va_builtin_install_ex("primitive_resolve", bi_primitive_resolve, 0, dtype_string, 3, 2, 0|0, dtype_string, dtype_string, dtype_number);
#line 145 "dns.bi"
va_builtin_install_ex("primitive_hasmx", bi_primitive_hasmx, 0, dtype_number, 1, 0, 0|0, dtype_string);
#line 229 "dns.bi"
va_builtin_install_ex("primitive_ismx", bi_primitive_ismx, 0, dtype_number, 2, 0, 0|0, dtype_string, dtype_string);
#line 305 "dns.bi"
va_builtin_install_ex("relayed", bi_relayed, 0, dtype_number, 1, 0, 0|0, dtype_string);
#line 311 "dns.bi"
va_builtin_install_ex("ptr_validate", bi_ptr_validate, 0, dtype_number, 1, 0, 0|0, dtype_string);
#line 329 "dns.bi"
va_builtin_install_ex("primitive_hasns", bi_primitive_hasns, 0, dtype_number, 1, 0, 0|0, dtype_string);
#line 434 "dns.bi"
DNS_id = builtin_priv_register(dns_reply_storage_alloc, dns_reply_storage_destroy,
#line 434
NULL);
#line 461 "dns.bi"
va_builtin_install_ex("dns_reply_release", bi_dns_reply_release, 0, dtype_unspecified, 1, 0, 0|0, dtype_number);
#line 471 "dns.bi"
va_builtin_install_ex("dns_reply_count", bi_dns_reply_count, 0, dtype_number, 1, 0, 0|0, dtype_number);
#line 489 "dns.bi"
va_builtin_install_ex("dns_reply_string", bi_dns_reply_string, 0, dtype_string, 2, 0, 0|0, dtype_number, dtype_number);
#line 516 "dns.bi"
va_builtin_install_ex("dns_reply_ip", bi_dns_reply_ip, 0, dtype_number, 2, 0, 0|0, dtype_number, dtype_number);
#line 552 "dns.bi"
va_builtin_install_ex("dns_query", bi_dns_query, 0, dtype_number, 4, 2, 0|0, dtype_number, dtype_string, dtype_number, dtype_number);

#line 1026 "../../src/builtin/snarf.m4"
	
#line 1026
}
#line 1026 "../../src/builtin/snarf.m4"

