diff options
author | Andrew Tridgell <tridge@samba.org> | 2005-01-31 01:57:58 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:09:23 -0500 |
commit | 414f6c80b22b128c25d947d62f6b5d1ec13091b6 (patch) | |
tree | da147a6c2802a7af79b5dfcdf3d3f6508287520e | |
parent | 37449657a8d335097b3f3559f8b5bf084b50b85a (diff) | |
download | samba-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)
-rw-r--r-- | source4/include/structs.h | 3 | ||||
-rw-r--r-- | source4/lib/netif/interface.c | 14 | ||||
-rw-r--r-- | source4/lib/util_strlist.c | 168 | ||||
-rw-r--r-- | source4/libcli/config.mk | 3 | ||||
-rw-r--r-- | source4/libcli/nbt/libnbt.h | 23 | ||||
-rw-r--r-- | source4/libcli/nbt/namequery.c | 27 | ||||
-rw-r--r-- | source4/libcli/nbt/nameregister.c | 140 | ||||
-rw-r--r-- | source4/libcli/nbt/nbtsocket.c | 52 | ||||
-rw-r--r-- | source4/librpc/idl/nbt.idl | 14 | ||||
-rw-r--r-- | source4/nbt_server/config.mk | 6 | ||||
-rw-r--r-- | source4/nbt_server/interfaces.c | 97 | ||||
-rw-r--r-- | source4/nbt_server/nbt_server.c | 18 | ||||
-rw-r--r-- | source4/nbt_server/nbt_server.h | 22 | ||||
-rw-r--r-- | source4/nbt_server/packet.c | 38 | ||||
-rw-r--r-- | source4/nbt_server/query.c | 122 | ||||
-rw-r--r-- | source4/nbt_server/register.c | 161 | ||||
-rw-r--r-- | source4/nbt_server/winsserver.c | 35 | ||||
-rw-r--r-- | source4/utils/nmblookup.c | 13 |
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; |