summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2005-01-31 01:57:58 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:09:23 -0500
commit414f6c80b22b128c25d947d62f6b5d1ec13091b6 (patch)
treeda147a6c2802a7af79b5dfcdf3d3f6508287520e /source4
parent37449657a8d335097b3f3559f8b5bf084b50b85a (diff)
downloadsamba-414f6c80b22b128c25d947d62f6b5d1ec13091b6.tar.gz
samba-414f6c80b22b128c25d947d62f6b5d1ec13091b6.tar.bz2
samba-414f6c80b22b128c25d947d62f6b5d1ec13091b6.zip
r5114: the nbtd task can now act as a basic B-node server. It registers its
names on the network and answers name queries. Lots of details are still missing, but at least this now means you don't need a Samba3 nmbd to use Samba4. missing pieces include: - name registrations should be "shout 3 times, then demand" - no WINS server yet - no master browser code (This used to be commit d7d31fdc6670f026f96b50e51a4de19f0b920e5b)
Diffstat (limited to 'source4')
-rw-r--r--source4/include/structs.h3
-rw-r--r--source4/lib/netif/interface.c14
-rw-r--r--source4/lib/util_strlist.c168
-rw-r--r--source4/libcli/config.mk3
-rw-r--r--source4/libcli/nbt/libnbt.h23
-rw-r--r--source4/libcli/nbt/namequery.c27
-rw-r--r--source4/libcli/nbt/nameregister.c140
-rw-r--r--source4/libcli/nbt/nbtsocket.c52
-rw-r--r--source4/librpc/idl/nbt.idl14
-rw-r--r--source4/nbt_server/config.mk6
-rw-r--r--source4/nbt_server/interfaces.c97
-rw-r--r--source4/nbt_server/nbt_server.c18
-rw-r--r--source4/nbt_server/nbt_server.h22
-rw-r--r--source4/nbt_server/packet.c38
-rw-r--r--source4/nbt_server/query.c122
-rw-r--r--source4/nbt_server/register.c161
-rw-r--r--source4/nbt_server/winsserver.c35
-rw-r--r--source4/utils/nmblookup.c13
18 files changed, 745 insertions, 211 deletions
diff --git a/source4/include/structs.h b/source4/include/structs.h
index 0de876ca7c..69f84ea55e 100644
--- a/source4/include/structs.h
+++ b/source4/include/structs.h
@@ -150,7 +150,7 @@ struct nbt_name_packet;
struct nbt_name_socket;
struct nbt_name_query;
struct nbt_name_status;
-
+struct nbt_name_register;
struct messaging_context;
struct stream_connection;
@@ -159,3 +159,4 @@ struct model_ops;
struct stream_server_ops;
struct nbt_server;
+struct nbt_interface;
diff --git a/source4/lib/netif/interface.c b/source4/lib/netif/interface.c
index 4f22095f7a..bf1e147e02 100644
--- a/source4/lib/netif/interface.c
+++ b/source4/lib/netif/interface.c
@@ -330,6 +330,20 @@ struct ipv4_addr *iface_n_bcast(int n)
return NULL;
}
+/****************************************************************************
+ return netmask of the Nth interface
+ **************************************************************************/
+struct ipv4_addr *iface_n_netmask(int n)
+{
+ struct interface *i;
+
+ for (i=local_interfaces;i && n;i=i->next)
+ n--;
+
+ if (i) return &i->nmask;
+ return NULL;
+}
+
/* these 3 functions return the ip/bcast/nmask for the interface
most appropriate for the given ip address. If they can't find
an appropriate interface they return the requested field of the
diff --git a/source4/lib/util_strlist.c b/source4/lib/util_strlist.c
index 33f824dcf8..d945c78472 100644
--- a/source4/lib/util_strlist.c
+++ b/source4/lib/util_strlist.c
@@ -152,173 +152,5 @@ void str_list_free(char ***list)
SAFE_FREE(*list);
}
-BOOL str_list_substitute(char **list, const char *pattern, const char *insert)
-{
- char *p, *s, *t;
- ssize_t ls, lp, li, ld, i, d;
-
- if (!list)
- return False;
- if (!pattern)
- return False;
- if (!insert)
- return False;
-
- lp = (ssize_t)strlen(pattern);
- li = (ssize_t)strlen(insert);
- ld = li -lp;
-
- while (*list) {
- s = *list;
- ls = (ssize_t)strlen(s);
-
- while ((p = strstr(s, pattern))) {
- t = *list;
- d = p -t;
- if (ld) {
- t = (char *) malloc(ls +ld +1);
- if (!t) {
- DEBUG(0,("str_list_substitute: Unable to allocate memory"));
- return False;
- }
- memcpy(t, *list, d);
- memcpy(t +d +li, p +lp, ls -d -lp +1);
- SAFE_FREE(*list);
- *list = t;
- ls += ld;
- s = t +d +li;
- }
-
- for (i = 0; i < li; i++) {
- switch (insert[i]) {
- case '`':
- case '"':
- case '\'':
- case ';':
- case '$':
- case '%':
- case '\r':
- case '\n':
- t[d +i] = '_';
- break;
- default:
- t[d +i] = insert[i];
- }
- }
- }
-
- list++;
- }
-
- return True;
-}
-#define IPSTR_LIST_SEP ","
-
-/**
- * Add ip string representation to ipstr list. Used also
- * as part of @function ipstr_list_make
- *
- * @param ipstr_list pointer to string containing ip list;
- * MUST BE already allocated and IS reallocated if necessary
- * @param ipstr_size pointer to current size of ipstr_list (might be changed
- * as a result of reallocation)
- * @param ip IP address which is to be added to list
- * @return pointer to string appended with new ip and possibly
- * reallocated to new length
- **/
-
-char* ipstr_list_add(char** ipstr_list, const struct ipv4_addr *ip)
-{
- char* new_ipstr = NULL;
-
- /* arguments checking */
- if (!ipstr_list || !ip) return NULL;
-
- /* attempt to convert ip to a string and append colon separator to it */
- if (*ipstr_list) {
- asprintf(&new_ipstr, "%s%s%s", *ipstr_list, IPSTR_LIST_SEP,sys_inet_ntoa(*ip));
- SAFE_FREE(*ipstr_list);
- } else {
- asprintf(&new_ipstr, "%s", sys_inet_ntoa(*ip));
- }
- *ipstr_list = new_ipstr;
- return *ipstr_list;
-}
-
-/**
- * Allocate and initialise an ipstr list using ip adresses
- * passed as arguments.
- *
- * @param ipstr_list pointer to string meant to be allocated and set
- * @param ip_list array of ip addresses to place in the list
- * @param ip_count number of addresses stored in ip_list
- * @return pointer to allocated ip string
- **/
-
-char* ipstr_list_make(char** ipstr_list, const struct ipv4_addr* ip_list, int ip_count)
-{
- int i;
-
- /* arguments checking */
- if (!ip_list && !ipstr_list) return 0;
-
- *ipstr_list = NULL;
-
- /* process ip addresses given as arguments */
- for (i = 0; i < ip_count; i++)
- *ipstr_list = ipstr_list_add(ipstr_list, &ip_list[i]);
-
- return (*ipstr_list);
-}
-
-
-/**
- * Parse given ip string list into array of ip addresses
- * (as in_addr structures)
- *
- * @param ipstr ip string list to be parsed
- * @param ip_list pointer to array of ip addresses which is
- * allocated by this function and must be freed by caller
- * @return number of succesfully parsed addresses
- **/
-
-int ipstr_list_parse(const char* ipstr_list, struct ipv4_addr** ip_list)
-{
- fstring token_str;
- int count;
-
- if (!ipstr_list || !ip_list) return 0;
-
- for (*ip_list = NULL, count = 0;
- next_token(&ipstr_list, token_str, IPSTR_LIST_SEP, FSTRING_LEN);
- count++) {
-
- struct ipv4_addr addr;
-
- /* convert single token to ip address */
- if ( (addr.addr = sys_inet_addr(token_str)) == INADDR_NONE )
- break;
-
- /* prepare place for another in_addr structure */
- *ip_list = realloc_p(*ip_list, struct ipv4_addr, count + 1);
- if (!*ip_list) return -1;
-
- (*ip_list)[count] = addr;
- }
-
- return count;
-}
-
-
-/**
- * Safely free ip string list
- *
- * @param ipstr_list ip string list to be freed
- **/
-
-void ipstr_list_free(char* ipstr_list)
-{
- SAFE_FREE(ipstr_list);
-}
diff --git a/source4/libcli/config.mk b/source4/libcli/config.mk
index 06b46c6f18..c801524c2f 100644
--- a/source4/libcli/config.mk
+++ b/source4/libcli/config.mk
@@ -24,7 +24,8 @@ ADD_OBJ_FILES = \
ADD_OBJ_FILES = \
libcli/nbt/nbtname.o \
libcli/nbt/nbtsocket.o \
- libcli/nbt/namequery.o
+ libcli/nbt/namequery.o \
+ libcli/nbt/nameregister.o
REQUIRED_SUBSYSTEMS = LIBNDR_RAW NDR_NBT SOCKET
[SUBSYSTEM::LIBCLI_RESOLVE]
diff --git a/source4/libcli/nbt/libnbt.h b/source4/libcli/nbt/libnbt.h
index 82069f0390..3bfffa25d0 100644
--- a/source4/libcli/nbt/libnbt.h
+++ b/source4/libcli/nbt/libnbt.h
@@ -113,7 +113,8 @@ struct nbt_name_query {
struct {
const char *reply_from;
struct nbt_name name;
- const char *reply_addr;
+ int16_t num_addrs;
+ const char **reply_addrs;
} out;
};
@@ -130,3 +131,23 @@ struct nbt_name_status {
struct nbt_rdata_status status;
} out;
};
+
+/* a name registration request */
+struct nbt_name_register {
+ struct {
+ struct nbt_name name;
+ const char *dest_addr;
+ const char *address;
+ uint16_t nb_flags;
+ BOOL register_demand;
+ BOOL broadcast;
+ uint32_t ttl;
+ int timeout; /* in seconds */
+ } in;
+ struct {
+ const char *reply_from;
+ struct nbt_name name;
+ const char *reply_addr;
+ uint8_t rcode;
+ } out;
+};
diff --git a/source4/libcli/nbt/namequery.c b/source4/libcli/nbt/namequery.c
index 6f549e6241..f6744e9f14 100644
--- a/source4/libcli/nbt/namequery.c
+++ b/source4/libcli/nbt/namequery.c
@@ -66,7 +66,7 @@ failed:
}
/*
- wait for a name query replu
+ wait for a name query reply
*/
NTSTATUS nbt_name_query_recv(struct nbt_name_request *req,
TALLOC_CTX *mem_ctx, struct nbt_name_query *io)
@@ -75,6 +75,7 @@ NTSTATUS nbt_name_query_recv(struct nbt_name_request *req,
struct nbt_name_packet *packet;
const char *addr;
struct in_addr in;
+ int i;
status = nbt_name_request_recv(req);
if (!NT_STATUS_IS_OK(status) ||
@@ -94,13 +95,23 @@ NTSTATUS nbt_name_query_recv(struct nbt_name_request *req,
}
io->out.name = packet->answers[0].name;
- in.s_addr = htonl(packet->answers[0].rdata.netbios.ipaddr);
- addr = inet_ntoa(in);
- if (addr == NULL) {
+ io->out.num_addrs = packet->answers[0].rdata.netbios.length / 6;
+ io->out.reply_addrs = talloc_array(mem_ctx, const char *, io->out.num_addrs);
+ if (io->out.reply_addrs == NULL) {
talloc_free(req);
return NT_STATUS_NO_MEMORY;
}
- io->out.reply_addr = talloc_strdup(mem_ctx, addr);
+
+ for (i=0;i<io->out.num_addrs;i++) {
+ in.s_addr = htonl(packet->answers[0].rdata.netbios.addresses[i].ipaddr);
+ addr = inet_ntoa(in);
+ if (addr == NULL) {
+ talloc_free(req);
+ return NT_STATUS_NO_MEMORY;
+ }
+ io->out.reply_addrs[i] = talloc_strdup(mem_ctx, addr);
+ }
+
talloc_steal(mem_ctx, io->out.name.name);
talloc_steal(mem_ctx, io->out.name.scope);
@@ -110,7 +121,7 @@ NTSTATUS nbt_name_query_recv(struct nbt_name_request *req,
}
/*
- wait for a name query replu
+ wait for a name query reply
*/
NTSTATUS nbt_name_query(struct nbt_name_socket *nbtsock,
TALLOC_CTX *mem_ctx, struct nbt_name_query *io)
@@ -156,7 +167,7 @@ failed:
}
/*
- wait for a name status replu
+ wait for a name status reply
*/
NTSTATUS nbt_name_status_recv(struct nbt_name_request *req,
TALLOC_CTX *mem_ctx, struct nbt_name_status *io)
@@ -199,7 +210,7 @@ NTSTATUS nbt_name_status_recv(struct nbt_name_request *req,
}
/*
- wait for a name status replu
+ wait for a name status reply
*/
NTSTATUS nbt_name_status(struct nbt_name_socket *nbtsock,
TALLOC_CTX *mem_ctx, struct nbt_name_status *io)
diff --git a/source4/libcli/nbt/nameregister.c b/source4/libcli/nbt/nameregister.c
new file mode 100644
index 0000000000..2d1e964977
--- /dev/null
+++ b/source4/libcli/nbt/nameregister.c
@@ -0,0 +1,140 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ send out a name registration request
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 2 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
+ GNU General Public License for more details.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "libcli/nbt/libnbt.h"
+#include "system/network.h"
+
+/*
+ send a nbt name registration request
+*/
+struct nbt_name_request *nbt_name_register_send(struct nbt_name_socket *nbtsock,
+ struct nbt_name_register *io)
+{
+ struct nbt_name_request *req;
+ struct nbt_name_packet *packet;
+
+ packet = talloc_zero(nbtsock, struct nbt_name_packet);
+ if (packet == NULL) return NULL;
+
+ packet->qdcount = 1;
+ packet->arcount = 1;
+ packet->operation = NBT_OPCODE_REGISTER;
+ if (io->in.broadcast) {
+ packet->operation |= NBT_FLAG_BROADCAST;
+ }
+ if (io->in.register_demand) {
+ packet->operation |= NBT_FLAG_RECURSION_DESIRED;
+ }
+
+ packet->questions = talloc_array(packet, struct nbt_name_question, 1);
+ if (packet->questions == NULL) goto failed;
+
+ packet->questions[0].name = io->in.name;
+ packet->questions[0].question_type = NBT_QTYPE_NETBIOS;
+ packet->questions[0].question_class = NBT_QCLASS_IP;
+
+ packet->additional = talloc_array(packet, struct nbt_res_rec, 1);
+ if (packet->additional == NULL) goto failed;
+
+ packet->additional[0].name = io->in.name;
+ packet->additional[0].rr_type = NBT_QTYPE_NETBIOS;
+ packet->additional[0].rr_class = NBT_QCLASS_IP;
+ packet->additional[0].ttl = io->in.ttl;
+ packet->additional[0].rdata.netbios.length = 6;
+ packet->additional[0].rdata.netbios.addresses = talloc_array(packet->additional,
+ struct nbt_rdata_address, 1);
+ if (packet->additional[0].rdata.netbios.addresses == NULL) goto failed;
+ packet->additional[0].rdata.netbios.addresses[0].nb_flags = io->in.nb_flags;
+ packet->additional[0].rdata.netbios.addresses[0].ipaddr = htonl(inet_addr(io->in.address));
+
+ req = nbt_name_request_send(nbtsock, io->in.dest_addr, lp_nbt_port(), packet,
+ timeval_current_ofs(io->in.timeout, 0), False);
+ if (req == NULL) goto failed;
+
+ talloc_steal(req, packet);
+
+ return req;
+
+failed:
+ talloc_free(packet);
+ return NULL;
+}
+
+/*
+ wait for a registration reply
+*/
+NTSTATUS nbt_name_register_recv(struct nbt_name_request *req,
+ TALLOC_CTX *mem_ctx, struct nbt_name_register *io)
+{
+ NTSTATUS status;
+ struct nbt_name_packet *packet;
+ const char *addr;
+ struct in_addr in;
+
+ status = nbt_name_request_recv(req);
+ if (!NT_STATUS_IS_OK(status) ||
+ req->num_replies == 0) {
+ talloc_free(req);
+ return status;
+ }
+
+ packet = req->replies[0].packet;
+ io->out.reply_from = talloc_steal(mem_ctx, req->replies[0].reply_addr);
+
+ if (packet->ancount != 1 ||
+ packet->answers[0].rr_type != NBT_QTYPE_NETBIOS ||
+ packet->answers[0].rr_class != NBT_QCLASS_IP) {
+ talloc_free(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ io->out.rcode = packet->operation & NBT_RCODE;
+ io->out.name = packet->answers[0].name;
+ if (packet->answers[0].rdata.netbios.length < 6) {
+ talloc_free(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ in.s_addr = htonl(packet->answers[0].rdata.netbios.addresses[0].ipaddr);
+ addr = inet_ntoa(in);
+ if (addr == NULL) {
+ talloc_free(req);
+ return NT_STATUS_NO_MEMORY;
+ }
+ io->out.reply_addr = talloc_strdup(mem_ctx, addr);
+ talloc_steal(mem_ctx, io->out.name.name);
+ talloc_steal(mem_ctx, io->out.name.scope);
+
+ talloc_free(req);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ synchronous name registration request
+*/
+NTSTATUS nbt_name_register(struct nbt_name_socket *nbtsock,
+ TALLOC_CTX *mem_ctx, struct nbt_name_register *io)
+{
+ struct nbt_name_request *req = nbt_name_register_send(nbtsock, io);
+ return nbt_name_register_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/nbt/nbtsocket.c b/source4/libcli/nbt/nbtsocket.c
index f1964b7158..90b4f6e029 100644
--- a/source4/libcli/nbt/nbtsocket.c
+++ b/source4/libcli/nbt/nbtsocket.c
@@ -41,7 +41,8 @@ static int nbt_name_request_destructor(void *ptr)
if (req->state == NBT_REQUEST_WAIT) {
req->nbtsock->num_pending--;
}
- if (req->request->name_trn_id != 0) {
+ if (req->request->name_trn_id != 0 &&
+ !(req->request->operation & NBT_FLAG_REPLY)) {
idr_remove(req->nbtsock->idr, req->request->name_trn_id);
req->request->name_trn_id = 0;
}
@@ -65,7 +66,7 @@ static int nbt_name_request_destructor(void *ptr)
static void nbt_name_socket_send(struct nbt_name_socket *nbtsock)
{
struct nbt_name_request *req = nbtsock->send_queue;
- TALLOC_CTX *tmp_ctx = talloc_new(req);
+ TALLOC_CTX *tmp_ctx = talloc_new(nbtsock);
NTSTATUS status;
while ((req = nbtsock->send_queue)) {
@@ -98,9 +99,13 @@ static void nbt_name_socket_send(struct nbt_name_socket *nbtsock)
}
DLIST_REMOVE(nbtsock->send_queue, req);
- req->state = NBT_REQUEST_WAIT;
- nbtsock->fde->flags |= EVENT_FD_READ;
- nbtsock->num_pending++;
+ if (req->request->operation & NBT_FLAG_REPLY) {
+ talloc_free(req);
+ } else {
+ req->state = NBT_REQUEST_WAIT;
+ nbtsock->fde->flags |= EVENT_FD_READ;
+ nbtsock->num_pending++;
+ }
}
nbtsock->fde->flags &= ~EVENT_FD_WRITE;
@@ -317,7 +322,8 @@ struct nbt_name_request *nbt_name_request_send(struct nbt_name_socket *nbtsock,
if (req == NULL) goto failed;
req->nbtsock = nbtsock;
- req->dest_addr = dest_addr;
+ req->dest_addr = talloc_strdup(req, dest_addr);
+ if (req->dest_addr == NULL) goto failed;
req->dest_port = dest_port;
req->request = talloc_reference(req, request);
req->allow_multiple_replies = allow_multiple_replies;
@@ -361,6 +367,39 @@ failed:
return NULL;
}
+
+/*
+ send off a nbt name reply
+*/
+NTSTATUS nbt_name_reply_send(struct nbt_name_socket *nbtsock,
+ const char *dest_addr, int dest_port,
+ struct nbt_name_packet *request)
+{
+ struct nbt_name_request *req;
+
+ req = talloc_zero(nbtsock, struct nbt_name_request);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ req->nbtsock = nbtsock;
+ req->dest_addr = talloc_strdup(req, dest_addr);
+ if (req->dest_addr == NULL) goto failed;
+ req->dest_port = dest_port;
+ req->request = talloc_reference(req, request);
+ req->state = NBT_REQUEST_SEND;
+
+ talloc_set_destructor(req, nbt_name_request_destructor);
+
+ DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
+
+ nbtsock->fde->flags |= EVENT_FD_WRITE;
+
+ return NT_STATUS_OK;
+
+failed:
+ talloc_free(req);
+ return NT_STATUS_NO_MEMORY;
+}
+
/*
wait for a nbt request to complete
*/
@@ -392,7 +431,6 @@ NTSTATUS nbt_set_incoming_handler(struct nbt_name_socket *nbtsock,
nbtsock->incoming.handler = handler;
nbtsock->incoming.private = private;
nbtsock->fde->flags |= EVENT_FD_READ;
- socket_set_option(nbtsock->sock, "SO_BROADCAST", "1");
return NT_STATUS_OK;
}
diff --git a/source4/librpc/idl/nbt.idl b/source4/librpc/idl/nbt.idl
index 429a35267c..35fc7d90c4 100644
--- a/source4/librpc/idl/nbt.idl
+++ b/source4/librpc/idl/nbt.idl
@@ -103,8 +103,12 @@ interface nbt
typedef struct {
nb_flags nb_flags;
uint32 ipaddr;
- } nbt_rdata_netbios;
+ } nbt_rdata_address;
+ typedef struct {
+ uint16 length;
+ nbt_rdata_address addresses[length/6];
+ } nbt_rdata_netbios;
typedef struct {
uint8 unit_id[6];
@@ -135,14 +139,16 @@ interface nbt
nb_flags nb_flags;
} nbt_status_name;
- typedef struct {
+ typedef [gensize] struct {
+ [value(ndr_size_nbt_rdata_status(r, ndr->flags)-2)] uint16 length;
uint8 num_names;
nbt_status_name names[num_names];
nbt_statistics statistics;
} nbt_rdata_status;
typedef struct {
- [flag(NDR_REMAINING)] DATA_BLOB data;
+ uint16 length;
+ uint8 data[length];
} nbt_rdata_data;
typedef [nodiscriminant] union {
@@ -156,7 +162,7 @@ interface nbt
nbt_qtype rr_type;
nbt_qclass rr_class;
uint32 ttl;
- [subcontext(2),switch_is(rr_type)] nbt_rdata rdata;
+ [switch_is(rr_type)] nbt_rdata rdata;
} nbt_res_rec;
typedef [flag(NDR_NOALIGN|NDR_BIG_ENDIAN|NDR_PAHEX),public] struct {
diff --git a/source4/nbt_server/config.mk b/source4/nbt_server/config.mk
index 093cb61864..e3f52a64e6 100644
--- a/source4/nbt_server/config.mk
+++ b/source4/nbt_server/config.mk
@@ -6,7 +6,11 @@
INIT_OBJ_FILES = \
nbt_server/nbt_server.o
ADD_OBJ_FILES = \
- nbt_server/interfaces.o
+ nbt_server/interfaces.o \
+ nbt_server/register.o \
+ nbt_server/query.o \
+ nbt_server/winsserver.o \
+ nbt_server/packet.o
REQUIRED_SUBSYSTEMS = \
LIBCLI_NBT
# End SUBSYSTEM SMB
diff --git a/source4/nbt_server/interfaces.c b/source4/nbt_server/interfaces.c
index 18893e179b..303802e2cd 100644
--- a/source4/nbt_server/interfaces.c
+++ b/source4/nbt_server/interfaces.c
@@ -24,13 +24,72 @@
#include "dlinklist.h"
#include "nbt_server/nbt_server.h"
#include "smbd/service_task.h"
-#include "libcli/nbt/libnbt.h"
+
+/*
+ find a registered name on an interface
+*/
+struct nbt_iface_name *nbt_find_iname(struct nbt_interface *iface, struct nbt_name *name,
+ uint16_t nb_flags)
+{
+ struct nbt_iface_name *iname;
+ for (iname=iface->names;iname;iname=iname->next) {
+ if (iname->name.type == name->type &&
+ StrCaseCmp(name->name, iname->name.name) == 0 &&
+ ((iname->nb_flags & nb_flags) == nb_flags)) {
+ return iname;
+ }
+ }
+ return NULL;
+}
+
+/*
+ see if a src address matches an interface
+*/
+static BOOL nbt_iface_match(struct nbt_interface *iface, const char *src_address)
+{
+ struct ipv4_addr ip1, ip2, mask;
+ ip1 = interpret_addr2(iface->ip_address);
+ ip2 = interpret_addr2(src_address);
+ mask = interpret_addr2(iface->netmask);
+ return same_net(ip1, ip2, mask);
+}
+
+
+/*
+ find the appropriate interface for a incoming packet. If a local interface isn't
+ found then the general broadcast interface is used
+*/
+struct nbt_interface *nbt_iface_find(struct nbt_name_socket *nbtsock, const char *src_address)
+{
+ struct nbt_interface *iface = talloc_get_type(nbtsock->incoming.private, struct nbt_interface);
+ struct nbt_server *nbtsrv = iface->nbtsrv;
+
+ /* it might have been received by one of our specific bound
+ addresses */
+ if (iface != nbtsrv->bcast_interface) {
+ return iface;
+ }
+
+ /* it came in on our broadcast interface - try to find a match */
+ for (iface=nbtsrv->interfaces;iface;iface=iface->next) {
+ if (nbt_iface_match(iface, src_address)) {
+ return iface;
+ }
+ }
+
+ /* it didn't match any specific interface - use our general broadcast interface */
+ return nbtsrv->bcast_interface;
+}
+
/*
start listening on the given address
*/
static NTSTATUS nbt_add_socket(struct nbt_server *nbtsrv,
- const char *address, const char *bcast)
+ const char *bind_address,
+ const char *address,
+ const char *bcast,
+ const char *netmask)
{
struct nbt_interface *iface;
NTSTATUS status;
@@ -38,14 +97,16 @@ static NTSTATUS nbt_add_socket(struct nbt_server *nbtsrv,
iface = talloc(nbtsrv, struct nbt_interface);
NT_STATUS_HAVE_NO_MEMORY(iface);
- iface->nbtsrv = nbtsrv;
+ iface->nbtsrv = nbtsrv;
iface->bcast_address = talloc_steal(iface, bcast);
- iface->ip_address = talloc_steal(iface, address);
+ iface->ip_address = talloc_steal(iface, address);
+ iface->netmask = talloc_steal(iface, netmask);
+ iface->names = NULL;
iface->nbtsock = nbt_name_socket_init(iface, nbtsrv->task->event_ctx);
NT_STATUS_HAVE_NO_MEMORY(iface->ip_address);
- status = socket_listen(iface->nbtsock->sock, address, lp_nbt_port(), 0, 0);
+ status = socket_listen(iface->nbtsock->sock, bind_address, lp_nbt_port(), 0, 0);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Failed to bind to %s:%d - %s\n",
address, lp_nbt_port(), nt_errstr(status)));
@@ -53,7 +114,13 @@ static NTSTATUS nbt_add_socket(struct nbt_server *nbtsrv,
return status;
}
- DLIST_ADD(nbtsrv->interfaces, iface);
+ socket_set_option(iface->nbtsock->sock, "SO_BROADCAST", "1");
+
+ if (strcmp(netmask, "0.0.0.0") == 0) {
+ DLIST_ADD(nbtsrv->bcast_interface, iface);
+ } else {
+ DLIST_ADD(nbtsrv->interfaces, iface);
+ }
return NT_STATUS_OK;
}
@@ -68,17 +135,29 @@ NTSTATUS nbt_startup_interfaces(struct nbt_server *nbtsrv)
int i;
TALLOC_CTX *tmp_ctx = talloc_new(nbtsrv);
NTSTATUS status;
+ const char *primary_address;
+
+ /* the primary address is the address we will return for non-WINS queries
+ not made on a specific interface */
+ if (num_interfaces > 0) {
+ primary_address = talloc_strdup(tmp_ctx, sys_inet_ntoa(*iface_n_ip(0)));
+ } else {
+ primary_address = sys_inet_ntoa(interpret_addr2(lp_netbios_name()));
+ }
status = nbt_add_socket(nbtsrv,
- talloc_strdup(tmp_ctx, "0.0.0.0"),
- talloc_strdup(tmp_ctx, "255.255.255.255"));
+ "0.0.0.0",
+ primary_address,
+ talloc_strdup(tmp_ctx, "255.255.255.255"),
+ talloc_strdup(tmp_ctx, "0.0.0.0"));
NT_STATUS_NOT_OK_RETURN(status);
for (i=0; i<num_interfaces; i++) {
const char *address = talloc_strdup(tmp_ctx, sys_inet_ntoa(*iface_n_ip(i)));
const char *bcast = talloc_strdup(tmp_ctx, sys_inet_ntoa(*iface_n_bcast(i)));
+ const char *netmask = talloc_strdup(tmp_ctx, sys_inet_ntoa(*iface_n_netmask(i)));
- status = nbt_add_socket(nbtsrv, address, bcast);
+ status = nbt_add_socket(nbtsrv, address, address, bcast, netmask);
NT_STATUS_NOT_OK_RETURN(status);
}
diff --git a/source4/nbt_server/nbt_server.c b/source4/nbt_server/nbt_server.c
index d05a31e421..603ec2a583 100644
--- a/source4/nbt_server/nbt_server.c
+++ b/source4/nbt_server/nbt_server.c
@@ -22,7 +22,6 @@
#include "includes.h"
#include "events.h"
-#include "libcli/nbt/libnbt.h"
#include "smbd/service_task.h"
#include "nbt_server/nbt_server.h"
@@ -36,9 +35,12 @@ static void nbt_request_handler(struct nbt_name_socket *nbtsock,
{
struct nbt_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbt_interface);
- DEBUG(0,("nbtd request from %s:%d\n", src_address, src_port));
- NDR_PRINT_DEBUG(nbt_name_packet, packet);
+ switch (packet->operation & NBT_OPCODE) {
+ case NBT_OPCODE_QUERY:
+ nbt_request_query(nbtsock, packet, src_address, src_port);
+ break;
+ }
}
@@ -57,8 +59,9 @@ static void nbtd_task_init(struct task_server *task)
return;
}
- nbtsrv->task = task;
- nbtsrv->interfaces = NULL;
+ nbtsrv->task = task;
+ nbtsrv->interfaces = NULL;
+ nbtsrv->bcast_interface = NULL;
/* start listening on the configured network interfaces */
status = nbt_startup_interfaces(nbtsrv);
@@ -71,6 +74,11 @@ static void nbtd_task_init(struct task_server *task)
for (iface=nbtsrv->interfaces;iface;iface=iface->next) {
nbt_set_incoming_handler(iface->nbtsock, nbt_request_handler, iface);
}
+ nbt_set_incoming_handler(nbtsrv->bcast_interface->nbtsock, nbt_request_handler,
+ nbtsrv->bcast_interface);
+
+ /* start the process of registering our names on all interfaces */
+ nbt_register_names(nbtsrv);
}
diff --git a/source4/nbt_server/nbt_server.h b/source4/nbt_server/nbt_server.h
index 9ef510fbb2..49a07ca95e 100644
--- a/source4/nbt_server/nbt_server.h
+++ b/source4/nbt_server/nbt_server.h
@@ -20,14 +20,30 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "libcli/nbt/libnbt.h"
+
+/*
+ a list of our registered names on each interface
+*/
+struct nbt_iface_name {
+ struct nbt_iface_name *next, *prev;
+ struct nbt_interface *iface;
+ struct nbt_name name;
+ uint16_t nb_flags;
+ struct timeval registration_time;
+ uint32_t ttl;
+};
+
/* a list of network interfaces we are listening on */
struct nbt_interface {
struct nbt_interface *next, *prev;
+ struct nbt_server *nbtsrv;
const char *ip_address;
const char *bcast_address;
+ const char *netmask;
struct nbt_name_socket *nbtsock;
- struct nbt_server *nbtsrv;
+ struct nbt_iface_name *names;
};
@@ -37,7 +53,11 @@ struct nbt_interface {
struct nbt_server {
struct task_server *task;
+ /* the list of local network interfaces */
struct nbt_interface *interfaces;
+
+ /* broadcast interface used for receiving packets only */
+ struct nbt_interface *bcast_interface;
};
diff --git a/source4/nbt_server/packet.c b/source4/nbt_server/packet.c
new file mode 100644
index 0000000000..53ccc4d93f
--- /dev/null
+++ b/source4/nbt_server/packet.c
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ packet utility functions
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 2 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
+ GNU General Public License for more details.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "dlinklist.h"
+#include "nbt_server/nbt_server.h"
+
+/*
+ we received a badly formed packet - log it
+*/
+void nbt_bad_packet(struct nbt_name_packet *packet,
+ const char *src_address, const char *reason)
+{
+ DEBUG(2,("nbtd: bad packet '%s' from %s\n", reason, src_address));
+ if (DEBUGLVL(5)) {
+ NDR_PRINT_DEBUG(nbt_name_packet, packet);
+ }
+}
+
diff --git a/source4/nbt_server/query.c b/source4/nbt_server/query.c
new file mode 100644
index 0000000000..c962a691c6
--- /dev/null
+++ b/source4/nbt_server/query.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ answer name queries
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 2 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
+ GNU General Public License for more details.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "dlinklist.h"
+#include "system/network.h"
+#include "nbt_server/nbt_server.h"
+
+/* check a condition on an incoming packet */
+#define NBT_ASSERT_PACKET(packet, src_address, test) do { \
+ if (!(test)) { \
+ nbt_bad_packet(packet, src_address, #test); \
+ return; \
+ } \
+} while (0)
+
+
+/*
+ send a name query reply
+*/
+static void nbt_name_query_reply(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *request_packet,
+ const char *src_address, int src_port,
+ struct nbt_name *name, uint32_t ttl,
+ uint16_t nb_flags, const char *address)
+{
+ struct nbt_name_packet *packet;
+
+ packet = talloc_zero(nbtsock, struct nbt_name_packet);
+ if (packet == NULL) return;
+
+ packet->name_trn_id = request_packet->name_trn_id;
+ packet->ancount = 1;
+ packet->operation =
+ NBT_FLAG_REPLY |
+ NBT_OPCODE_QUERY |
+ NBT_FLAG_AUTHORITIVE |
+ NBT_FLAG_RECURSION_DESIRED |
+ NBT_FLAG_RECURSION_AVAIL;
+
+ packet->answers = talloc_array(packet, struct nbt_res_rec, 1);
+ if (packet->answers == NULL) goto failed;
+
+ packet->answers[0].name = *name;
+ packet->answers[0].rr_type = NBT_QTYPE_NETBIOS;
+ packet->answers[0].rr_class = NBT_QCLASS_IP;
+ packet->answers[0].ttl = ttl;
+ packet->answers[0].rdata.netbios.length = 6;
+ packet->answers[0].rdata.netbios.addresses = talloc_array(packet->answers,
+ struct nbt_rdata_address, 1);
+ if (packet->answers[0].rdata.netbios.addresses == NULL) goto failed;
+ packet->answers[0].rdata.netbios.addresses[0].nb_flags = nb_flags;
+ packet->answers[0].rdata.netbios.addresses[0].ipaddr = htonl(inet_addr(address));
+
+ DEBUG(7,("Sending name query reply for %s<%02x> at %s to %s:%d\n",
+ name->name, name->type, src_address, address, src_port));
+
+ nbt_name_reply_send(nbtsock, src_address, src_port, packet);
+
+failed:
+ talloc_free(packet);
+}
+
+
+/*
+ answer a name query
+*/
+void nbt_request_query(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ const char *src_address, int src_port)
+{
+ struct nbt_interface *iface;
+ struct nbt_iface_name *iname;
+ struct nbt_name *name;
+
+ /* if its a WINS query then direct to our WINS server */
+ if ((packet->operation & NBT_FLAG_RECURSION_DESIRED) &&
+ !(packet->operation & NBT_FLAG_BROADCAST)) {
+ nbt_query_wins(nbtsock, packet, src_address, src_port);
+ return;
+ }
+
+ /* find the interface for this query */
+ iface = nbt_iface_find(nbtsock, src_address);
+
+ NBT_ASSERT_PACKET(packet, src_address, packet->qdcount == 1);
+ NBT_ASSERT_PACKET(packet, src_address, packet->questions[0].question_type == NBT_QTYPE_NETBIOS);
+ NBT_ASSERT_PACKET(packet, src_address, packet->questions[0].question_class == NBT_QCLASS_IP);
+
+ /* see if we have the requested name on this interface */
+ name = &packet->questions[0].name;
+
+ iname = nbt_find_iname(iface, name, NBT_NM_ACTIVE);
+ if (iname == NULL) {
+ DEBUG(7,("Query for %s<%02x> from %s - not found on %s\n",
+ name->name, name->type, src_address, iface->ip_address));
+ return;
+ }
+
+ nbt_name_query_reply(nbtsock, packet, src_address, src_port,
+ &iname->name, iname->ttl, iname->nb_flags,
+ iface->ip_address);
+}
diff --git a/source4/nbt_server/register.c b/source4/nbt_server/register.c
new file mode 100644
index 0000000000..9a416e37aa
--- /dev/null
+++ b/source4/nbt_server/register.c
@@ -0,0 +1,161 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ register our names
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 2 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
+ GNU General Public License for more details.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "dlinklist.h"
+#include "nbt_server/nbt_server.h"
+
+/*
+ start a timer to refresh this name
+*/
+static void nbt_start_refresh_timer(struct nbt_iface_name *iname)
+{
+}
+
+
+/*
+ a name registration has completed
+*/
+static void nbt_register_handler(struct nbt_name_request *req)
+{
+ struct nbt_iface_name *iname = talloc_get_type(req->async.private, struct nbt_iface_name);
+ NTSTATUS status;
+ struct nbt_name_register io;
+
+ status = nbt_name_register_recv(req, iname, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ /* good - nobody complained about our registration */
+ iname->nb_flags |= NBT_NM_ACTIVE;
+ DEBUG(3,("Registered %s<%02x> on interface %s\n",
+ iname->name.name, iname->name.type, iname->iface->bcast_address));
+ nbt_start_refresh_timer(iname);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("Error registering %s<%02x> on interface %s - %s\n",
+ iname->name.name, iname->name.type, iname->iface->bcast_address,
+ nt_errstr(status)));
+ return;
+ }
+
+ /* someone must have replied with an objection! */
+ iname->nb_flags |= NBT_NM_CONFLICT;
+
+ DEBUG(1,("Name conflict registering %s<%02x> on interface %s - rcode %d from %s for %s\n",
+ iname->name.name, iname->name.type, iname->iface->bcast_address,
+ io.out.rcode, io.out.reply_from, io.out.reply_addr));
+}
+
+
+/*
+ register a name on a network interface
+*/
+static void nbt_register_name_iface(struct nbt_interface *iface,
+ const char *name, enum nbt_name_type type,
+ uint16_t nb_flags)
+{
+ struct nbt_iface_name *iname;
+ const char *scope = lp_netbios_scope();
+ struct nbt_name_register io;
+ struct nbt_name_request *req;
+
+ iname = talloc(iface, struct nbt_iface_name);
+ if (!iname) return;
+
+ iname->iface = iface;
+ iname->name.name = talloc_strdup(iname, name);
+ iname->name.type = type;
+ if (scope && *scope) {
+ iname->name.scope = talloc_strdup(iname, scope);
+ } else {
+ iname->name.scope = NULL;
+ }
+ iname->nb_flags = nb_flags;
+ iname->ttl = lp_parm_int(-1, "nbtd", "bcast_ttl", 300000);
+ iname->registration_time = timeval_zero();
+
+ DLIST_ADD(iface->names, iname);
+
+ if (nb_flags & NBT_NM_PERMANENT) {
+ /* permanent names are not announced and are immediately active */
+ iname->nb_flags |= NBT_NM_ACTIVE;
+ iname->ttl = 0;
+ return;
+ }
+
+ /* setup a broadcast name registration request */
+ io.in.name = iname->name;
+ io.in.dest_addr = iface->bcast_address;
+ io.in.address = iface->ip_address;
+ io.in.nb_flags = nb_flags;
+ io.in.register_demand = False;
+ io.in.broadcast = True;
+ io.in.ttl = iname->ttl;
+ io.in.timeout = 1;
+
+ req = nbt_name_register_send(iface->nbtsock, &io);
+ if (req == NULL) return;
+
+ req->async.fn = nbt_register_handler;
+ req->async.private = iname;
+}
+
+
+/*
+ register one name on all our interfaces
+*/
+static void nbt_register_name(struct nbt_server *nbtsrv,
+ const char *name, enum nbt_name_type type,
+ uint16_t nb_flags)
+{
+ struct nbt_interface *iface;
+
+ /* register with all the local interfaces */
+ for (iface=nbtsrv->interfaces;iface;iface=iface->next) {
+ nbt_register_name_iface(iface, name, type, nb_flags);
+ }
+
+ /* register on our general broadcast interface as a permanent name */
+ nbt_register_name_iface(nbtsrv->bcast_interface, name, type, nb_flags | NBT_NM_PERMANENT);
+
+ /* TODO: register with our WINS servers */
+}
+
+
+/*
+ register our names on all interfaces
+*/
+void nbt_register_names(struct nbt_server *nbtsrv)
+{
+ uint16_t nb_flags = NBT_NODE_M;
+
+ /* note that we don't initially mark the names "ACTIVE". They are
+ marked active once registration is successful */
+ nbt_register_name(nbtsrv, lp_netbios_name(), NBT_NAME_CLIENT, nb_flags);
+ nbt_register_name(nbtsrv, lp_netbios_name(), NBT_NAME_USER, nb_flags);
+ nbt_register_name(nbtsrv, lp_netbios_name(), NBT_NAME_SERVER, nb_flags);
+ nbt_register_name(nbtsrv, lp_workgroup(), NBT_NAME_CLIENT, nb_flags | NBT_NM_GROUP);
+ nbt_register_name(nbtsrv, lp_workgroup(), NBT_NAME_SERVER, nb_flags | NBT_NM_GROUP);
+ nbt_register_name(nbtsrv, "__SAMBA__", NBT_NAME_CLIENT, nb_flags | NBT_NM_PERMANENT);
+ nbt_register_name(nbtsrv, "__SAMBA__", NBT_NAME_SERVER, nb_flags | NBT_NM_PERMANENT);
+}
diff --git a/source4/nbt_server/winsserver.c b/source4/nbt_server/winsserver.c
new file mode 100644
index 0000000000..a659772cb3
--- /dev/null
+++ b/source4/nbt_server/winsserver.c
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ core wins server handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 2 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
+ GNU General Public License for more details.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "dlinklist.h"
+#include "nbt_server/nbt_server.h"
+
+/*
+ answer a name query
+*/
+void nbt_query_wins(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ const char *src_address, int src_port)
+{
+ DEBUG(0,("WINS query from %s\n", src_address));
+}
diff --git a/source4/utils/nmblookup.c b/source4/utils/nmblookup.c
index 5881b25c79..824ce64298 100644
--- a/source4/utils/nmblookup.c
+++ b/source4/utils/nmblookup.c
@@ -141,6 +141,7 @@ static NTSTATUS do_node_query(struct nbt_name_socket *nbtsock,
{
struct nbt_name_query io;
NTSTATUS status;
+ int i;
io.in.name.name = node_name;
io.in.name.type = node_type;
@@ -151,14 +152,16 @@ static NTSTATUS do_node_query(struct nbt_name_socket *nbtsock,
io.in.timeout = 3;
status = nbt_name_query(nbtsock, nbtsock, &io);
- if (NT_STATUS_IS_OK(status)) {
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ for (i=0;i<io.out.num_addrs;i++) {
printf("%s %s<%02x>\n",
- io.out.reply_addr,
+ io.out.reply_addrs[i],
io.out.name.name,
io.out.name.type);
- if (options.node_status) {
- do_node_status(nbtsock, io.out.reply_addr);
- }
+ }
+ if (options.node_status && io.out.num_addrs > 0) {
+ do_node_status(nbtsock, io.out.reply_addrs[0]);
}
return status;