fix: populate ldns submodule and add autotools to LDNS build stage

- Re-cloned zonemaster-ldns with --recurse-submodules so the bundled
  ldns C library source (including Changelog and configure.ac) is present
- Added autoconf, automake, libtool to Dockerfile.backend ldns-build stage
  so libtoolize + autoreconf can generate ldns/configure during make

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-21 08:33:38 +02:00
parent 8d4eaa1489
commit eaaa8f6a11
541 changed files with 138189 additions and 0 deletions

View File

@@ -0,0 +1,632 @@
#include "config.h"
#include "config_file.h"
#include "server.h"
#include "zones.h"
#include "process.h"
/* global variables set by signals */
static int work = 1;
static int hupped = 0;
/* signal handler for server */
void server_handle_signal(int sig)
{
switch(sig) {
case SIGHUP:
hupped = 1;
work = 0;
break;
case SIGTERM:
case SIGINT:
case SIGQUIT:
work = 0;
break;
default:
printf("unhandled signal %d\n", sig);
break;
}
}
int server_start(const char* config)
{
struct server_info_t* sinfo = (struct server_info_t*)calloc(1,
sizeof(struct server_info_t));
if(!sinfo) {
printf("out of memory\n");
return 0;
}
FD_ZERO(&sinfo->rset);
FD_ZERO(&sinfo->wset);
FD_ZERO(&sinfo->eset);
work = 1;
hupped = 0;
signal(SIGHUP, server_handle_signal);
signal(SIGQUIT, server_handle_signal);
signal(SIGTERM, server_handle_signal);
signal(SIGINT, server_handle_signal);
signal(SIGPIPE, SIG_IGN);
sinfo->zones = zones_create();
sinfo->cfg = config_file_create(config);
config_file_read(sinfo->cfg, config, sinfo->zones);
zones_read(sinfo->zones);
server_bind(sinfo, sinfo->cfg->port);
if(!sinfo->sock_list) {
printf("Could not start server.\n");
server_free(sinfo);
return 0;
}
printf("service for %d zones (%s) on port %d\n",
(int)sinfo->zones->ztree->count, config, sinfo->cfg->port);
printf("Masterdont started pid %d\n", (int)getpid());
while(work) {
server_handle_net(sinfo);
}
server_free(sinfo);
return hupped;
}
void server_handle_net(struct server_info_t *sinfo)
{
struct socket_service* p, **prevp;
int delete_me;
fd_set rset, wset, eset;
rset = sinfo->rset;
wset = sinfo->wset;
eset = sinfo->eset;
if(select(sinfo->maxfd+1, &rset, &wset, &eset, NULL) == -1) {
if(errno==EINTR)
return;
printf("select: %s\n", strerror(errno));
return;
}
p = sinfo->sock_list;
prevp = &sinfo->sock_list;
while(p) {
delete_me = 0;
if(FD_ISSET(p->s, &rset))
handle_read(sinfo, p, &delete_me, sinfo->zones);
if(!delete_me && FD_ISSET(p->s, &wset))
handle_write(sinfo, p, &delete_me);
/* eset ignored */
if(delete_me) {
printf("delete me\n");
FD_CLR(p->s, &sinfo->rset);
FD_CLR(p->s, &sinfo->wset);
FD_CLR(p->s, &sinfo->eset);
if(p->is_tcp)
sinfo->num_tcp --;
/* cannot lower maxfd */
*prevp = p->next; /* snip out of list */
server_service_free(p);
p = *prevp; /* go to next */
} else {
prevp = &p->next;
p = p->next;
}
}
}
void server_free(struct server_info_t* sinfo)
{
struct socket_service* p = sinfo->sock_list, *np=0;
config_file_delete(sinfo->cfg);
zones_free(sinfo->zones);
while(p) {
np = p->next;
server_service_free(p);
p = np;
}
free(sinfo);
}
void server_bind(struct server_info_t* sinfo, int port)
{
struct addrinfo hints;
struct addrinfo *res = 0;
struct addrinfo *p = 0;
int err=0;
char service[16];
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_flags = AI_PASSIVE
#ifdef AI_NUMERICSERV
| AI_NUMERICSERV
#endif
;
sprintf(service, "%d", port);
if((err=getaddrinfo(NULL, service, &hints, &res)) != 0) {
printf("getaddrinfo: %s\n", gai_strerror(err));
return;
}
p = res;
while(p)
{
/* only TCP and UDP */
if(p->ai_protocol == IPPROTO_TCP ||
p->ai_protocol == IPPROTO_UDP) {
struct socket_service* sv;
sv = server_service_create(p);
if(!sv) {
err=errno;
} else {
sv->next = sinfo->sock_list;
sinfo->sock_list = sv;
FD_SET(sv->s, &sinfo->rset);
if(sv->s > sinfo->maxfd)
sinfo->maxfd = sv->s;
}
}
p = p->ai_next;
}
if(!sinfo->sock_list) {
printf("Fatal: No interfaces could be initialized: %s\n",
strerror(err));
}
if(res) freeaddrinfo(res);
}
struct socket_service* server_service_create(struct addrinfo *ai)
{
struct socket_service* svr;
int s;
const int on = 1;
s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if(s == -1) {
return 0;
}
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
goto error;
}
#ifdef IPV6_V6ONLY
if(ai->ai_protocol == AF_INET6 &&
setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
goto error;
}
#endif
if(bind(s, ai->ai_addr, ai->ai_addrlen) == -1) {
goto error;
}
if(ai->ai_socktype == SOCK_STREAM &&
listen(s, TCP_LISTEN_BACKLOG) == -1) {
goto error;
}
/* create service struct */
svr = (struct socket_service*)malloc(sizeof(struct socket_service));
if(!svr) {
errno = ENOMEM;
goto error;
}
memset(svr, 0, sizeof(struct socket_service));
svr->tcp_state = svr_tcp_listen;
svr->s = s;
if(ai->ai_socktype == SOCK_STREAM)
svr->is_tcp = 1;
svr->buffer = ldns_buffer_new(SERVER_BUFFER_SIZE);
if(!svr->buffer) {
errno = ENOMEM;
free(svr);
goto error;
}
return svr;
error:
close(s);
return 0;
}
void server_service_free(struct socket_service* svr)
{
close(svr->s);
if(svr->reply)
ldns_rr_list_deep_free(svr->reply);
if(svr->buffer)
ldns_buffer_free(svr->buffer);
free(svr);
}
void
handle_listen(struct server_info_t *sinfo, struct socket_service* listen_v)
{
struct socket_service* sh;
int newfd;
if(sinfo->num_tcp >= MAX_TCP) {
printf("Error: incoming tcp query, but MAX_TCP reached (%d)\n",
MAX_TCP);
return;
}
if((newfd=accept(listen_v->s, NULL, NULL)) == -1) {
printf("Error tcp accept: %s", strerror(errno));
return;
}
if(fcntl(newfd, F_SETFL, O_NONBLOCK) == -1) {
printf("Error fcntl: %s\n", strerror(errno));
close(newfd);
return;
}
sh = (struct socket_service*)malloc(sizeof(struct socket_service));
if(!sh) {
printf("out of memory\n");
close(newfd);
return;
}
memset(sh, 0, sizeof(struct socket_service));
sh->tcp_state = svr_tcp_read;
sh->s = newfd;
sh->buffer = ldns_buffer_new(SERVER_BUFFER_SIZE);
sh->is_tcp = 1;
if(!sh->buffer) {
printf("out of memory\n");
close(newfd);
free(sh);
return;
}
ldns_buffer_clear(sh->buffer);
if(sh->s > sinfo->maxfd)
sinfo->maxfd = sh->s;
FD_SET(sh->s, &sinfo->rset);
sinfo->num_tcp++;
/* put after the current service */
sh->next = listen_v->next;
listen_v->next = sh;
}
void handle_read(struct server_info_t *sinfo, struct socket_service* sv,
int *del, struct zones_t* zones)
{
printf("handle_read %s %s\n", sv->is_tcp?"tcp":"udp",
sv->tcp_state==svr_tcp_listen?"listen":"");
/* get the data */
if(sv->is_tcp) {
if(sv->tcp_state == svr_tcp_listen) {
handle_listen(sinfo, sv);
return;
}
if(sv->tcp_state != svr_tcp_read) {
FD_CLR(sv->s, &sinfo->rset);
return;
}
/* read some more from channel */
if(!read_tcp_query(sv, del))
return; /* continue later */
} else {
/* udp recv */
if(!read_udp_query(sv))
return;
}
/* handle request */
if(!process_query(sv, zones))
return;
if(!sv->is_tcp) {
/* sendto */
send_udp_answer(sv);
} else {
/* change socket tcp to writing mode */
sv->tcp_state = svr_tcp_write;
sv->bytes_done = 0;
FD_CLR(sv->s, &sinfo->rset);
FD_SET(sv->s, &sinfo->wset);
}
}
void handle_write(struct server_info_t *sinfo, struct socket_service* sv,
int *del)
{
if(sv->is_tcp) {
if(sv->tcp_state != svr_tcp_write) {
FD_CLR(sv->s, &sinfo->wset);
return;
}
write_tcp_answer(sv, del);
} else {
FD_CLR(sv->s, &sinfo->wset);
}
}
int read_tcp_query(struct socket_service *sv, int* del)
{
ssize_t ret;
uint16_t len;
printf("read tcp query %d\n", (int)sv->bytes_done);
if(sv->bytes_done < sizeof(len)) {
ret = read(sv->s, ldns_buffer_current(sv->buffer),
sizeof(len)-ldns_buffer_position(sv->buffer));
printf("read tcp query got %d\n", (int)ret);
if(ret == 0) {
*del = 1;
return 0;
}
if(ret == -1) {
*del = 1;
printf("read: %s\n", strerror(errno));
return 0;
}
ldns_buffer_skip(sv->buffer, ret);
if(ldns_buffer_position(sv->buffer) < sizeof(len))
return 0; /* more later */
len = ldns_buffer_read_u16_at(sv->buffer, 0);
printf("so len is %d\n", len);
ldns_buffer_clear(sv->buffer);
ldns_buffer_set_limit(sv->buffer, len);
sv->bytes_done = sizeof(len);
}
ret = read(sv->s, ldns_buffer_current(sv->buffer),
ldns_buffer_remaining(sv->buffer));
printf("read tcp query content got %d\n", (int)ret);
if(ret == 0) {
*del = 1;
return 0;
}
if(ret == -1) {
*del = 1;
printf("read: %s\n", strerror(errno));
return 0;
}
ldns_buffer_skip(sv->buffer, ret);
if(ldns_buffer_remaining(sv->buffer) > 0) {
return 0;
}
ldns_buffer_flip(sv->buffer);
return 1;
}
int read_udp_query(struct socket_service *sv)
{
ssize_t sz;
ldns_buffer_clear(sv->buffer);
printf("udp read\n");
sv->peerlen = sizeof(sv->peer);
sz = recvfrom(sv->s, ldns_buffer_begin(sv->buffer),
ldns_buffer_capacity(sv->buffer),
0, (struct sockaddr*)&sv->peer, &sv->peerlen);
printf("udp read %d\n", (int)sz);
if(sz == -1) {
printf("recvfrom: %s\n", strerror(errno));
return 0;
}
if(sz == 0)
return 0;
ldns_buffer_skip(sv->buffer, sz);
ldns_buffer_flip(sv->buffer);
return 1;
}
static void set_error(struct socket_service* sv, ldns_pkt_rcode rcode)
{
LDNS_QR_SET(ldns_buffer_begin(sv->buffer));
LDNS_TC_CLR(ldns_buffer_begin(sv->buffer));
LDNS_RCODE_SET(ldns_buffer_begin(sv->buffer), rcode);
}
int process_query(struct socket_service* sv, struct zones_t* zones)
{
ldns_pkt* q = 0, *r = 0;
ldns_status status;
ldns_pkt_rcode rcode = LDNS_RCODE_NOERROR;
/* if QR bit is set drop packet */
if(ldns_buffer_limit(sv->buffer) < LDNS_HEADER_SIZE ||
LDNS_QR_WIRE(ldns_buffer_begin(sv->buffer)))
return 0;
status = ldns_wire2pkt(&q, ldns_buffer_begin(sv->buffer),
ldns_buffer_limit(sv->buffer));
if(status != LDNS_STATUS_OK || !q) {
/* form error */
printf("bad packet: %s\n", ldns_get_errorstr_by_id(status));
if(q)
ldns_pkt_free(q);
set_error(sv, LDNS_RCODE_FORMERR);
return 1;
}
if(1) {
printf("Got query:\n");
ldns_pkt_print(stdout, q);
}
r = ldns_pkt_new();
ldns_pkt_set_id(r, ldns_pkt_id(q));
ldns_pkt_set_qr(r, true);
ldns_pkt_set_aa(r, true);
ldns_pkt_set_rd(r, ldns_pkt_rd(q));
rcode = process_pkts(sv, q, r, zones);
if(rcode != LDNS_RCODE_NOERROR) {
printf("answer error %d\n", rcode);
ldns_pkt_free(q);
ldns_pkt_free(r);
set_error(sv, rcode);
return 1;
}
if(sv->reply)
fill_r_up_pkt(sv, r);
if(1) {
printf("Got answer %s:\n", sv->reply?"(head, more to follow)":"pkt");
ldns_pkt_print(stdout, r);
}
ldns_buffer_clear(sv->buffer);
status = ldns_pkt2buffer_wire(sv->buffer, r);
if(status != LDNS_STATUS_OK) {
printf("could not wireformat pkt: %s\n",
ldns_get_errorstr_by_id(status));
ldns_pkt_free(q);
ldns_pkt_free(r);
return 0;
}
ldns_buffer_flip(sv->buffer);
ldns_pkt_free(q);
ldns_pkt_free(r);
return 1;
}
void send_udp_answer(struct socket_service* sv)
{
ssize_t sz = sendto(sv->s, ldns_buffer_begin(sv->buffer),
ldns_buffer_limit(sv->buffer), 0,
(struct sockaddr*)&sv->peer, sv->peerlen);
if(sz == -1) {
printf("sendto: %s\n", strerror(errno));
return;
}
if(sz < (int)ldns_buffer_limit(sv->buffer)) {
printf("sendto: message not completely sent.\n");
return;
}
}
void write_tcp_answer(struct socket_service* sv, int* del)
{
uint16_t len = htons(ldns_buffer_limit(sv->buffer));
ssize_t ret;
if(sv->bytes_done < sizeof(len)) {
ret = write(sv->s,
((uint8_t*)&len) + ldns_buffer_position(sv->buffer),
sizeof(len)-ldns_buffer_position(sv->buffer));
if(ret == 0) {
*del = 1;
return;
}
if(ret == -1) {
printf("write: %s\n", strerror(errno));
*del = 1;
return;
}
ldns_buffer_skip(sv->buffer, ret);
if(ldns_buffer_position(sv->buffer) < sizeof(len))
return; /* later */
sv->bytes_done = sizeof(len);
ldns_buffer_set_position(sv->buffer, 0);
}
ret = write(sv->s, ldns_buffer_current(sv->buffer),
ldns_buffer_remaining(sv->buffer));
if(ret == 0) {
*del = 1;
return;
}
if(ret == -1) {
printf("write: %s\n", strerror(errno));
*del = 1;
return;
}
ldns_buffer_skip(sv->buffer, ret);
if(ldns_buffer_remaining(sv->buffer) > 0)
return; /* later */
/* finished TCP packet, more? */
if(sv->reply) {
fill_r_up(sv);
/* write that next time */
return;
}
*del = 1;
return;
}
void fill_r_up_pkt(struct socket_service* sv, ldns_pkt* in_here)
{
size_t max = 512; /* max bytes in packet */
size_t now = 0;
size_t i;
size_t added = 0;
if(sv->is_tcp)
max = TCP_PKT_SIZE;
else {
/* EDNS 0 */
}
if(!sv->reply)
return;
now = LDNS_HEADER_SIZE;
for(i=0; i<ldns_pkt_qdcount(in_here); i++)
now += ldns_rr_uncompressed_size(ldns_rr_list_rr(
ldns_pkt_question(in_here), i));
/* add until the max is reached */
for(i=0; i<ldns_rr_list_rr_count(sv->reply); i++)
{
now += ldns_rr_uncompressed_size(ldns_rr_list_rr(
sv->reply, i));
if(now >= max) {
/* TCP: always add one, maybe more */
/* UDP: never cross limit. */
if(!sv->is_tcp || added != 0)
break;
}
ldns_pkt_push_rr(in_here, LDNS_SECTION_ANSWER,
ldns_rr_list_rr(sv->reply, i));
ldns_rr_list_set_rr(sv->reply, NULL, i);
added++;
}
/* fixup the rrlist */
memmove(sv->reply->_rrs, sv->reply->_rrs+added,
sizeof(ldns_rr*)*(sv->reply->_rr_count-added));
sv->reply->_rr_count -= added;
if(ldns_rr_list_rr_count(sv->reply) == 0) {
ldns_rr_list_free(sv->reply);
sv->reply = 0;
}
if(!sv->is_tcp && sv->reply) {
ldns_pkt_set_tc(in_here, true);
ldns_rr_list_deep_free(sv->reply);
sv->reply = 0;
}
}
void fill_r_up(struct socket_service* sv)
{
ldns_pkt* p = ldns_pkt_new();
ldns_status status;
if(!p) {
printf("out of memory\n");
LDNS_RCODE_SET(ldns_buffer_begin(sv->buffer),
LDNS_RCODE_SERVFAIL);
ldns_buffer_set_limit(sv->buffer, LDNS_HEADER_SIZE);
ldns_rr_list_deep_free(sv->reply);
sv->reply = 0;
return;
}
ldns_buffer_clear(sv->buffer);
ldns_pkt_set_id(p, LDNS_ID_WIRE(ldns_buffer_begin(sv->buffer)));
ldns_pkt_set_qr(p, LDNS_QR_WIRE(ldns_buffer_begin(sv->buffer)));
ldns_pkt_set_opcode(p, LDNS_OPCODE_WIRE(ldns_buffer_begin(sv->buffer)));
ldns_pkt_set_aa(p, LDNS_AA_WIRE(ldns_buffer_begin(sv->buffer)));
ldns_pkt_set_rd(p, LDNS_RD_WIRE(ldns_buffer_begin(sv->buffer)));
ldns_pkt_set_ra(p, LDNS_RA_WIRE(ldns_buffer_begin(sv->buffer)));
ldns_pkt_set_rcode(p, LDNS_RCODE_WIRE(ldns_buffer_begin(sv->buffer)));
fill_r_up_pkt(sv, p);
status = ldns_pkt2buffer_wire(sv->buffer, p);
if(status != LDNS_STATUS_OK) {
printf("could not wireformat continuation packet\n");
LDNS_RCODE_SET(ldns_buffer_begin(sv->buffer),
LDNS_RCODE_SERVFAIL);
ldns_buffer_set_limit(sv->buffer, LDNS_HEADER_SIZE);
if(sv->reply) ldns_rr_list_deep_free(sv->reply);
sv->reply = 0;
}
ldns_buffer_flip(sv->buffer);
}