mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
daemon code.
git-svn-id: file:///svn/unbound/trunk@136 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
618ef6acbb
commit
d5de0d10d5
12 changed files with 382 additions and 34 deletions
|
|
@ -57,7 +57,7 @@ UNITTEST_SRC=testcode/unitmain.c $(COMMON_SRC)
|
|||
UNITTEST_OBJ=$(addprefix $(BUILD),$(UNITTEST_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
DAEMON_SRC=$(wildcard daemon/*.c) $(COMMON_SRC)
|
||||
DAEMON_OBJ=$(addprefix $(BUILD),$(DAEMON_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c daemon/worker.c testcode/replay.c testcode/fake_event.c $(filter-out util/netevent.c services/listen_dnsport.c services/outside_network.c, $(COMMON_SRC))
|
||||
TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c daemon/worker.c daemon/daemon.c testcode/replay.c testcode/fake_event.c $(filter-out util/netevent.c services/listen_dnsport.c services/outside_network.c, $(COMMON_SRC))
|
||||
TESTBOUND_OBJ=$(addprefix $(BUILD),$(TESTBOUND_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) $(TESTBOUND_SRC)
|
||||
ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.o) $(addprefix compat/,$(LIBOBJS))) $(COMPAT_OBJ)
|
||||
|
|
|
|||
117
daemon/daemon.c
Normal file
117
daemon/daemon.c
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* daemon/daemon.c - collection of workers that handles requests.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* The daemon consists of global settings and a number of workers.
|
||||
*/
|
||||
|
||||
/** buffer size for network connections */
|
||||
#define BUFSZ 65552
|
||||
|
||||
#include "config.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
|
||||
struct daemon*
|
||||
daemon_init()
|
||||
{
|
||||
struct daemon* daemon = (struct daemon*)calloc(1,
|
||||
sizeof(struct daemon));
|
||||
if(!daemon)
|
||||
return NULL;
|
||||
lock_basic_init(&daemon->lock);
|
||||
daemon->need_to_exit = 0;
|
||||
return daemon;
|
||||
}
|
||||
|
||||
int
|
||||
daemon_open_shared_ports(struct daemon* daemon, struct config_file* cfg)
|
||||
{
|
||||
log_assert(daemon);
|
||||
daemon->cfg = cfg;
|
||||
if(daemon->cfg->port == daemon->listening_port)
|
||||
return 1;
|
||||
listening_ports_free(daemon->ports);
|
||||
if(!(daemon->ports=listening_ports_open(daemon->cfg)))
|
||||
return 0;
|
||||
daemon->listening_port = daemon->cfg->port;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
daemon_fork(struct daemon* daemon)
|
||||
{
|
||||
/* only one thread for now */
|
||||
log_assert(daemon);
|
||||
daemon->num = 1;
|
||||
daemon->workers = (struct worker**)calloc((size_t)daemon->num,
|
||||
sizeof(struct worker*));
|
||||
if(!(daemon->workers[0] = worker_init(daemon->cfg, BUFSZ)))
|
||||
fatal_exit("could not initialize thread # %d", 0);
|
||||
daemon->workers[0]->daemon = daemon;
|
||||
daemon->workers[0]->thread_num = 0;
|
||||
|
||||
log_info("start of service (%s).", PACKAGE_STRING);
|
||||
worker_work(daemon->workers[0]);
|
||||
daemon->need_to_exit = 1;
|
||||
}
|
||||
|
||||
void
|
||||
daemon_cleanup(struct daemon* daemon)
|
||||
{
|
||||
int i;
|
||||
log_assert(daemon);
|
||||
for(i=0; i<daemon->num; i++)
|
||||
worker_delete(daemon->workers[i]);
|
||||
free(daemon->workers);
|
||||
daemon->workers = NULL;
|
||||
daemon->num = 0;
|
||||
daemon->cfg = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
daemon_delete(struct daemon* daemon)
|
||||
{
|
||||
if(!daemon)
|
||||
return;
|
||||
listening_ports_free(daemon->ports);
|
||||
lock_basic_destroy(&daemon->lock);
|
||||
free(daemon);
|
||||
}
|
||||
104
daemon/daemon.h
Normal file
104
daemon/daemon.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* daemon/daemon.h - collection of workers that handles requests.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* The daemon consists of global settings and a number of workers.
|
||||
*/
|
||||
|
||||
#ifndef DAEMON_H
|
||||
#define DAEMON_H
|
||||
|
||||
#include "util/locks.h"
|
||||
struct config_file;
|
||||
struct worker;
|
||||
struct listen_port;
|
||||
|
||||
/**
|
||||
* Structure holding worker list.
|
||||
* Holds globally visible information.
|
||||
*/
|
||||
struct daemon {
|
||||
/** mutex for exclusive access to this structure. */
|
||||
lock_basic_t lock;
|
||||
/** The config settings */
|
||||
struct config_file* cfg;
|
||||
/** port number that has ports opened. */
|
||||
int listening_port;
|
||||
/** listening ports, opened, to be shared by threads */
|
||||
struct listen_port* ports;
|
||||
/** num threads allocated */
|
||||
int num;
|
||||
/** the worker entries */
|
||||
struct worker** workers;
|
||||
/** do we need to exit unbound (or is it only a reload?) */
|
||||
int need_to_exit;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize daemon structure.
|
||||
* @return: The daemon structure, or NULL on error.
|
||||
*/
|
||||
struct daemon* daemon_init();
|
||||
|
||||
/**
|
||||
* Open shared listening ports (if needed).
|
||||
* @param daemon: the daemon.
|
||||
* @param cfg: the cfg settings. Applied to daemon.
|
||||
* @return: false on error.
|
||||
*/
|
||||
int daemon_open_shared_ports(struct daemon* daemon, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Fork workers and start service.
|
||||
* When the routine exits, it is no longer forked.
|
||||
* @param daemon: the daemon.
|
||||
*/
|
||||
void daemon_fork(struct daemon* daemon);
|
||||
|
||||
/**
|
||||
* Close off the worker thread information.
|
||||
* Bring the daemon back into state ready for daemon_fork again.
|
||||
* @param daemon: the daemon.
|
||||
*/
|
||||
void daemon_cleanup(struct daemon* daemon);
|
||||
|
||||
/**
|
||||
* Delete worker.
|
||||
*/
|
||||
void daemon_delete(struct daemon* daemon);
|
||||
|
||||
#endif /* DAEMON_H */
|
||||
108
daemon/unbound.c
108
daemon/unbound.c
|
|
@ -42,11 +42,10 @@
|
|||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "util/config_file.h"
|
||||
|
||||
/** buffer size for network connections */
|
||||
#define BUFSZ 65552
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
|
||||
/** print usage. */
|
||||
static void usage()
|
||||
|
|
@ -61,6 +60,51 @@ static void usage()
|
|||
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
|
||||
}
|
||||
|
||||
/** daemonize, drop user priviliges and chroot if needed */
|
||||
static void
|
||||
do_chroot(struct config_file* cfg)
|
||||
{
|
||||
log_assert(cfg);
|
||||
|
||||
/* daemonize last to be able to print error to user */
|
||||
if(cfg->chrootdir && cfg->chrootdir[0])
|
||||
if(chroot(cfg->chrootdir))
|
||||
fatal_exit("unable to chroot: %s", strerror(errno));
|
||||
if(cfg->username && cfg->username[0]) {
|
||||
struct passwd *pwd;
|
||||
if((pwd = getpwnam(cfg->username)) == NULL)
|
||||
fatal_exit("user '%s' does not exist.", cfg->username);
|
||||
if(setgid(pwd->pw_gid) != 0)
|
||||
fatal_exit("unable to set group id: %s", strerror(errno));
|
||||
if(setuid(pwd->pw_uid) != 0)
|
||||
fatal_exit("unable to set user id: %s", strerror(errno));
|
||||
endpwent();
|
||||
}
|
||||
if(cfg->do_daemonize) {
|
||||
int fd;
|
||||
/* Take off... */
|
||||
switch (fork()) {
|
||||
case 0:
|
||||
break;
|
||||
case -1:
|
||||
fatal_exit("fork failed: %s", strerror(errno));
|
||||
default:
|
||||
/* exit interactive session */
|
||||
exit(0);
|
||||
}
|
||||
/* detach */
|
||||
if(setsid() == -1)
|
||||
fatal_exit("setsid() failed: %s", strerror(errno));
|
||||
if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
|
||||
(void)dup2(fd, STDIN_FILENO);
|
||||
(void)dup2(fd, STDOUT_FILENO);
|
||||
(void)dup2(fd, STDERR_FILENO);
|
||||
if (fd > 2)
|
||||
(void)close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the daemon.
|
||||
* @param cfgfile: the config file name.
|
||||
|
|
@ -69,35 +113,41 @@ static void usage()
|
|||
*/
|
||||
static void run_daemon(const char* cfgfile, int cmdline_verbose)
|
||||
{
|
||||
struct worker* worker = NULL;
|
||||
struct config_file *cfg = NULL;
|
||||
struct config_file* cfg = NULL;
|
||||
struct daemon* daemon = NULL;
|
||||
int done_chroot = 0;
|
||||
|
||||
if(!(cfg = config_create())) {
|
||||
fprintf(stderr, "Could not init config defaults.");
|
||||
exit(1);
|
||||
}
|
||||
if(cfgfile) {
|
||||
if(!config_read(cfg, cfgfile)) {
|
||||
config_delete(cfg);
|
||||
exit(1);
|
||||
}
|
||||
if(!(daemon = daemon_init()))
|
||||
fatal_exit("alloc failure");
|
||||
while(!daemon->need_to_exit) {
|
||||
if(done_chroot)
|
||||
log_info("Restart of %s.", PACKAGE_STRING);
|
||||
else log_info("Start of %s.", PACKAGE_STRING);
|
||||
|
||||
/* config stuff */
|
||||
if(!(cfg = config_create()))
|
||||
fatal_exit("Could not alloc config defaults");
|
||||
if(!config_read(cfg, cfgfile))
|
||||
fatal_exit("Could not read config file: %s", cfgfile);
|
||||
verbosity = cmdline_verbose + cfg->verbosity;
|
||||
|
||||
/* prepare */
|
||||
if(!daemon_open_shared_ports(daemon, cfg))
|
||||
fatal_exit("could not open ports");
|
||||
if(!done_chroot) {
|
||||
do_chroot(cfg);
|
||||
done_chroot = 1;
|
||||
}
|
||||
/* work */
|
||||
daemon_fork(daemon);
|
||||
|
||||
/* clean up for restart */
|
||||
verbose(VERB_ALGO, "cleanup.");
|
||||
daemon_cleanup(daemon);
|
||||
config_delete(cfg);
|
||||
}
|
||||
log_info("Start of %s.", PACKAGE_STRING);
|
||||
|
||||
/* setup */
|
||||
worker = worker_init(cfg, BUFSZ);
|
||||
if(!worker) {
|
||||
fatal_exit("could not initialize");
|
||||
}
|
||||
|
||||
/* drop user priviliges and chroot if needed */
|
||||
log_info("start of service (%s).", PACKAGE_STRING);
|
||||
worker_work(worker);
|
||||
|
||||
/* cleanup */
|
||||
verbose(VERB_ALGO, "Exit cleanup.");
|
||||
worker_delete(worker);
|
||||
daemon_delete(daemon);
|
||||
}
|
||||
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
|
|
|
|||
|
|
@ -281,6 +281,7 @@ worker_delete(struct worker* worker)
|
|||
outside_network_delete(worker->back);
|
||||
comm_signal_delete(worker->comsig);
|
||||
comm_base_delete(worker->base);
|
||||
free(worker->rndstate);
|
||||
free(worker);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,9 +45,11 @@
|
|||
|
||||
#include "config.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/locks.h"
|
||||
struct listen_dnsport;
|
||||
struct outside_network;
|
||||
struct config_file;
|
||||
struct daemon;
|
||||
|
||||
/** size of table used for random numbers. large to be more secure. */
|
||||
#define RND_STATE_SIZE 256
|
||||
|
|
@ -57,6 +59,10 @@ struct config_file;
|
|||
* Holds globally visible information.
|
||||
*/
|
||||
struct worker {
|
||||
/** global shared daemon structure */
|
||||
struct daemon* daemon;
|
||||
/** the thread number (in daemon array). */
|
||||
int thread_num;
|
||||
/** the event base this worker works with */
|
||||
struct comm_base* base;
|
||||
/** the frontside listening interface where request events come in */
|
||||
|
|
|
|||
|
|
@ -391,3 +391,14 @@ listen_delete(struct listen_dnsport* front)
|
|||
ldns_buffer_free(front->udp_buff);
|
||||
free(front);
|
||||
}
|
||||
|
||||
struct listen_port*
|
||||
listening_ports_open(struct config_file* cfg)
|
||||
{
|
||||
return calloc(1,1);
|
||||
}
|
||||
|
||||
void listening_ports_free(struct listen_port* list)
|
||||
{
|
||||
free(list);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
#include "util/netevent.h"
|
||||
struct listen_list;
|
||||
struct addrinfo;
|
||||
struct config_file;
|
||||
|
||||
/**
|
||||
* Listening for queries structure.
|
||||
|
|
@ -73,6 +74,31 @@ struct listen_list {
|
|||
struct comm_point* com;
|
||||
};
|
||||
|
||||
/**
|
||||
* Single linked list to store shared ports that have been
|
||||
* opened for use by all threads.
|
||||
*/
|
||||
struct listen_port {
|
||||
/** next in list */
|
||||
struct listen_port* next;
|
||||
/** file descriptor, open and ready for use */
|
||||
int fd;
|
||||
/** type of file descriptor, udp or tcp */
|
||||
int is_udp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create shared listening ports
|
||||
* @param cfg: settings on what ports to open.
|
||||
* @return: linked list of ports or NULL on error.
|
||||
*/
|
||||
struct listen_port* listening_ports_open(struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Close and delete the (list of) listening ports.
|
||||
*/
|
||||
void listening_ports_free(struct listen_port* list);
|
||||
|
||||
/**
|
||||
* Getaddrinfo, create socket, bind and listen to zero or more
|
||||
* interfaces for IP4 and/or IP6, for UDP and/or TCP.
|
||||
|
|
|
|||
|
|
@ -690,4 +690,15 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
|||
runtime->pending_list = pend;
|
||||
}
|
||||
|
||||
struct listen_port* listening_ports_open(struct config_file* ATTR_UNUSED(cfg))
|
||||
{
|
||||
return calloc(1, 1);
|
||||
}
|
||||
|
||||
void listening_ports_free(struct listen_port* list)
|
||||
{
|
||||
free(list);
|
||||
}
|
||||
|
||||
|
||||
/*********** End of Dummy routines ***********/
|
||||
|
|
|
|||
|
|
@ -77,12 +77,20 @@ config_create()
|
|||
cfg->do_tcp = 1;
|
||||
cfg->outgoing_base_port = cfg->port + 1000;
|
||||
cfg->outgoing_num_ports = 16;
|
||||
cfg->fwd_address = strdup("");
|
||||
if(!cfg->fwd_address) {
|
||||
free(cfg);
|
||||
if(!(cfg->fwd_address = strdup(""))) {
|
||||
config_delete(cfg);
|
||||
return NULL;
|
||||
}
|
||||
if(!(cfg->username = strdup(""))) {
|
||||
config_delete(cfg);
|
||||
return NULL;
|
||||
}
|
||||
if(!(cfg->chrootdir = strdup(""))) {
|
||||
config_delete(cfg);
|
||||
return NULL;
|
||||
}
|
||||
cfg->fwd_port = UNBOUND_DNS_PORT;
|
||||
cfg->do_daemonize = 0;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +109,10 @@ create_cfg_parser(struct config_file* cfg, char* filename)
|
|||
int
|
||||
config_read(struct config_file* cfg, const char* filename)
|
||||
{
|
||||
FILE *in = fopen(filename, "r");
|
||||
FILE *in;
|
||||
if(!filename)
|
||||
return 1;
|
||||
in = fopen(filename, "r");
|
||||
if(!in) {
|
||||
log_err("Could not open %s: %s", filename, strerror(errno));
|
||||
return 0;
|
||||
|
|
@ -124,6 +135,8 @@ config_delete(struct config_file* cfg)
|
|||
{
|
||||
if(!cfg) return;
|
||||
free(cfg->fwd_address);
|
||||
free(cfg->username);
|
||||
free(cfg->chrootdir);
|
||||
free(cfg);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,14 @@ struct config_file {
|
|||
char* fwd_address;
|
||||
/** forwarder port */
|
||||
int fwd_port;
|
||||
|
||||
/** chrootdir, if not "" or chroot will be done */
|
||||
char* chrootdir;
|
||||
/** username to change to, if not "". */
|
||||
char* username;
|
||||
|
||||
/** daemonize, i.e. fork into the background. */
|
||||
int do_daemonize;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -84,7 +92,7 @@ struct config_file* config_create();
|
|||
/**
|
||||
* Read the config file from the specified filename.
|
||||
* @param config: where options are stored into, must be freshly created.
|
||||
* @param filename: name of configfile.
|
||||
* @param filename: name of configfile. If NULL nothing is done.
|
||||
* @return: false on error.
|
||||
*/
|
||||
int config_read(struct config_file* config, const char* filename);
|
||||
|
|
|
|||
|
|
@ -676,6 +676,7 @@ comm_point_delete(struct comm_point* c)
|
|||
comm_point_delete(c->tcp_handlers[i]);
|
||||
free(c->tcp_handlers);
|
||||
}
|
||||
free(c->timeout);
|
||||
if(c->type == comm_tcp)
|
||||
ldns_buffer_free(c->buffer);
|
||||
free(c->ev);
|
||||
|
|
|
|||
Loading…
Reference in a new issue