ban2fail/pdns.c

539 lines
15 KiB
C
Raw Normal View History

/***************************************************************************
* Copyright (C) 2019 by John D. Robertson *
* john@rrci.com *
* *
* 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 of the License, 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 *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
2019-11-30 19:14:42 +00:00
#define _GNU_SOURCE
2019-12-01 02:30:17 +00:00
#include <arpa/inet.h>
#include <assert.h>
#include <limits.h>
#include <signal.h>
2020-09-15 15:31:27 +00:00
#include "es.h"
#include "ez_libc.h"
2019-12-05 16:00:39 +00:00
#include "ez_libpthread.h"
#include "msgqueue.h"
#include "pdns.h"
#include "util.h"
enum vsignals {
2019-11-30 19:14:42 +00:00
/* All vsigs before this are used to indicate worker ready to join */
2019-11-30 14:35:29 +00:00
EXIT_VSIG= PDNS_MAX_THREADS,
CHECK_INBOX_VSIG
};
2019-11-30 19:14:42 +00:00
enum lookupType {
FWD_LOOKUP,
REV_LOOKUP
};
/* Messages in the mgr inbox look like this */
struct mgrMsg {
2019-12-02 03:29:32 +00:00
OFFENTRY *e;
2019-11-30 19:14:42 +00:00
unsigned worker_ndx;
};
/* Messages in the worker inbox look like this */
struct workerMsg {
2019-12-02 03:29:32 +00:00
OFFENTRY *e;
2019-11-30 19:14:42 +00:00
};
/*============================================================*/
/*=========== Forward declarations ===========================*/
/*============================================================*/
2019-11-30 19:14:42 +00:00
static int mgr_check_inbox_f(void *data, int signo);
static int join_f(void *data, int signo);
2019-12-01 11:55:58 +00:00
static unsigned nThreads_joined(void);
static int shutdown_f(void *data);
2019-11-30 19:14:42 +00:00
static void stop_remaining_workers(void);
static int timeout_f(void *data);
2019-12-01 11:55:58 +00:00
static int worker_check_inbox_f(void *vp_ndx, int signo);
static void* worker_main (void *data);
static int worker_exit_f(void *data, int signo);
/*============================================================*/
/*=========== Static data ====================================*/
/*============================================================*/
static struct {
2019-12-01 11:55:58 +00:00
volatile enum {
2019-11-30 19:14:42 +00:00
EXIT_FLG= 1<<0,
ORPHAN_FLG= 1<<1
} flags;
2019-11-30 14:35:29 +00:00
int64_t start_ms;
int timeoutKey,
2019-11-30 14:35:29 +00:00
shutdownKey,
inboxKey,
2019-11-30 14:35:29 +00:00
joinKeyArr[PDNS_MAX_THREADS];
pthread_t tid;
MSGQUEUE inbox;
2019-12-02 03:29:32 +00:00
OFFENTRY **lePtrArr;
2019-11-30 19:14:42 +00:00
unsigned processedNdx,
2019-11-30 14:35:29 +00:00
nThreads,
nItems;
2019-11-30 19:14:42 +00:00
/* One of these for each worker thread */
struct worker {
2019-12-01 11:55:58 +00:00
volatile int is_joined;
pthread_t tid;
MSGQUEUE inbox;
2019-11-30 19:14:42 +00:00
} workerArr[PDNS_MAX_THREADS];
2019-12-01 02:30:17 +00:00
#ifdef DEBUG
pthread_mutex_t prt_mtx;
#endif
2019-12-01 02:30:17 +00:00
} S= {
#ifdef DEBUG
.prt_mtx= PTHREAD_MUTEX_INITIALIZER
#endif
};
/*============================================================*/
/*=========== PDNS ===========================================*/
/*============================================================*/
int
2019-12-02 03:29:32 +00:00
PDNS_lookup(OFFENTRY *lePtrArr[], unsigned nItems, unsigned timeout_ms)
/**************************************************************
2019-12-02 03:29:32 +00:00
* Perform parallel DNS reverse lookups on all OFFENTRY objects
2019-11-30 14:35:29 +00:00
* referenced in lePtrArr.
*/
{
int rtn= -1;
2019-11-30 19:14:42 +00:00
/* Check for trivial case */
2019-11-30 14:35:29 +00:00
if(!nItems)
return 0;
/* Note when we start */
S.start_ms= clock_gettime_ms(CLOCK_REALTIME);
/* Publish our thread ID */
S.tid= pthread_self();
/* Prepare our inbox */
2019-11-30 19:14:42 +00:00
MSGQUEUE_constructor(&S.inbox, sizeof(struct mgrMsg), PDNS_MGR_INBOX_SZ);
/* Stash this where it's easy to get to */
S.nItems= nItems;
2019-11-30 14:35:29 +00:00
S.nThreads= MIN(nItems, PDNS_MAX_THREADS);
S.lePtrArr= lePtrArr;
/* Register a countdown timer to know when to stop */
S.timeoutKey= ez_ES_registerTimer(timeout_ms, 0, timeout_f, NULL);
/* Check inbox on CHECK_INBOX_VSIG */
2019-11-30 19:14:42 +00:00
S.inboxKey= ez_ES_registerVSignal(CHECK_INBOX_VSIG, mgr_check_inbox_f, NULL);
2019-11-30 19:14:42 +00:00
/* Start worker threads */
2019-11-30 14:35:29 +00:00
for(unsigned i= 0; i < S.nThreads; ++i) {
2019-11-30 19:14:42 +00:00
struct worker *wrk= S.workerArr + i;
/* Register the join handler on vsig= array index */
2019-11-30 14:35:29 +00:00
S.joinKeyArr[i]= ez_ES_registerVSignal(i, join_f, NULL);
2019-11-30 19:14:42 +00:00
/* Pass the worker's array index in to worker_main() */
wrk->tid= ES_spawn_thread(worker_main, (void*)(long unsigned)i);
2019-11-30 14:35:29 +00:00
}
2019-11-30 19:14:42 +00:00
/* Give worker threads something to do */
for(; S.processedNdx < S.nThreads; ++S.processedNdx) {
2019-11-30 14:35:29 +00:00
2019-11-30 19:14:42 +00:00
struct worker *wrk= S.workerArr + S.processedNdx;
struct workerMsg worker_msg= {.e= S.lePtrArr[S.processedNdx]};
2019-11-30 19:14:42 +00:00
/* Give the worker something to do */
ez_MSGQUEUE_submitTypedMsg(&wrk->inbox, worker_msg);
/* Prompt worker to check inbox */
ES_VSignal(wrk->tid, CHECK_INBOX_VSIG);
}
/* Wait for something to happen */
ES_run();
/* Unregister signal handlers for this thread */
if(S.timeoutKey)
ez_ES_unregister(S.timeoutKey);
2019-11-30 14:35:29 +00:00
if(S.shutdownKey)
ez_ES_unregister(S.shutdownKey);
ez_ES_unregister(S.inboxKey);
/* Release all the join registrations */
2019-11-30 14:35:29 +00:00
for(unsigned i= 0; i < S.nThreads; ++i) {
ez_ES_unregister(S.joinKeyArr[i]);
}
2019-11-30 19:14:42 +00:00
rtn= S.processedNdx;
abort:
return rtn;
}
static int
2019-11-30 19:14:42 +00:00
mgr_check_inbox_f(void *data, int signo)
2019-12-01 11:55:58 +00:00
/**************************************************************************
* Manager was prompted by a worker to check the inbox and see which worker is
* ready for another task.
*/
{
int rtn= -1;
2019-11-30 19:14:42 +00:00
struct mgrMsg msg;
while(EOF != MSGQUEUE_extractTypedMsg(&S.inbox, msg)) {
/* Get pointer to worker */
struct worker *wrk= S.workerArr + msg.worker_ndx;
struct workerMsg worker_msg= {.e= NULL};
if(msg.e->dns.flags & PDNS_DONE_MASK) {
2019-11-30 19:14:42 +00:00
/* If we've finished up, start pruning worker threads */
if(S.processedNdx == S.nItems) {
pthread_kill(wrk->tid, SIGTERM);
continue;
}
2019-11-30 19:14:42 +00:00
worker_msg.e= S.lePtrArr[S.processedNdx];
++S.processedNdx;
} else {
/* Perform forward lookup next */
worker_msg.e= msg.e;
}
2019-11-30 19:14:42 +00:00
/* Give worker another task */
ez_MSGQUEUE_submitTypedMsg(&wrk->inbox, worker_msg);
ES_VSignal(wrk->tid, CHECK_INBOX_VSIG);
}
rtn= 0;
abort:
return rtn;
}
2019-11-30 14:35:29 +00:00
static unsigned
nThreads_joined(void)
/*********************************************************
* Return the number of threads which have already joined.
*/
{
unsigned rtn= 0;
for(unsigned i= 0; i < S.nThreads; ++i) {
2019-11-30 19:14:42 +00:00
struct worker *wrk= S.workerArr + i;
if(!wrk->is_joined) continue;
2019-11-30 14:35:29 +00:00
++rtn;
}
return rtn;
}
static int
join_f(void *data, int signo)
/*********************************************************
2019-11-30 19:14:42 +00:00
* Worker prompted us to join
*/
{
2019-11-30 19:14:42 +00:00
struct worker *wrk= S.workerArr + signo;
void *pRtn;
2019-12-05 16:00:39 +00:00
ez_pthread_join(wrk->tid, &pRtn);
2019-11-30 14:35:29 +00:00
2019-11-30 19:14:42 +00:00
wrk->is_joined= 1;
/* This will naturally terminate when we are done.*/
2019-11-30 14:35:29 +00:00
return S.nThreads == nThreads_joined() ? -1 : 0;
}
static void
2019-11-30 19:14:42 +00:00
stop_remaining_workers(void)
/*********************************************************
2019-11-30 19:14:42 +00:00
* Signal any remaining workers to stop.
*/
{
2019-11-30 19:14:42 +00:00
/* Tell all remaining worker threads to exit now */
unsigned i;
2019-11-30 14:35:29 +00:00
for(i= 0; i < S.nThreads; ++i) {
2019-11-30 19:14:42 +00:00
struct worker *wrk= S.workerArr + i;
/* If it has already joined, skip it */
2019-11-30 19:14:42 +00:00
if(wrk->is_joined) continue;
2019-11-30 19:14:42 +00:00
/* Prompt worker to shut down now */
pthread_kill(wrk->tid, SIGTERM);
}
}
static int
timeout_f(void *data)
/*********************************************************
* Countdown timer has expired.
*/
{
/* Note that the countdown timer fired */
S.timeoutKey= 0;
/* Post notice that it is time to shut down */
S.flags |= EXIT_FLG;
2019-11-30 19:14:42 +00:00
#ifdef DEBUG
eprintf("Timed out with %u threads remaining", S.nThreads - nThreads_joined());
#endif
2019-11-30 19:14:42 +00:00
stop_remaining_workers();
2019-11-30 14:35:29 +00:00
/* Register a countdown timer to know when to forcefully
* stop remaining threads.
*/
S.shutdownKey= ez_ES_registerTimer(PDNS_SHUTDOWN_PAUSE_MS, 0, shutdown_f, NULL);
return 0;
}
2019-11-30 14:35:29 +00:00
static int
shutdown_f(void *data)
/*********************************************************
* Terminate any remaining threads.
*/
{
S.shutdownKey= 0;
2019-11-30 19:14:42 +00:00
#ifdef DEBUG
eprintf("WTF: %u threads *still* remain!", S.nThreads - nThreads_joined());
#endif
/* Let workerren know not to signal for a join */
S.flags |= ORPHAN_FLG;
2019-11-30 14:35:29 +00:00
return -1;
}
/*============================================================*/
2019-12-01 18:04:41 +00:00
/*================= Worker threads ===========================*/
/*============================================================*/
static void*
2019-11-30 19:14:42 +00:00
worker_main (void *vp_ndx)
/*********************************************************
2019-11-30 19:14:42 +00:00
* Workers begin execution here.
*/
{
unsigned ndx= (long unsigned)vp_ndx;
2019-11-30 19:14:42 +00:00
struct worker *self= S.workerArr + ndx;
2019-11-30 19:14:42 +00:00
/* Prepare worker's static data */
MSGQUEUE_constructor(&self->inbox, sizeof(struct workerMsg), PDNS_WORKER_INBOX_SZ);
/* Register to exit when prompted */
2019-11-30 19:14:42 +00:00
ez_ES_registerSignal(SIGTERM, worker_exit_f, NULL);
/* Register to check inbox when prompted */
2019-11-30 19:14:42 +00:00
ez_ES_registerVSignal(CHECK_INBOX_VSIG, worker_check_inbox_f, vp_ndx);
/* Parent has been blocked waiting for this call */
ES_release_parent();
2019-11-30 19:14:42 +00:00
/* Respond to directives from mgr */
ES_run();
2019-11-30 19:14:42 +00:00
#ifdef qqDEBUG
int64_t ms= clock_gettime_ms(CLOCK_REALTIME) - S.start_ms;
eprintf("thread %u exiting at %f seconds", ndx, (double)ms/1000.);
#endif
2019-11-30 14:35:29 +00:00
2019-11-30 19:14:42 +00:00
/* Parent thread may have moved on. In that case, don't join. */
if(!(S.flags & ORPHAN_FLG))
/* Let the main thread know we are ready to join */
ES_VSignal(S.tid, ndx);
/* Free resources for this thread */
// JDR Wed 04 Dec 2019 11:52:15 AM EST
// This causes a double free() error, so let it leak for now.
ES_cleanup();
return NULL;
}
static int
2019-11-30 19:14:42 +00:00
worker_check_inbox_f(void *vp_ndx, int signo)
/*********************************************************
2019-11-30 19:14:42 +00:00
* Worker was prompted to check the inbox for tasks.
*/
{
int rtn= -1;
2019-12-01 18:04:41 +00:00
/* Our S.workerArr index was passed in as (void*) */
unsigned ndx= (long unsigned)vp_ndx;
2019-11-30 19:14:42 +00:00
struct worker *self= S.workerArr + ndx;
struct workerMsg msg;
while(!(S.flags & EXIT_FLG) &&
2019-11-30 19:14:42 +00:00
EOF != MSGQUEUE_extractTypedMsg(&self->inbox, msg))
{
2019-11-30 19:14:42 +00:00
assert(msg.e);
2019-11-30 14:35:29 +00:00
int64_t ms= clock_gettime_ms(CLOCK_REALTIME) - S.start_ms;
2019-11-30 19:14:42 +00:00
2019-12-01 02:30:17 +00:00
/* Check to see if we've finished the reverse DNS lookup */
2019-12-04 14:11:39 +00:00
if(!(msg.e->dns.flags & PDNS_REV_DNS_FLG)) {
2019-11-30 19:14:42 +00:00
const static struct addrinfo hints= {
2019-12-04 14:11:39 +00:00
.ai_flags = AI_NUMERICHOST, /* doing reverse lookups */
.ai_family = AF_UNSPEC, /* Allow IPv4 or IPv6 */
2019-12-01 02:30:17 +00:00
.ai_socktype= SOCK_DGRAM,
.ai_protocol= IPPROTO_UDP
2019-11-30 19:14:42 +00:00
};
2019-12-04 14:11:39 +00:00
/* Place to which getnameinfo can copy result */
char hostBuf[PATH_MAX];
2019-11-30 19:14:42 +00:00
/* Get a populated addrinfo object */
struct addrinfo *res= NULL;
2019-12-04 14:11:39 +00:00
int rc= ez_getaddrinfo(msg.e->addr, NULL, &hints, &res);
assert(0 == rc);
assert(res && res->ai_addr && res->ai_addrlen);
/* Now do blocking reverse lookup */
rc= ez_getnameinfo(res->ai_addr, res->ai_addrlen, hostBuf, sizeof(hostBuf)-1, NULL, 0, NI_NAMEREQD);
2019-11-30 19:14:42 +00:00
2019-12-01 02:30:17 +00:00
#ifdef qqDEBUG
2019-12-04 14:11:39 +00:00
if(!strcmp(msg.e->addr, "113.183.137.246")) {
2019-12-05 16:00:39 +00:00
ez_pthread_mutex_lock(&S.prt_mtx);
2019-12-04 14:11:39 +00:00
ez_fprintf(stderr, "rc= %d, %s ----------------------------------\n", rc, msg.e->addr);
2019-12-01 18:04:41 +00:00
addrinfo_print(res, stderr);
2019-12-01 02:30:17 +00:00
fflush(stderr);
2019-12-05 16:00:39 +00:00
ez_pthread_mutex_unlock(&S.prt_mtx);
2019-12-01 02:30:17 +00:00
}
#endif
2019-12-04 14:11:39 +00:00
if(res) freeaddrinfo(res);
2019-11-30 19:14:42 +00:00
switch(rc) {
case 0:
2019-12-04 14:11:39 +00:00
msg.e->dns.name= strdup(hostBuf);
msg.e->dns.flags |= PDNS_REV_DNS_FLG;
2019-11-30 19:14:42 +00:00
break;
case EAI_NONAME:
2019-12-04 14:11:39 +00:00
msg.e->dns.flags |= PDNS_NXDOMAIN_FLG;
2019-11-30 19:14:42 +00:00
break;
case EAI_AGAIN:
2019-12-04 14:11:39 +00:00
msg.e->dns.flags |= PDNS_SERVFAIL_FLG;
2019-11-30 19:14:42 +00:00
break;
default:
2019-12-04 14:11:39 +00:00
eprintf("FATAL: getnameinfo() returned %d", rc);
abort();
2019-11-30 19:14:42 +00:00
}
2019-12-01 02:30:17 +00:00
2019-11-30 19:14:42 +00:00
} else { /* reverse lookup */
2019-12-04 14:11:39 +00:00
2019-11-30 19:14:42 +00:00
const static struct addrinfo hints= {
2019-12-04 14:11:39 +00:00
.ai_family= AF_UNSPEC, /* Allow IPv4 or IPv6 */
2019-12-01 02:30:17 +00:00
.ai_socktype= SOCK_DGRAM,
.ai_protocol= IPPROTO_UDP
2019-11-30 19:14:42 +00:00
};
/* Get a populated addrinfo object */
struct addrinfo *res= NULL;
2019-12-04 14:11:39 +00:00
int rc= ez_getaddrinfo(msg.e->dns.name, NULL, &hints, &res);
2019-12-01 02:30:17 +00:00
#ifdef qqDEBUG
2019-12-04 14:11:39 +00:00
if(!strcmp(msg.e->addr, "113.183.137.246")) {
2019-12-05 16:00:39 +00:00
ez_pthread_mutex_lock(&S.prt_mtx);
2019-12-04 14:11:39 +00:00
ez_fprintf(stderr, "rc= %d, %s (%s) ----------------------------------\n", rc, msg.e->addr, msg.e->dns.name);
2019-12-01 18:04:41 +00:00
addrinfo_print(res, stderr);
2019-12-01 02:30:17 +00:00
fflush(stderr);
2019-12-05 16:00:39 +00:00
ez_pthread_mutex_unlock(&S.prt_mtx);
2019-12-01 02:30:17 +00:00
}
#endif
2019-11-30 19:14:42 +00:00
switch(rc) {
case 0:
2019-12-04 14:11:39 +00:00
if(!addrinfo_is_match(res, msg.e->addr))
msg.e->dns.flags |= PDNS_FWD_MISMATCH_FLG;
#ifdef qqDEBUG
if(!strcmp(msg.e->addr, "113.183.137.246")) {
eprintf( "113.183.137.246 %s"
, msg.e->dns.flags & PDNS_FWD_MISMATCH_FLG ? "Mismatched" : "matched"
);
}
#endif
2019-11-30 19:14:42 +00:00
break;
case EAI_NONAME:
2019-12-04 14:11:39 +00:00
msg.e->dns.flags |= PDNS_FWD_NONE_FLG;
2019-11-30 19:14:42 +00:00
break;
2019-12-04 14:11:39 +00:00
case EAI_FAIL:
case EAI_NODATA:
2019-11-30 19:14:42 +00:00
case EAI_AGAIN:
2019-12-04 14:11:39 +00:00
msg.e->dns.flags |= PDNS_FWD_FAIL_FLG;
2019-11-30 19:14:42 +00:00
break;
default:
2019-12-04 14:11:39 +00:00
eprintf("rc= %d", rc);
assert(0);
2019-11-30 19:14:42 +00:00
}
2019-12-01 02:30:17 +00:00
2019-12-04 14:11:39 +00:00
/* In any case, we are done */
msg.e->dns.flags |= PDNS_FWD_DNS_FLG;
if(res) freeaddrinfo(res);
}
2019-11-30 19:14:42 +00:00
/* Catch being bumped out of blocking call by signal */
if(S.flags & EXIT_FLG) break;
}
2019-11-30 19:14:42 +00:00
/* Only do follow up if we are not exiting */
if(!(S.flags & EXIT_FLG)) {
2019-11-30 19:14:42 +00:00
struct mgrMsg mgr_msg= {.e= msg.e, .worker_ndx= ndx};
/* Submit the worker's message to main mgr
* thread's inbox to indicate we are ready for
* more.
*/
ez_MSGQUEUE_submitTypedMsg(&S.inbox, mgr_msg);
ES_VSignal(S.tid, CHECK_INBOX_VSIG);
rtn= 0;
}
abort:
return rtn;
}
static int
2019-11-30 19:14:42 +00:00
worker_exit_f(void *vp_ndx, int signo)
/**************************************************************************
* Worker was prompted to exit now, so return -1 causing worker_main() return
* from ES_run().
*/
{
return -1;
}
2019-12-01 02:30:17 +00:00