mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-27 18:20:02 -05:00
forward command for unbound-control.
git-svn-id: file:///svn/unbound/trunk@1482 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
dfef08c21f
commit
a2dcd9c019
14 changed files with 231 additions and 50 deletions
131
daemon/remote.c
131
daemon/remote.c
|
|
@ -63,6 +63,8 @@
|
|||
#include "validator/val_kcache.h"
|
||||
#include "validator/val_kentry.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "iterator/iter_fwd.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "services/outbound_list.h"
|
||||
#include "services/outside_network.h"
|
||||
|
||||
|
|
@ -1221,24 +1223,102 @@ do_flush_name(SSL* ssl, struct worker* worker, char* arg)
|
|||
|
||||
/** print root forwards */
|
||||
static int
|
||||
print_root_fwds(SSL* ssl, struct config_file* cfg)
|
||||
print_root_fwds(SSL* ssl, struct iter_forwards* fwds, uint8_t* root)
|
||||
{
|
||||
struct config_stub* s;
|
||||
if(!ssl_printf(ssl, "root-forward:"))
|
||||
return 0;
|
||||
for(s = cfg->forwards; s; s = s->next) {
|
||||
if(s->name && strcmp(s->name, ".") == 0) {
|
||||
struct config_strlist* p;
|
||||
for(p = s->hosts; p; p = p->next)
|
||||
if(!ssl_printf(ssl, " %s", p->str))
|
||||
return 0;
|
||||
for(p = s->addrs; p; p = p->next)
|
||||
if(!ssl_printf(ssl, " %s", p->str))
|
||||
return 0;
|
||||
return ssl_printf(ssl, "\n");
|
||||
char buf[257];
|
||||
struct delegpt* dp;
|
||||
struct delegpt_ns* ns;
|
||||
struct delegpt_addr* a;
|
||||
int f = 0;
|
||||
dp = forwards_lookup(fwds, root, LDNS_RR_CLASS_IN);
|
||||
if(!dp)
|
||||
return ssl_printf(ssl, "off (using root hints)\n");
|
||||
/* if dp is returned it must be the root */
|
||||
log_assert(query_dname_compare(dp->name, root)==0);
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
dname_str(ns->name, buf);
|
||||
if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf))
|
||||
return 0;
|
||||
f = 1;
|
||||
}
|
||||
for(a = dp->target_list; a; a = a->next_target) {
|
||||
addr_to_str(&a->addr, a->addrlen, buf, sizeof(buf));
|
||||
if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf))
|
||||
return 0;
|
||||
f = 1;
|
||||
}
|
||||
return ssl_printf(ssl, "\n");
|
||||
}
|
||||
|
||||
/** parse args into delegpt */
|
||||
static struct delegpt*
|
||||
parse_delegpt(SSL* ssl, struct regional* region, char* args, uint8_t* root)
|
||||
{
|
||||
/* parse args and add in */
|
||||
char* p = args;
|
||||
char* todo;
|
||||
struct delegpt* dp = delegpt_create(region);
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
if(!dp || !delegpt_set_name(dp, region, root)) {
|
||||
(void)ssl_printf(ssl, "error out of memory\n");
|
||||
return NULL;
|
||||
}
|
||||
while(p) {
|
||||
todo = p;
|
||||
p = strchr(p, ' '); /* find next spot, if any */
|
||||
if(p) {
|
||||
*p++ = 0; /* end this spot */
|
||||
p = skipwhite(p); /* position at next spot */
|
||||
}
|
||||
/* parse address */
|
||||
if(!extstrtoaddr(todo, &addr, &addrlen)) {
|
||||
(void)ssl_printf(ssl, "error cannot parse"
|
||||
" IP address '%s'\n", todo);
|
||||
return NULL;
|
||||
}
|
||||
/* add address */
|
||||
if(!delegpt_add_addr(dp, region, &addr, addrlen, 0, 1)) {
|
||||
(void)ssl_printf(ssl, "error out of memory\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return ssl_printf(ssl, " no (using root hints)\n");
|
||||
return dp;
|
||||
}
|
||||
|
||||
/** do the status command */
|
||||
static void
|
||||
do_forward(SSL* ssl, struct worker* worker, char* args)
|
||||
{
|
||||
struct iter_forwards* fwd = worker->env.fwds;
|
||||
uint8_t* root = (uint8_t*)"\000";
|
||||
if(!fwd) {
|
||||
(void)ssl_printf(ssl, "error: structure not allocated\n");
|
||||
return;
|
||||
}
|
||||
if(args == NULL || args[0] == 0) {
|
||||
(void)print_root_fwds(ssl, fwd, root);
|
||||
return;
|
||||
}
|
||||
/* set root forwards for this thread. since we are in remote control
|
||||
* the actual mesh is not running, so we can freely edit it. */
|
||||
/* delete all the existing queries first */
|
||||
mesh_delete_all(worker->env.mesh);
|
||||
/* reset the fwd structure ; the cfg is unchanged (shared by threads)*/
|
||||
/* this reset frees up memory */
|
||||
forwards_apply_cfg(fwd, worker->env.cfg);
|
||||
if(strcmp(args, "off") == 0) {
|
||||
forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, root);
|
||||
} else {
|
||||
struct delegpt* dp;
|
||||
if(!(dp = parse_delegpt(ssl, fwd->region, args, root)))
|
||||
return;
|
||||
if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) {
|
||||
(void)ssl_printf(ssl, "error out of memory\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
send_ok(ssl);
|
||||
}
|
||||
|
||||
/** do the status command */
|
||||
|
|
@ -1261,8 +1341,6 @@ do_status(SSL* ssl, struct worker* worker)
|
|||
}
|
||||
if(!ssl_printf(ssl, " ]\n"))
|
||||
return;
|
||||
if(!print_root_fwds(ssl, worker->env.cfg))
|
||||
return;
|
||||
uptime = (time_t)time(NULL) - (time_t)worker->daemon->time_boot.tv_sec;
|
||||
if(!ssl_printf(ssl, "uptime: %u seconds\n", (unsigned)uptime))
|
||||
return;
|
||||
|
|
@ -1310,21 +1388,11 @@ get_mesh_status(struct mesh_area* mesh, struct mesh_state* m,
|
|||
if(ol->first == NULL)
|
||||
snprintf(buf, len, " (empty_list)");
|
||||
for(e = ol->first; e; e = e->next) {
|
||||
int af = (int)((struct sockaddr_in*)&e->qsent->addr)
|
||||
->sin_family;
|
||||
void* sinaddr = &((struct sockaddr_in*)&e->qsent->addr)
|
||||
->sin_addr;
|
||||
if(addr_is_ip6(&e->qsent->addr, e->qsent->addrlen))
|
||||
sinaddr = &((struct sockaddr_in6*)
|
||||
&e->qsent->addr)->sin6_addr;
|
||||
|
||||
snprintf(buf, len, " ");
|
||||
l = strlen(buf);
|
||||
buf += l; len -= l;
|
||||
|
||||
if(inet_ntop(af, sinaddr, buf, (socklen_t)len) == 0) {
|
||||
snprintf(buf, len, "(inet_ntop_error)");
|
||||
}
|
||||
addr_to_str(&e->qsent->addr, e->qsent->addrlen,
|
||||
buf, len);
|
||||
l = strlen(buf);
|
||||
buf += l; len -= l;
|
||||
}
|
||||
|
|
@ -1434,6 +1502,11 @@ execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd,
|
|||
} else if(strncmp(p, "load_cache", 10) == 0) {
|
||||
if(load_cache(ssl, worker)) send_ok(ssl);
|
||||
return;
|
||||
} else if(strncmp(p, "forward", 7) == 0) {
|
||||
/* must always distribute this cmd */
|
||||
if(rc) distribute_cmd(rc, ssl, cmd);
|
||||
do_forward(ssl, worker, skipwhite(p+7));
|
||||
return;
|
||||
} else if(strncmp(p, "flush_stats", 11) == 0) {
|
||||
/* must always distribute this cmd */
|
||||
if(rc) distribute_cmd(rc, ssl, cmd);
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#include "util/data/dname.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/tube.h"
|
||||
#include "iterator/iter_fwd.h"
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
|
|
@ -158,7 +159,8 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
|
|||
+ sizeof(worker->rndstate)
|
||||
+ regional_get_mem(worker->scratchpad)
|
||||
+ sizeof(*worker->env.scratch_buffer)
|
||||
+ ldns_buffer_capacity(worker->env.scratch_buffer);
|
||||
+ ldns_buffer_capacity(worker->env.scratch_buffer)
|
||||
+ forwards_get_mem(worker->env.fwds);
|
||||
if(cur_serv) {
|
||||
me += serviced_get_mem(cur_serv);
|
||||
}
|
||||
|
|
@ -1119,6 +1121,12 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
|||
worker->env.kill_sub = &mesh_state_delete;
|
||||
worker->env.detect_cycle = &mesh_detect_cycle;
|
||||
worker->env.scratch_buffer = ldns_buffer_new(cfg->msg_buffer_size);
|
||||
if(!(worker->env.fwds = forwards_create()) ||
|
||||
!forwards_apply_cfg(worker->env.fwds, cfg)) {
|
||||
log_err("Could not set forward zones");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
if(!worker->env.mesh || !worker->env.scratch_buffer) {
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
|
|
@ -1151,6 +1159,7 @@ worker_delete(struct worker* worker)
|
|||
}
|
||||
mesh_delete(worker->env.mesh);
|
||||
ldns_buffer_free(worker->env.scratch_buffer);
|
||||
forwards_delete(worker->env.fwds);
|
||||
listen_delete(worker->front);
|
||||
outside_network_delete(worker->back);
|
||||
comm_signal_delete(worker->comsig);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
13 February 2009: Wouter
|
||||
- forwarder information now per-thread duplicated.
|
||||
This keeps it read only for speed, with no locking necessary.
|
||||
- forward command for unbound control to change forwarders to use
|
||||
on the fly.
|
||||
|
||||
12 February 2009: Wouter
|
||||
- call setusercontext if available (on BSD).
|
||||
- small refactor of stats clearing.
|
||||
|
|
|
|||
|
|
@ -134,6 +134,27 @@ such as a higher verbosity level.
|
|||
Show what is worked on. Prints all queries that the server is currently
|
||||
working on. Prints the time that users have been waiting. For internal
|
||||
requests, no time is printed. And then prints out the module status.
|
||||
.TP
|
||||
.B forward [off | addr ... ]
|
||||
Setup forwarding mode. Configures if the server should ask other upstream
|
||||
nameservers, should go to the internet root nameservers itself, or show
|
||||
the current config. You could pass the nameservers after a DHCP update.
|
||||
.IP
|
||||
Without arguments the current list of addresses used to forward all queries
|
||||
to is printed. On startup this is from the forward-zone "." configuration.
|
||||
Afterwards it shows the status. It prints off when no forwarding is used.
|
||||
.IP
|
||||
If \fIoff\fR is passed, forwarding is disabled and the root nameservers
|
||||
are used. This can be used to avoid to avoid buggy or non-DNSSEC supporting
|
||||
nameservers returned from DHCP. But may not work in hotels or hotspots.
|
||||
.IP
|
||||
If one or more IPv4 or IPv6 addresses are given, those are then used to forward
|
||||
queries to. The addresses must be separated with spaces. With '@port' the
|
||||
port number can be set explicitly (default port is 53 (DNS)).
|
||||
.IP
|
||||
By default the forwarder information from the config file for the root "." is
|
||||
used. The config file is not changed, so after a reload these changes are
|
||||
gone. Other forward zones from the config file are not affected by this command.
|
||||
.SH "EXIT CODE"
|
||||
The unbound-control program exits with status code 1 on error, 0 on success.
|
||||
.SH "SET UP"
|
||||
|
|
|
|||
|
|
@ -292,3 +292,27 @@ forwards_get_mem(struct iter_forwards* fwd)
|
|||
return sizeof(*fwd) + sizeof(*fwd->tree) +
|
||||
regional_get_mem(fwd->region);
|
||||
}
|
||||
|
||||
int
|
||||
forwards_add_zone(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp)
|
||||
{
|
||||
if(!forwards_insert(fwd, c, dp))
|
||||
return 0;
|
||||
fwd_init_parents(fwd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm)
|
||||
{
|
||||
struct iter_forward_zone key;
|
||||
key.node.key = &key;
|
||||
key.dclass = c;
|
||||
key.name = nm;
|
||||
key.namelabs = dname_count_size_labels(nm, &key.namelen);
|
||||
if(!rbtree_search(fwd->tree, &key))
|
||||
return; /* nothing to do */
|
||||
(void)rbtree_delete(fwd->tree, &key);
|
||||
fwd_init_parents(fwd);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@
|
|||
#ifndef ITERATOR_ITER_FWD_H
|
||||
#define ITERATOR_ITER_FWD_H
|
||||
#include "util/rbtree.h"
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct delegpt;
|
||||
struct regional;
|
||||
|
|
@ -128,4 +127,28 @@ size_t forwards_get_mem(struct iter_forwards* fwd);
|
|||
/** compare two fwd entries */
|
||||
int fwd_cmp(const void* k1, const void* k2);
|
||||
|
||||
/**
|
||||
* Add zone to forward structure. For external use since it recalcs
|
||||
* the tree parents.
|
||||
* @param fwd: the forward data structure
|
||||
* @param c: class of zone
|
||||
* @param dp: delegation point with name and target nameservers for new
|
||||
* forward zone. This delegation point and all its data must be
|
||||
* malloced in the fwd->region. (then it is freed when the fwd is
|
||||
* deleted).
|
||||
* @return false on failure (out of memory);
|
||||
*/
|
||||
int forwards_add_zone(struct iter_forwards* fwd, uint16_t c,
|
||||
struct delegpt* dp);
|
||||
|
||||
/**
|
||||
* Remove zone from forward structure. For external use since it
|
||||
* recalcs the tree parents. Does not actually release any memory, the region
|
||||
* is unchanged.
|
||||
* @param fwd: the forward data structure
|
||||
* @param c: class of zone
|
||||
* @param nm: name of zone (in uncompressed wireformat).
|
||||
*/
|
||||
void forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm);
|
||||
|
||||
#endif /* ITERATOR_ITER_FWD_H */
|
||||
|
|
|
|||
|
|
@ -112,12 +112,6 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
|
|||
log_err("Could not set root or stub hints");
|
||||
return 0;
|
||||
}
|
||||
if(!iter_env->fwds)
|
||||
iter_env->fwds = forwards_create();
|
||||
if(!iter_env->fwds || !forwards_apply_cfg(iter_env->fwds, cfg)) {
|
||||
log_err("Could not set forward zones");
|
||||
return 0;
|
||||
}
|
||||
if(!iter_env->donotq)
|
||||
iter_env->donotq = donotq_create();
|
||||
if(!iter_env->donotq || !donotq_apply_cfg(iter_env->donotq, cfg)) {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ iter_deinit(struct module_env* env, int id)
|
|||
free(iter_env->target_fetch_policy);
|
||||
priv_delete(iter_env->priv);
|
||||
hints_delete(iter_env->hints);
|
||||
forwards_delete(iter_env->fwds);
|
||||
donotq_delete(iter_env->donotq);
|
||||
free(iter_env);
|
||||
env->modinfo[id] = NULL;
|
||||
|
|
@ -775,16 +774,14 @@ generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id)
|
|||
*
|
||||
* @param qstate: query state.
|
||||
* @param iq: iterator query state.
|
||||
* @param ie: iterator shared global environment.
|
||||
* @return true if the request is forwarded, false if not.
|
||||
* If returns true but, iq->dp is NULL then a malloc failure occurred.
|
||||
*/
|
||||
static int
|
||||
forward_request(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
struct iter_env* ie)
|
||||
forward_request(struct module_qstate* qstate, struct iter_qstate* iq)
|
||||
{
|
||||
struct delegpt* dp = forwards_lookup(ie->fwds, iq->qchase.qname,
|
||||
iq->qchase.qclass);
|
||||
struct delegpt* dp = forwards_lookup(qstate->env->fwds,
|
||||
iq->qchase.qname, iq->qchase.qclass);
|
||||
if(!dp)
|
||||
return 0;
|
||||
/* send recursion desired to forward addr */
|
||||
|
|
@ -892,7 +889,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
}
|
||||
|
||||
/* attempt to forward the request */
|
||||
if(forward_request(qstate, iq, ie))
|
||||
if(forward_request(qstate, iq))
|
||||
{
|
||||
if(!iq->dp) {
|
||||
log_err("alloc failure for forward dp");
|
||||
|
|
@ -2129,8 +2126,7 @@ iter_get_mem(struct module_env* env, int id)
|
|||
if(!ie)
|
||||
return 0;
|
||||
return sizeof(*ie) + sizeof(int)*((size_t)ie->max_dependency_depth+1)
|
||||
+ hints_get_mem(ie->hints) + forwards_get_mem(ie->fwds)
|
||||
+ donotq_get_mem(ie->donotq);
|
||||
+ hints_get_mem(ie->hints) + donotq_get_mem(ie->donotq);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -87,9 +87,6 @@ struct iter_env {
|
|||
/** A flag to indicate whether or not we have an IPv6 route */
|
||||
int supports_ipv6;
|
||||
|
||||
/** Mapping of forwarding zones to targets. */
|
||||
struct iter_forwards* fwds;
|
||||
|
||||
/** A set of inetaddrs that should never be queried. */
|
||||
struct iter_donotq* donotq;
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@
|
|||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/tube.h"
|
||||
#include "iterator/iter_fwd.h"
|
||||
|
||||
/** handle new query command for bg worker */
|
||||
static void handle_newq(struct libworker* w, uint8_t* buf, uint32_t len);
|
||||
|
|
@ -76,6 +77,7 @@ libworker_delete(struct libworker* w)
|
|||
!w->is_bg || w->is_bg_thread);
|
||||
ldns_buffer_free(w->env->scratch_buffer);
|
||||
regional_destroy(w->env->scratch);
|
||||
forwards_delete(w->env->fwds);
|
||||
ub_randfree(w->env->rnd);
|
||||
free(w->env);
|
||||
}
|
||||
|
|
@ -114,10 +116,15 @@ libworker_setup(struct ub_ctx* ctx, int is_bg)
|
|||
}
|
||||
w->env->scratch = regional_create_custom(cfg->msg_buffer_size);
|
||||
w->env->scratch_buffer = ldns_buffer_new(cfg->msg_buffer_size);
|
||||
w->env->fwds = forwards_create();
|
||||
if(w->env->fwds && !forwards_apply_cfg(w->env->fwds, cfg)) {
|
||||
forwards_delete(w->env->fwds);
|
||||
w->env->fwds = NULL;
|
||||
}
|
||||
if(!w->is_bg || w->is_bg_thread) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
if(!w->env->scratch || !w->env->scratch_buffer) {
|
||||
if(!w->env->scratch || !w->env->scratch_buffer || !w->env->fwds) {
|
||||
libworker_delete(w);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,9 @@ usage()
|
|||
printf(" flush_stats flush statistics, make zero\n");
|
||||
printf(" flush_requestlist drop queries that are worked on\n");
|
||||
printf(" dump_requestlist show what is worked on\n");
|
||||
printf(" forward [off | addr ...] without arg show forward setup\n");
|
||||
printf(" or off to turn off root forwarding\n");
|
||||
printf(" or give list of ip addresses\n");
|
||||
printf("Version %s\n", PACKAGE_VERSION);
|
||||
printf("BSD licensed, see LICENSE in source package for details.\n");
|
||||
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ struct mesh_area;
|
|||
struct mesh_state;
|
||||
struct val_anchors;
|
||||
struct val_neg_cache;
|
||||
struct iter_forwards;
|
||||
|
||||
/** Maximum number of modules in operation */
|
||||
#define MAX_MODULE 5
|
||||
|
|
@ -208,6 +209,9 @@ struct module_env {
|
|||
/** negative cache, configured by the validator. if not NULL,
|
||||
* contains NSEC record lookup trees. */
|
||||
struct val_neg_cache* neg_cache;
|
||||
/** Mapping of forwarding zones to targets.
|
||||
* iterator forwarder information. per-thread, created by worker */
|
||||
struct iter_forwards* fwds;
|
||||
/** module specific data. indexed by module id. */
|
||||
void* modinfo[MAX_MODULE];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -461,3 +461,16 @@ addr_in_common(struct sockaddr_storage* addr1, int net1,
|
|||
if(match > min) match = min;
|
||||
return match;
|
||||
}
|
||||
|
||||
void
|
||||
addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
char* buf, size_t len)
|
||||
{
|
||||
int af = (int)((struct sockaddr_in*)addr)->sin_family;
|
||||
void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
|
||||
if(addr_is_ip6(addr, addrlen))
|
||||
sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr;
|
||||
if(inet_ntop(af, sinaddr, buf, (socklen_t)len) == 0) {
|
||||
snprintf(buf, len, "(inet_ntop_error)");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,4 +258,15 @@ void addr_mask(struct sockaddr_storage* addr, socklen_t len, int net);
|
|||
int addr_in_common(struct sockaddr_storage* addr1, int net1,
|
||||
struct sockaddr_storage* addr2, int net2, socklen_t addrlen);
|
||||
|
||||
/**
|
||||
* Put address into string, works for IPv4 and IPv6.
|
||||
* @param addr: address
|
||||
* @param addrlen: length of address
|
||||
* @param buf: result string stored here
|
||||
* @param len: length of buf.
|
||||
* On failure a string with "error" is stored inside.
|
||||
*/
|
||||
void addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
char* buf, size_t len);
|
||||
|
||||
#endif /* NET_HELP_H */
|
||||
|
|
|
|||
Loading…
Reference in a new issue