mirror of https://github.com/oxen-io/lokinet
move hook out of dnsd into daemon, made hook use timer, decode_answer has beginning SOA parsing support, getDNSstring() refactor
This commit is contained in:
parent
82f300523d
commit
52bbf890ef
105
daemon/dns.cpp
105
daemon/dns.cpp
|
@ -27,17 +27,85 @@ handle_signal(int sig)
|
|||
done = true;
|
||||
}
|
||||
|
||||
sockaddr *
|
||||
hookChecker(std::string name, struct dnsd_context *context)
|
||||
{
|
||||
llarp::LogInfo("Hooked ", name);
|
||||
// cast your context->user;
|
||||
return nullptr;
|
||||
std::string const default_chars =
|
||||
"abcdefghijklmnaoqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
|
||||
#include <random>
|
||||
|
||||
std::string random_string(size_t len = 15, std::string const &allowed_chars = default_chars) {
|
||||
std::mt19937_64 gen { std::random_device()() };
|
||||
|
||||
std::uniform_int_distribution<size_t> dist { 0, allowed_chars.length()-1 };
|
||||
|
||||
std::string ret;
|
||||
|
||||
std::generate_n(std::back_inserter(ret), len, [&] { return allowed_chars[dist(gen)]; });
|
||||
return ret;
|
||||
}
|
||||
|
||||
// FIXME: make configurable
|
||||
#define SERVER "8.8.8.8"
|
||||
#define PORT 53
|
||||
/*
|
||||
/// check_online_request hook definition
|
||||
typedef void (*check_query_request_hook_func)(struct check_query_request *);
|
||||
|
||||
struct check_query_request
|
||||
{
|
||||
bool done;
|
||||
///hook
|
||||
check_query_request_hook_func hook;
|
||||
};
|
||||
|
||||
void
|
||||
llarp_dnsd_checkQuery_resolved(struct check_query_request *request)
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
struct check_query_simple_request
|
||||
{
|
||||
const struct sockaddr *from;
|
||||
dnsd_question_request *request;
|
||||
};
|
||||
|
||||
void
|
||||
llarp_dnsd_checkQuery(void *u, uint64_t orig, uint64_t left)
|
||||
{
|
||||
if(left)
|
||||
return;
|
||||
//struct check_query_request *request = static_cast< struct check_query_request * >(u);
|
||||
struct check_query_simple_request *qr = static_cast< struct check_query_simple_request * >(u);
|
||||
llarp::LogInfo("Sending cname to delay");
|
||||
writecname_dnss_response(random_string(32, "abcdefghijklmnopqrstuvwxyz")+"bob.loki", qr->from, qr->request);
|
||||
delete qr;
|
||||
}
|
||||
|
||||
dnsd_query_hook_response *
|
||||
hookChecker(std::string name, const struct sockaddr *from,
|
||||
struct dnsd_question_request *request)
|
||||
{
|
||||
dnsd_query_hook_response *response = new dnsd_query_hook_response;
|
||||
response->dontLookUp = false;
|
||||
response->dontSendResponse = false;
|
||||
response->returnThis = nullptr;
|
||||
llarp::LogInfo("Hooked ", name);
|
||||
std::string lName = name;
|
||||
std::transform(lName.begin(), lName.end(), lName.begin(), ::tolower);
|
||||
// FIXME: probably should just read the last 5 bytes
|
||||
if (lName.find(".loki") != std::string::npos)
|
||||
{
|
||||
llarp::LogInfo("Detect Loki Lookup");
|
||||
//check_query_request *query_request = new check_query_request;
|
||||
//query_request->hook = &llarp_dnsd_checkQuery_resolved;
|
||||
check_query_simple_request *qr = new check_query_simple_request;
|
||||
qr->from = from;
|
||||
qr->request = request;
|
||||
// nslookup on osx is about 5 sec before a retry
|
||||
llarp_logic_call_later(request->context->logic,
|
||||
{5000, qr, &llarp_dnsd_checkQuery});
|
||||
response->dontSendResponse = true;
|
||||
}
|
||||
// cast your context->user;
|
||||
return response;
|
||||
}
|
||||
|
||||
struct dns_relay_config
|
||||
{
|
||||
|
@ -92,6 +160,8 @@ main(int argc, char *argv[])
|
|||
iter.visit = &dns_iter_config;
|
||||
llarp_config_iter(config_reader, &iter);
|
||||
llarp::LogInfo("config [", conffname, "] loaded");
|
||||
|
||||
const uint16_t server_port = 1053;
|
||||
|
||||
// llarp::SetLogLevel(llarp::eLogDebug);
|
||||
|
||||
|
@ -101,12 +171,14 @@ main(int argc, char *argv[])
|
|||
llarp_ev_loop *netloop = nullptr;
|
||||
llarp_threadpool *worker = nullptr;
|
||||
llarp_logic *logic = nullptr;
|
||||
|
||||
llarp_ev_loop_alloc(&netloop);
|
||||
|
||||
llarp_ev_loop_alloc(&netloop); // set up netio worker
|
||||
worker = llarp_init_same_process_threadpool();
|
||||
logic = llarp_init_single_process_logic(worker); // set up logic worker
|
||||
|
||||
// configure main netloop
|
||||
struct dnsd_context dnsd;
|
||||
if(!llarp_dnsd_init(&dnsd, netloop, "*", 1053,
|
||||
if(!llarp_dnsd_init(&dnsd, netloop, logic, "*", server_port,
|
||||
(const char *)dnsr_config.upstream_host.c_str(),
|
||||
dnsr_config.upstream_port))
|
||||
{
|
||||
|
@ -118,18 +190,21 @@ main(int argc, char *argv[])
|
|||
dnsd.intercept = &hookChecker;
|
||||
|
||||
llarp::LogInfo("singlethread start");
|
||||
worker = llarp_init_same_process_threadpool();
|
||||
logic = llarp_init_single_process_logic(worker);
|
||||
llarp_ev_loop_run_single_process(netloop, worker, logic);
|
||||
llarp::LogInfo("singlethread end");
|
||||
llarp_ev_loop_free(&netloop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// need this for timer stuff
|
||||
llarp_threadpool *worker = nullptr;
|
||||
llarp_logic *logic = nullptr;
|
||||
worker = llarp_init_same_process_threadpool();
|
||||
logic = llarp_init_single_process_logic(worker); // set up logic worker
|
||||
|
||||
// configure main netloop
|
||||
struct dnsd_context dnsd;
|
||||
if(!llarp_dnsd_init(&dnsd, nullptr, "*", 1053,
|
||||
if(!llarp_dnsd_init(&dnsd, nullptr, logic, "*", server_port,
|
||||
(const char *)dnsr_config.upstream_host.c_str(),
|
||||
dnsr_config.upstream_port))
|
||||
{
|
||||
|
|
|
@ -2,6 +2,34 @@
|
|||
#include "dnsd.hpp" // for llarp_handle_dnsd_recvfrom, dnsc
|
||||
#include "logger.hpp"
|
||||
|
||||
/*
|
||||
<domain-name> is a domain name represented as a series of labels, and
|
||||
terminated by a label with zero length. <character-string> is a single
|
||||
length octet followed by that number of characters. <character-string>
|
||||
is treated as binary information, and can be up to 256 characters in
|
||||
length (including the length octet).
|
||||
*/
|
||||
std::string getDNSstring(const char *buffer)
|
||||
{
|
||||
std::string str = "";
|
||||
uint8_t length = *buffer++;
|
||||
//printf("dnsStringLen[%d]\n", length);
|
||||
//llarp::LogInfo("dnsStringLen ", length);
|
||||
if (!length) return str;
|
||||
while(length != 0)
|
||||
{
|
||||
for(int i = 0; i < length; i++)
|
||||
{
|
||||
char c = *buffer++;
|
||||
str.append(1, c);
|
||||
}
|
||||
length = *buffer++;
|
||||
if(length != 0)
|
||||
str.append(1, '.');
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
uint16_t
|
||||
|
@ -14,7 +42,6 @@ extern "C"
|
|||
return value;
|
||||
}
|
||||
|
||||
// uint32_t
|
||||
uint32_t
|
||||
get32bits(const char *&buffer) throw()
|
||||
{
|
||||
|
@ -52,11 +79,14 @@ extern "C"
|
|||
hdr->arCount = get16bits(buffer);
|
||||
return hdr;
|
||||
}
|
||||
|
||||
|
||||
dns_msg_question *
|
||||
decode_question(const char *buffer)
|
||||
{
|
||||
dns_msg_question *question = new dns_msg_question;
|
||||
std::string m_qName = getDNSstring(buffer);
|
||||
//buffer += m_qName.size() + 1;
|
||||
/*
|
||||
std::string m_qName = "";
|
||||
int length = *buffer++;
|
||||
// llarp::LogInfo("qNamLen", length);
|
||||
|
@ -71,6 +101,7 @@ extern "C"
|
|||
if(length != 0)
|
||||
m_qName.append(1, '.');
|
||||
}
|
||||
*/
|
||||
question->name = m_qName;
|
||||
question->type = get16bits(buffer);
|
||||
question->qClass = get16bits(buffer);
|
||||
|
@ -81,6 +112,17 @@ extern "C"
|
|||
decode_answer(const char *buffer)
|
||||
{
|
||||
dns_msg_answer *answer = new dns_msg_answer;
|
||||
// skip for now until we can handle compressed labels
|
||||
/*
|
||||
std::string aName = getDNSstring((char *)buffer);
|
||||
buffer += aName.size() + 1;
|
||||
*/
|
||||
uint8_t first = *buffer++;
|
||||
// SOA hack
|
||||
if (first != 12)
|
||||
{
|
||||
buffer --; // rewind buffer one byte
|
||||
}
|
||||
answer->type = get16bits(buffer);
|
||||
// assert(answer->type < 259);
|
||||
if(answer->type > 259)
|
||||
|
@ -97,6 +139,33 @@ extern "C"
|
|||
}
|
||||
else
|
||||
{
|
||||
switch(answer->type)
|
||||
{
|
||||
case 6: // type 6 = SOA
|
||||
{
|
||||
// 2 names, then 4x 32bit
|
||||
std::string mname = getDNSstring((char *)buffer);
|
||||
std::string rname = getDNSstring((char *)buffer);
|
||||
uint32_t serial = get32bits(buffer);
|
||||
uint32_t refresh = get32bits(buffer);
|
||||
uint32_t retry = get32bits(buffer);
|
||||
uint32_t expire = get32bits(buffer);
|
||||
uint32_t minimum = get32bits(buffer);
|
||||
llarp::LogInfo("mname : ", mname);
|
||||
llarp::LogInfo("rname : ", rname);
|
||||
llarp::LogDebug("serial : ", serial);
|
||||
llarp::LogDebug("refresh : ", refresh);
|
||||
llarp::LogDebug("retry : ", retry);
|
||||
llarp::LogDebug("expire : ", expire);
|
||||
llarp::LogDebug("minimum : ", minimum);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
//llarp::LogWarn("Unknown Type ", answer->type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
llarp::LogWarn("Unknown Type ", answer->type);
|
||||
}
|
||||
return answer;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <sys/types.h> // for uint & ssize_t
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// dnsc can work over any UDP socket
|
||||
// however we can't ignore udp->user
|
||||
|
@ -61,6 +62,15 @@ struct dns_msg_answer
|
|||
uint8_t *rData;
|
||||
};
|
||||
|
||||
struct dns_packet
|
||||
{
|
||||
struct dns_msg_header *header;
|
||||
std::vector<dns_msg_question *> questions;
|
||||
std::vector<dns_msg_answer *> answers;
|
||||
std::vector<dns_msg_answer *> auth_rrs;
|
||||
std::vector<dns_msg_answer *> additional_rrs;
|
||||
};
|
||||
|
||||
extern "C"
|
||||
{
|
||||
uint16_t
|
||||
|
|
|
@ -123,6 +123,7 @@ answer_request_alloc(struct dnsc_context *dnsc, void *sock, const char *url,
|
|||
// register our self with the tracker
|
||||
dns_tracker *tracker = request->context->tracker;
|
||||
uint16_t id = ++tracker->c_requests;
|
||||
if (id == 65535) id = 0;
|
||||
tracker->client_request[id] = request;
|
||||
|
||||
dns_query *dns_packet = build_dns_packet((char *)request->question.name.c_str(), id, 1);
|
||||
|
@ -215,7 +216,10 @@ generic_handle_dnsc_recvfrom(dnsc_answer_request *request,
|
|||
{
|
||||
question = decode_question((const char *)castBuf);
|
||||
llarp::LogDebug("Read a question");
|
||||
castBuf += question->name.length() + 8;
|
||||
// 1 dot: 1 byte for length + length
|
||||
// 4 bytes for class/type
|
||||
castBuf += question->name.length() + 1 + 4;
|
||||
castBuf += 2; // skip answer label
|
||||
}
|
||||
|
||||
// FIXME: only handling one atm
|
||||
|
@ -225,6 +229,11 @@ generic_handle_dnsc_recvfrom(dnsc_answer_request *request,
|
|||
answer = decode_answer((const char *)castBuf);
|
||||
llarp::LogDebug("Read an answer");
|
||||
castBuf += answer->name.length() + 4 + 4 + 4 + answer->rdLen;
|
||||
auto diff = castBuf - (unsigned char *)buf;
|
||||
if (diff > sz)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// handle authority records (usually no answers with these, so we'll just
|
||||
// stomp) usually NS records tho
|
||||
|
|
|
@ -47,7 +47,7 @@ raw_handle_recvfrom(int *sockfd, const struct sockaddr *saddr, const void *buf,
|
|||
struct dnsc_context
|
||||
{
|
||||
/// Target: DNS server hostname/port to use
|
||||
// FIXME: ipv6 it
|
||||
// FIXME: ipv6 it & make it a vector
|
||||
sockaddr *server;
|
||||
/// tracker
|
||||
struct dns_tracker *tracker;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include <llarp/logic.h>
|
||||
|
||||
#include "dnsd.hpp"
|
||||
#include <llarp/dns.h>
|
||||
#include <string>
|
||||
|
@ -35,22 +37,6 @@ llarp_sendto_dns_hook_func(void *sock, const struct sockaddr *from,
|
|||
return llarp_ev_udp_sendto(udp, from, buffer, length);
|
||||
}
|
||||
|
||||
std::string const default_chars =
|
||||
"abcdefghijklmnaoqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
|
||||
#include <random>
|
||||
|
||||
std::string random_string(size_t len = 15, std::string const &allowed_chars = default_chars) {
|
||||
std::mt19937_64 gen { std::random_device()() };
|
||||
|
||||
std::uniform_int_distribution<size_t> dist { 0, allowed_chars.length()-1 };
|
||||
|
||||
std::string ret;
|
||||
|
||||
std::generate_n(std::back_inserter(ret), len, [&] { return allowed_chars[dist(gen)]; });
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
write404_dnss_response(const struct sockaddr *from, dnsd_question_request *request)
|
||||
{
|
||||
|
@ -87,7 +73,7 @@ write404_dnss_response(const struct sockaddr *from, dnsd_question_request *reque
|
|||
*write_buffer++ = 0; // write a null byte
|
||||
|
||||
uint out_bytes = write_buffer - bufferBegin;
|
||||
llarp::LogDebug("Sending ", out_bytes, " bytes");
|
||||
llarp::LogDebug("Sending 404, ", out_bytes, " bytes");
|
||||
// struct llarp_udp_io *udp = (struct llarp_udp_io *)request->user;
|
||||
request->sendto_hook(request->user, from, buf, out_bytes);
|
||||
}
|
||||
|
@ -152,13 +138,13 @@ writecname_dnss_response(std::string cname, const struct sockaddr *from,
|
|||
put32bits(write_buffer, 1); // ttl
|
||||
|
||||
put16bits(write_buffer, 4); // rdLength
|
||||
*write_buffer++ = 172;
|
||||
*write_buffer++ = 16;
|
||||
*write_buffer++ = 127;
|
||||
*write_buffer++ = 0;
|
||||
*write_buffer++ = 117;
|
||||
*write_buffer++ = 0;
|
||||
*write_buffer++ = 1;
|
||||
|
||||
uint out_bytes = write_buffer - bufferBegin;
|
||||
llarp::LogDebug("Sending ", out_bytes, " bytes");
|
||||
llarp::LogDebug("Sending cname, ", out_bytes, " bytes");
|
||||
// struct llarp_udp_io *udp = (struct llarp_udp_io *)request->user;
|
||||
request->sendto_hook(request->user, from, buf, out_bytes);
|
||||
}
|
||||
|
@ -169,13 +155,12 @@ void
|
|||
writesend_dnss_response(struct sockaddr *hostRes, const struct sockaddr *from,
|
||||
dnsd_question_request *request)
|
||||
{
|
||||
//llarp::Addr test(*from);
|
||||
//llarp::LogInfo("from ", test);
|
||||
if(!hostRes)
|
||||
{
|
||||
llarp::LogWarn("Failed to resolve");
|
||||
// FIXME: actually return correct packet
|
||||
//write404_dnss_response(from, request);
|
||||
usleep(1000*1000);
|
||||
writecname_dnss_response(random_string(32, "abcdefghijklmnopqrstuvwxyz")+"bob.loki", from, request);
|
||||
write404_dnss_response(from, request);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -218,7 +203,7 @@ writesend_dnss_response(struct sockaddr *hostRes, const struct sockaddr *from,
|
|||
*write_buffer++ = ip[3];
|
||||
|
||||
uint out_bytes = write_buffer - bufferBegin;
|
||||
llarp::LogDebug("Sending ", out_bytes, " bytes");
|
||||
llarp::LogDebug("Sending found, ", out_bytes, " bytes");
|
||||
// struct llarp_udp_io *udp = (struct llarp_udp_io *)request->user;
|
||||
request->sendto_hook(request->user, from, buf, out_bytes);
|
||||
}
|
||||
|
@ -281,26 +266,38 @@ handle_recvfrom(const char *buffer, ssize_t nbytes, const struct sockaddr *from,
|
|||
llarp::LogInfo("DNS request from ", test2);
|
||||
*/
|
||||
|
||||
sockaddr *fromCopy = new sockaddr(*from); // make our own sockaddr that won't get cleaned up
|
||||
if(request->context->intercept)
|
||||
{
|
||||
sockaddr *intercept = request->context->intercept(request->question.name, request->context);
|
||||
//llarp::Addr test(*from);
|
||||
//llarp::LogInfo("from ", test);
|
||||
dnsd_query_hook_response *intercept = request->context->intercept(request->question.name, fromCopy, request);
|
||||
if(intercept != nullptr)
|
||||
{
|
||||
// told that hook will handle overrides
|
||||
sockaddr *fromCopy = new sockaddr(*from);
|
||||
writesend_dnss_response(intercept, fromCopy, request);
|
||||
return;
|
||||
llarp::LogDebug("hook returned a response");
|
||||
if (intercept->dontSendResponse)
|
||||
{
|
||||
llarp::LogDebug("HOOKED: Not sending a response");
|
||||
return;
|
||||
}
|
||||
if (intercept->dontLookUp == true && intercept->returnThis)
|
||||
{
|
||||
llarp::LogDebug("HOOKED: sending an immediate override");
|
||||
// told that hook will handle overrides
|
||||
writesend_dnss_response(intercept->returnThis, fromCopy, request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: check request's context
|
||||
// FIXME: check request and context's client
|
||||
if (!request->context)
|
||||
{
|
||||
llarp::LogError("dnsd request context was not a dnsd context");
|
||||
sockaddr *fromCopy = new sockaddr(*from);
|
||||
writesend_dnss_response(nullptr, fromCopy, request);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
struct dns_tracker *tracker = (struct dns_tracker *)request->context->tracker;
|
||||
if (!tracker)
|
||||
{
|
||||
|
@ -317,16 +314,18 @@ handle_recvfrom(const char *buffer, ssize_t nbytes, const struct sockaddr *from,
|
|||
writesend_dnss_response(nullptr, fromCopy, request);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if(request->llarp)
|
||||
{
|
||||
// make async request
|
||||
llarp_resolve_host(&dnsd->client, m_qName.c_str(), &handle_dnsc_result,
|
||||
llarp_resolve_host(&request->context->client, m_qName.c_str(), &handle_dnsc_result,
|
||||
(void *)request);
|
||||
}
|
||||
else
|
||||
{
|
||||
// make raw/sync request
|
||||
raw_resolve_host(&dnsd->client, m_qName.c_str(), &handle_dnsc_result,
|
||||
raw_resolve_host(&request->context->client, m_qName.c_str(), &handle_dnsc_result,
|
||||
(void *)request);
|
||||
}
|
||||
}
|
||||
|
@ -373,6 +372,7 @@ raw_handle_recvfrom(int *sockfd, const struct sockaddr *saddr, const void *buf,
|
|||
|
||||
bool
|
||||
llarp_dnsd_init(struct dnsd_context *dnsd, struct llarp_ev_loop *netloop,
|
||||
struct llarp_logic *logic,
|
||||
const char *dnsd_ifname, uint16_t dnsd_port,
|
||||
const char *dnsc_hostname, uint16_t dnsc_port)
|
||||
{
|
||||
|
@ -388,6 +388,7 @@ llarp_dnsd_init(struct dnsd_context *dnsd, struct llarp_ev_loop *netloop,
|
|||
dns_udp_tracker.dnsd = dnsd;
|
||||
|
||||
dnsd->tracker = &dns_udp_tracker; // register global tracker with context
|
||||
dnsd->logic = logic; // set logic worker for timers
|
||||
dnsd->intercept = nullptr; // set default intercepter
|
||||
|
||||
// configure dns client
|
||||
|
|
|
@ -31,16 +31,29 @@ struct dnsd_question_request
|
|||
dnsd_context *context; // or you can access it via user (udp)
|
||||
};
|
||||
|
||||
// FIXME: made better as a two way structure, collapse the request and response together
|
||||
struct dnsd_query_hook_response
|
||||
{
|
||||
/// turn off communication
|
||||
bool dontSendResponse;
|
||||
/// turn off recursion
|
||||
bool dontLookUp;
|
||||
/// potential address
|
||||
sockaddr *returnThis;
|
||||
};
|
||||
|
||||
/// intercept query hook functor
|
||||
// we could have passed in the source sockaddr in case you wanted to
|
||||
// handle the response yourself
|
||||
typedef sockaddr *(*intercept_query_hook)(std::string name, struct dnsd_context *context);
|
||||
typedef dnsd_query_hook_response *(*intercept_query_hook)(std::string name,
|
||||
const struct sockaddr *from,
|
||||
struct dnsd_question_request *request);
|
||||
|
||||
/// DNS Server context
|
||||
struct dnsd_context
|
||||
{
|
||||
/// DNS daemon socket to listen on
|
||||
struct llarp_udp_io udp;
|
||||
/// for timers (MAYBEFIXME? maybe we decouple this)
|
||||
struct llarp_logic *logic;
|
||||
/// tracker
|
||||
struct dns_tracker *tracker;
|
||||
/// upstream DNS client context to use
|
||||
|
@ -57,10 +70,17 @@ llarp_handle_dnsd_recvfrom(struct llarp_udp_io *udp,
|
|||
const struct sockaddr *saddr, const void *buf,
|
||||
ssize_t sz);
|
||||
|
||||
/// for hook functions to use
|
||||
void
|
||||
writecname_dnss_response(std::string cname, const struct sockaddr *from,
|
||||
dnsd_question_request *request);
|
||||
|
||||
|
||||
/// initialize dns subsystem and bind socket
|
||||
/// returns true on bind success otherwise returns false
|
||||
bool
|
||||
llarp_dnsd_init(struct dnsd_context *dnsd, struct llarp_ev_loop *netloop,
|
||||
struct llarp_logic *logic,
|
||||
const char *dnsd_ifname, uint16_t dnsd_port,
|
||||
const char *dnsc_hostname, uint16_t dnsc_port);
|
||||
|
||||
|
|
Loading…
Reference in New Issue