diff options
Diffstat (limited to 'libcli/nbt')
-rw-r--r-- | libcli/nbt/libnbt.h | 377 | ||||
-rw-r--r-- | libcli/nbt/lmhosts.c | 243 | ||||
-rw-r--r-- | libcli/nbt/man/nmblookup4.1.xml | 213 | ||||
-rw-r--r-- | libcli/nbt/namequery.c | 234 | ||||
-rw-r--r-- | libcli/nbt/namerefresh.c | 347 | ||||
-rw-r--r-- | libcli/nbt/nameregister.c | 514 | ||||
-rw-r--r-- | libcli/nbt/namerelease.c | 134 | ||||
-rw-r--r-- | libcli/nbt/nbt_proto.h | 65 | ||||
-rw-r--r-- | libcli/nbt/nbtname.c | 500 | ||||
-rw-r--r-- | libcli/nbt/nbtsocket.c | 564 | ||||
-rw-r--r-- | libcli/nbt/pynbt.c | 447 | ||||
-rw-r--r-- | libcli/nbt/tools/nmblookup.c | 477 | ||||
-rw-r--r-- | libcli/nbt/wscript_build | 32 |
13 files changed, 4147 insertions, 0 deletions
diff --git a/libcli/nbt/libnbt.h b/libcli/nbt/libnbt.h new file mode 100644 index 0000000..204484b --- /dev/null +++ b/libcli/nbt/libnbt.h @@ -0,0 +1,377 @@ +/* + Unix SMB/CIFS implementation. + + a raw async NBT library + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __LIBNBT_H__ +#define __LIBNBT_H__ + +#include "librpc/gen_ndr/nbt.h" +#include "librpc/ndr/libndr.h" + +/* + possible states for pending requests +*/ +enum nbt_request_state {NBT_REQUEST_SEND, + NBT_REQUEST_WAIT, + NBT_REQUEST_DONE, + NBT_REQUEST_TIMEOUT, + NBT_REQUEST_ERROR}; + +/* + a nbt name request +*/ +struct nbt_name_request { + struct nbt_name_request *next, *prev; + + enum nbt_request_state state; + + NTSTATUS status; + + /* the socket this was on */ + struct nbt_name_socket *nbtsock; + + /* where to send the request */ + struct socket_address *dest; + + /* timeout between retries */ + int timeout; + + /* how many retries to send on timeout */ + int num_retries; + + /* whether we have received a WACK */ + bool received_wack; + + /* the timeout event */ + struct tevent_timer *te; + + /* the name transaction id */ + uint16_t name_trn_id; + + /* is it a reply? */ + bool is_reply; + + /* the encoded request */ + DATA_BLOB encoded; + + /* shall we allow multiple replies? */ + bool allow_multiple_replies; + + unsigned int num_replies; + struct nbt_name_reply { + struct nbt_name_packet *packet; + struct socket_address *dest; + } *replies; + + /* information on what to do on completion */ + struct { + void (*fn)(struct nbt_name_request *); + void *private_data; + } async; +}; + + + +/* + context structure for operations on name queries +*/ +struct nbt_name_socket { + struct socket_context *sock; + struct tevent_context *event_ctx; + + /* a queue of requests pending to be sent */ + struct nbt_name_request *send_queue; + + /* the fd event */ + struct tevent_fd *fde; + + /* mapping from name_trn_id to pending event */ + struct idr_context *idr; + + /* how many requests are waiting for a reply */ + uint16_t num_pending; + + /* what to do with incoming request packets */ + struct { + void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *, + struct socket_address *); + void *private_data; + } incoming; + + /* what to do with unexpected replies */ + struct { + void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *, + struct socket_address *); + void *private_data; + } unexpected; +}; + + +/* a simple name query */ +struct nbt_name_query { + struct { + struct nbt_name name; + const char *dest_addr; + uint16_t dest_port; + bool broadcast; + bool wins_lookup; + int timeout; /* in seconds */ + int retries; + } in; + struct { + const char *reply_from; + struct nbt_name name; + int16_t num_addrs; + const char **reply_addrs; + } out; +}; + +/* a simple name status query */ +struct nbt_name_status { + struct { + struct nbt_name name; + const char *dest_addr; + uint16_t dest_port; + int timeout; /* in seconds */ + int retries; + } in; + struct { + const char *reply_from; + struct nbt_name name; + struct nbt_rdata_status status; + } out; +}; + +/* a name registration request */ +struct nbt_name_register { + struct { + struct nbt_name name; + const char *dest_addr; + uint16_t dest_port; + const char *address; + uint16_t nb_flags; + bool register_demand; + bool broadcast; + bool multi_homed; + uint32_t ttl; + int timeout; /* in seconds */ + int retries; + } in; + struct { + const char *reply_from; + struct nbt_name name; + const char *reply_addr; + uint8_t rcode; + } out; +}; + +/* a send 3 times then demand name broadcast name registration */ +struct nbt_name_register_bcast { + struct { + struct nbt_name name; + const char *dest_addr; + uint16_t dest_port; + const char *address; + uint16_t nb_flags; + uint32_t ttl; + } in; +}; + + +/* wins name register with multiple wins servers to try and multiple + addresses to register */ +struct nbt_name_register_wins { + struct { + struct nbt_name name; + const char **wins_servers; + uint16_t wins_port; + const char **addresses; + uint16_t nb_flags; + uint32_t ttl; + } in; + struct { + const char *wins_server; + uint8_t rcode; + } out; +}; + + + +/* a name refresh request */ +struct nbt_name_refresh { + struct { + struct nbt_name name; + const char *dest_addr; + uint16_t dest_port; + const char *address; + uint16_t nb_flags; + bool broadcast; + uint32_t ttl; + int timeout; /* in seconds */ + int retries; + } in; + struct { + const char *reply_from; + struct nbt_name name; + const char *reply_addr; + uint8_t rcode; + } out; +}; + +/* wins name refresh with multiple wins servers to try and multiple + addresses to register */ +struct nbt_name_refresh_wins { + struct { + struct nbt_name name; + const char **wins_servers; + uint16_t wins_port; + const char **addresses; + uint16_t nb_flags; + uint32_t ttl; + } in; + struct { + const char *wins_server; + uint8_t rcode; + } out; +}; + + +/* a name release request */ +struct nbt_name_release { + struct { + struct nbt_name name; + const char *dest_addr; + uint16_t dest_port; + const char *address; + uint16_t nb_flags; + bool broadcast; + int timeout; /* in seconds */ + int retries; + } in; + struct { + const char *reply_from; + struct nbt_name name; + const char *reply_addr; + uint8_t rcode; + } out; +}; + +struct nbt_name_socket *nbt_name_socket_init(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx); +void nbt_name_socket_handle_response_packet(struct nbt_name_request *req, + struct nbt_name_packet *packet, + struct socket_address *src); +struct nbt_name_request *nbt_name_query_send(struct nbt_name_socket *nbtsock, + struct nbt_name_query *io); +NTSTATUS nbt_name_query_recv(struct nbt_name_request *req, + TALLOC_CTX *mem_ctx, struct nbt_name_query *io); +NTSTATUS nbt_name_query(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, struct nbt_name_query *io); +struct nbt_name_request *nbt_name_status_send(struct nbt_name_socket *nbtsock, + struct nbt_name_status *io); +NTSTATUS nbt_name_status_recv(struct nbt_name_request *req, + TALLOC_CTX *mem_ctx, struct nbt_name_status *io); +NTSTATUS nbt_name_status(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, struct nbt_name_status *io); + +NTSTATUS nbt_name_dup(TALLOC_CTX *mem_ctx, + const struct nbt_name *name, + struct nbt_name *newname); +NTSTATUS nbt_name_to_blob(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct nbt_name *name); +NTSTATUS nbt_name_from_blob(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, struct nbt_name *name); +void nbt_choose_called_name(TALLOC_CTX *mem_ctx, struct nbt_name *n, const char *name, int type); +char *nbt_name_string(TALLOC_CTX *mem_ctx, const struct nbt_name *name); +NTSTATUS nbt_name_register(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, struct nbt_name_register *io); +NTSTATUS nbt_name_refresh(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, struct nbt_name_refresh *io); +NTSTATUS nbt_name_release(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, struct nbt_name_release *io); +NTSTATUS nbt_name_register_wins(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, + struct nbt_name_register_wins *io); +NTSTATUS nbt_name_refresh_wins(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, + struct nbt_name_refresh_wins *io); +NTSTATUS nbt_name_register_recv(struct nbt_name_request *req, + TALLOC_CTX *mem_ctx, struct nbt_name_register *io); +struct nbt_name_request *nbt_name_register_send(struct nbt_name_socket *nbtsock, + struct nbt_name_register *io); +NTSTATUS nbt_name_release_recv(struct nbt_name_request *req, + TALLOC_CTX *mem_ctx, struct nbt_name_release *io); + +struct nbt_name_request *nbt_name_release_send(struct nbt_name_socket *nbtsock, + struct nbt_name_release *io); + +NTSTATUS nbt_name_refresh_recv(struct nbt_name_request *req, + TALLOC_CTX *mem_ctx, struct nbt_name_refresh *io); + +NTSTATUS nbt_set_incoming_handler(struct nbt_name_socket *nbtsock, + void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *, + struct socket_address *), + void *private_data); +NTSTATUS nbt_set_unexpected_handler(struct nbt_name_socket *nbtsock, + void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *, + struct socket_address *), + void *private_data); +NTSTATUS nbt_name_reply_send(struct nbt_name_socket *nbtsock, + struct socket_address *dest, + struct nbt_name_packet *request); + + +NDR_SCALAR_PTR_PROTO(wrepl_nbt_name, struct nbt_name) +NDR_BUFFER_PROTO(nbt_name, struct nbt_name) +NTSTATUS nbt_rcode_to_ntstatus(uint8_t rcode); + +struct tevent_context; +struct tevent_req; +struct tevent_req *nbt_name_register_bcast_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct nbt_name_socket *nbtsock, + struct nbt_name_register_bcast *io); +NTSTATUS nbt_name_register_bcast_recv(struct tevent_req *req); +struct tevent_req *nbt_name_register_wins_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct nbt_name_socket *nbtsock, + struct nbt_name_register_wins *io); +NTSTATUS nbt_name_register_wins_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct nbt_name_register_wins *io); +struct tevent_req *nbt_name_refresh_wins_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct nbt_name_socket *nbtsock, + struct nbt_name_refresh_wins *io); +NTSTATUS nbt_name_refresh_wins_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct nbt_name_refresh_wins *io); + +FILE *startlmhosts(const char *fname); +bool getlmhostsent(TALLOC_CTX *ctx, FILE *fp, char **pp_name, int *name_type, + struct sockaddr_storage *pss); +void endlmhosts(FILE *fp); + +NTSTATUS resolve_lmhosts_file_as_sockaddr(TALLOC_CTX *mem_ctx, + const char *lmhosts_file, + const char *name, + int name_type, + struct sockaddr_storage **return_iplist, + size_t *return_count); + +#endif /* __LIBNBT_H__ */ diff --git a/libcli/nbt/lmhosts.c b/libcli/nbt/lmhosts.c new file mode 100644 index 0000000..dd06e70 --- /dev/null +++ b/libcli/nbt/lmhosts.c @@ -0,0 +1,243 @@ +/* + Unix SMB/CIFS implementation. + + manipulate nbt name structures + + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jeremy Allison 2007 + Copyright (C) Andrew Bartlett 2009. + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/util/util_net.h" +#include "system/filesys.h" +#include "system/network.h" +#include "../libcli/nbt/libnbt.h" + +/******************************************************** + Start parsing the lmhosts file. +*********************************************************/ + +FILE *startlmhosts(const char *fname) +{ + FILE *fp = fopen(fname, "r"); + if (!fp) { + DEBUG(4,("startlmhosts: Can't open lmhosts file %s. " + "Error was %s\n", + fname, strerror(errno))); + return NULL; + } + return fp; +} + +/******************************************************** + Parse the next line in the lmhosts file. +*********************************************************/ + +bool getlmhostsent(TALLOC_CTX *ctx, FILE *fp, char **pp_name, int *name_type, + struct sockaddr_storage *pss) +{ + char line[1024]; + + *pp_name = NULL; + + while(!feof(fp) && !ferror(fp)) { + char *ip = NULL; + char *flags = NULL; + char *extra = NULL; + char *name = NULL; + const char *ptr; + char *ptr1 = NULL; + int count = 0; + + *name_type = -1; + + if (!fgets_slash(NULL,line,sizeof(line),fp)) { + continue; + } + + if (*line == '#') { + continue; + } + + ptr = line; + + if (next_token_talloc(ctx, &ptr, &ip, NULL)) + ++count; + if (next_token_talloc(ctx, &ptr, &name, NULL)) + ++count; + if (next_token_talloc(ctx, &ptr, &flags, NULL)) + ++count; + if (next_token_talloc(ctx, &ptr, &extra, NULL)) + ++count; + + if (count <= 0) + continue; + + if (count > 0 && count < 2) { + DEBUG(0,("getlmhostsent: Ill formed hosts line [%s]\n", + line)); + continue; + } + + if (count >= 4) { + DEBUG(0,("getlmhostsent: too many columns " + "in lmhosts file (obsolete syntax)\n")); + continue; + } + + if (!flags) { + flags = talloc_strdup(ctx, ""); + if (!flags) { + continue; + } + } + + DEBUG(4, ("getlmhostsent: lmhost entry: %s %s %s\n", + ip, name, flags)); + + if (strchr_m(flags,'G') || strchr_m(flags,'S')) { + DEBUG(0,("getlmhostsent: group flag " + "in lmhosts ignored (obsolete)\n")); + continue; + } + + if (!interpret_string_addr(pss, ip, AI_NUMERICHOST)) { + DEBUG(0,("getlmhostsent: invalid address " + "%s.\n", ip)); + } + + /* Extra feature. If the name ends in '#XX', + * where XX is a hex number, then only add that name type. */ + if((ptr1 = strchr_m(name, '#')) != NULL) { + char *endptr; + ptr1++; + + *name_type = (int)strtol(ptr1, &endptr, 16); + if(!*ptr1 || (endptr == ptr1)) { + DEBUG(0,("getlmhostsent: invalid name " + "%s containing '#'.\n", name)); + continue; + } + + *(--ptr1) = '\0'; /* Truncate at the '#' */ + } + + *pp_name = talloc_strdup(ctx, name); + if (!*pp_name) { + return false; + } + return true; + } + + return false; +} + +/******************************************************** + Finish parsing the lmhosts file. +*********************************************************/ + +void endlmhosts(FILE *fp) +{ + fclose(fp); +} + +/******************************************************** + Resolve via "lmhosts" method. +*********************************************************/ + +NTSTATUS resolve_lmhosts_file_as_sockaddr(TALLOC_CTX *mem_ctx, + const char *lmhosts_file, + const char *name, + int name_type, + struct sockaddr_storage **return_iplist, + size_t *return_count) +{ + /* + * "lmhosts" means parse the local lmhosts file. + */ + + FILE *fp; + char *lmhost_name = NULL; + int name_type2; + struct sockaddr_storage return_ss; + NTSTATUS status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + TALLOC_CTX *ctx = NULL; + size_t ret_count = 0; + struct sockaddr_storage *iplist = NULL; + + DEBUG(3,("resolve_lmhosts: " + "Attempting lmhosts lookup for name %s<0x%x>\n", + name, name_type)); + + fp = startlmhosts(lmhosts_file); + + if ( fp == NULL ) + return NT_STATUS_NO_SUCH_FILE; + + ctx = talloc_new(mem_ctx); + if (!ctx) { + endlmhosts(fp); + return NT_STATUS_NO_MEMORY; + } + + while (getlmhostsent(ctx, fp, &lmhost_name, &name_type2, &return_ss)) { + + if (!strequal(name, lmhost_name)) { + TALLOC_FREE(lmhost_name); + continue; + } + + if ((name_type2 != -1) && (name_type != name_type2)) { + TALLOC_FREE(lmhost_name); + continue; + } + + /* wrap check. */ + if (ret_count + 1 < ret_count) { + TALLOC_FREE(ctx); + endlmhosts(fp); + return NT_STATUS_INVALID_PARAMETER; + } + iplist = talloc_realloc(ctx, iplist, + struct sockaddr_storage, + ret_count+1); + + if (iplist == NULL) { + TALLOC_FREE(ctx); + endlmhosts(fp); + DEBUG(3,("resolve_lmhosts: talloc_realloc fail !\n")); + return NT_STATUS_NO_MEMORY; + } + + iplist[ret_count] = return_ss; + ret_count += 1; + + /* we found something */ + status = NT_STATUS_OK; + + /* Multiple names only for DC lookup */ + if (name_type != 0x1c) + break; + } + + *return_count = ret_count; + *return_iplist = talloc_move(mem_ctx, &iplist); + TALLOC_FREE(ctx); + endlmhosts(fp); + return status; +} + diff --git a/libcli/nbt/man/nmblookup4.1.xml b/libcli/nbt/man/nmblookup4.1.xml new file mode 100644 index 0000000..b6fe48c --- /dev/null +++ b/libcli/nbt/man/nmblookup4.1.xml @@ -0,0 +1,213 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> +<refentry id="nmblookup4"> + +<refmeta> + <refentrytitle>nmblookup4</refentrytitle> + <manvolnum>1</manvolnum> + <refmiscinfo class="source">Samba</refmiscinfo> + <refmiscinfo class="manual">User Commands</refmiscinfo> + <refmiscinfo class="version">3.2</refmiscinfo> +</refmeta> + + +<refnamediv> + <refname>nmblookup4</refname> + <refpurpose>NetBIOS over TCP/IP client used to lookup NetBIOS + names</refpurpose> +</refnamediv> + +<refsynopsisdiv> + <cmdsynopsis> + <command>nmblookup4</command> + <arg choice="opt">-M</arg> + <arg choice="opt">-R</arg> + <arg choice="opt">-S</arg> + <arg choice="opt">-r</arg> + <arg choice="opt">-A</arg> + <arg choice="opt">-h</arg> + <arg choice="opt">-B <broadcast address></arg> + <arg choice="opt">-U <unicast address></arg> + <arg choice="opt">-d <debug level></arg> + <arg choice="opt">-s <smb config file></arg> + <arg choice="opt">-i <NetBIOS scope></arg> + <arg choice="opt">-T</arg> + <arg choice="opt">-f</arg> + <arg choice="req">name</arg> + </cmdsynopsis> +</refsynopsisdiv> + +<refsect1> + <title>DESCRIPTION</title> + + <para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle> + <manvolnum>7</manvolnum></citerefentry> suite.</para> + + <para><command>nmblookup4</command> is used to query NetBIOS names + and map them to IP addresses in a network using NetBIOS over TCP/IP + queries. The options allow the name queries to be directed at a + particular IP broadcast area or to a particular machine. All queries + are done over UDP.</para> +</refsect1> + +<refsect1> + <title>OPTIONS</title> + + <variablelist> + <varlistentry> + <term>-M</term> + <listitem><para>Searches for a master browser by looking + up the NetBIOS <replaceable>name</replaceable> with a + type of <constant>0x1d</constant>. If <replaceable> + name</replaceable> is "-" then it does a lookup on the special name + <constant>__MSBROWSE__</constant>. Please note that in order to + use the name "-", you need to make sure "-" isn't parsed as an + argument, e.g. use : + <userinput>nmblookup4 -M -- -</userinput>.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-R</term> + <listitem><para>Set the recursion desired bit in the packet + to do a recursive lookup. This is used when sending a name + query to a machine running a WINS server and the user wishes + to query the names in the WINS server. If this bit is unset + the normal (broadcast responding) NetBIOS processing code + on a machine is used instead. See RFC1001, RFC1002 for details. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-S</term> + <listitem><para>Once the name query has returned an IP + address then do a node status query as well. A node status + query returns the NetBIOS names registered by a host. + </para></listitem> + </varlistentry> + + + <varlistentry> + <term>-r</term> + <listitem><para>Try and bind to UDP port 137 to send and receive UDP + datagrams. The reason for this option is a bug in Windows 95 + where it ignores the source port of the requesting packet + and only replies to UDP port 137. Unfortunately, on most UNIX + systems root privilege is needed to bind to this port, and + in addition, if the <citerefentry><refentrytitle>nmbd</refentrytitle> + <manvolnum>8</manvolnum></citerefentry> daemon is running on this machine it also binds to this port. + </para></listitem> + </varlistentry> + + + <varlistentry> + <term>-A</term> + <listitem><para>Interpret <replaceable>name</replaceable> as + an IP Address and do a node status query on this address.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-B <broadcast address></term> + <listitem><para>Send the query to the given broadcast address. Without + this option the default behavior of nmblookup4 is to send the + query to the broadcast address of the network interfaces as + either auto-detected or defined in the <ulink + url="smb.conf.5.html#INTERFACES"><parameter>interfaces</parameter> + </ulink> parameter of the <citerefentry><refentrytitle>smb.conf</refentrytitle> + <manvolnum>5</manvolnum></citerefentry> file. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-U <unicast address></term> + <listitem><para>Do a unicast query to the specified address or + host <replaceable>unicast address</replaceable>. This option + (along with the <parameter>-R</parameter> option) is needed to + query a WINS server.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-T</term> + <listitem><para>This causes any IP addresses found in the + lookup to be looked up via a reverse DNS lookup into a + DNS name, and printed out before each</para> + + <para><emphasis>IP address .... NetBIOS name</emphasis></para> + + <para> pair that is the normal output.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-f</term> + <listitem><para> + Show which flags apply to the name that has been looked up. Possible + answers are zero or more of: Response, Authoritative, + Truncated, Recursion_Desired, Recursion_Available, Broadcast. + </para></listitem> + </varlistentry> + + + <varlistentry> + <term>name</term> + <listitem><para>This is the NetBIOS name being queried. Depending + upon the previous options this may be a NetBIOS name or IP address. + If a NetBIOS name then the different name types may be specified + by appending '#<type>' to the name. This name may also be + '*', which will return all registered names within a broadcast + area.</para></listitem> + </varlistentry> + </variablelist> +</refsect1> + + +<refsect1> + <title>EXAMPLES</title> + + <para><command>nmblookup4</command> can be used to query + a WINS server (in the same way <command>nslookup</command> is + used to query DNS servers). To query a WINS server, <command>nmblookup4</command> + must be called like this:</para> + + <para><command>nmblookup4 -U server -R 'name'</command></para> + + <para>For example, running :</para> + + <para><command>nmblookup4 -U samba.org -R 'IRIX#1B'</command></para> + + <para>would query the WINS server samba.org for the domain + master browser (1B name type) for the IRIX workgroup.</para> +</refsect1> + +<refsect1> + <title>VERSION</title> + + <para>This man page is correct for version 3 of + the Samba suite.</para> +</refsect1> + +<refsect1> + <title>SEE ALSO</title> + <para><citerefentry><refentrytitle>nmbd</refentrytitle> + <manvolnum>8</manvolnum></citerefentry>, <citerefentry><refentrytitle>samba</refentrytitle> + <manvolnum>7</manvolnum></citerefentry>, and <citerefentry><refentrytitle>smb.conf</refentrytitle> + <manvolnum>5</manvolnum></citerefentry>.</para> +</refsect1> + +<refsect1> + <title>AUTHOR</title> + + <para>The original Samba software and related utilities + were created by Andrew Tridgell. Samba is now developed + by the Samba Team as an Open Source project similar + to the way the Linux kernel is developed.</para> + + <para>The original Samba man pages were written by Karl Auer. + The man page sources were converted to YODL format (another + excellent piece of Open Source software, available at <ulink url="ftp://ftp.icce.rug.nl/pub/unix/"> + ftp://ftp.icce.rug.nl/pub/unix/</ulink>) and updated for the Samba 2.0 + release by Jeremy Allison. The conversion to DocBook for + Samba 2.2 was done by Gerald Carter. The conversion to DocBook + XML 4.2 for Samba 3.0 was done by Alexander Bokovoy.</para> +</refsect1> + +</refentry> diff --git a/libcli/nbt/namequery.c b/libcli/nbt/namequery.c new file mode 100644 index 0000000..49ab10c --- /dev/null +++ b/libcli/nbt/namequery.c @@ -0,0 +1,234 @@ +/* + Unix SMB/CIFS implementation. + + make nbt name query requests + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../libcli/nbt/libnbt.h" +#include "../libcli/nbt/nbt_proto.h" +#include "lib/socket/socket.h" + +/** + send a nbt name query +*/ +_PUBLIC_ struct nbt_name_request *nbt_name_query_send(struct nbt_name_socket *nbtsock, + struct nbt_name_query *io) +{ + struct nbt_name_request *req; + struct nbt_name_packet *packet; + struct socket_address *dest; + + packet = talloc_zero(nbtsock, struct nbt_name_packet); + if (packet == NULL) return NULL; + + packet->qdcount = 1; + packet->operation = NBT_OPCODE_QUERY; + if (io->in.broadcast) { + packet->operation |= NBT_FLAG_BROADCAST; + } + if (io->in.wins_lookup) { + 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; + + dest = socket_address_from_strings(packet, nbtsock->sock->backend_name, + io->in.dest_addr, io->in.dest_port); + if (dest == NULL) goto failed; + req = nbt_name_request_send(nbtsock, nbtsock, dest, packet, + io->in.timeout, io->in.retries, false); + if (req == NULL) goto failed; + + talloc_free(packet); + return req; + +failed: + talloc_free(packet); + return NULL; +} + +/** + wait for a name query reply +*/ +_PUBLIC_ NTSTATUS nbt_name_query_recv(struct nbt_name_request *req, + TALLOC_CTX *mem_ctx, struct nbt_name_query *io) +{ + NTSTATUS status; + struct nbt_name_packet *packet; + int i; + + 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].dest->addr); + + if ((packet->operation & NBT_RCODE) != 0) { + status = nbt_rcode_to_ntstatus(packet->operation & NBT_RCODE); + talloc_free(req); + return status; + } + + if (packet->ancount != 1 || + packet->answers[0].rr_type != NBT_QTYPE_NETBIOS || + packet->answers[0].rr_class != NBT_QCLASS_IP) { + talloc_free(req); + return status; + } + + io->out.name = packet->answers[0].name; + 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+1); + if (io->out.reply_addrs == NULL) { + talloc_free(req); + return NT_STATUS_NO_MEMORY; + } + + for (i=0;i<io->out.num_addrs;i++) { + io->out.reply_addrs[i] = talloc_steal(io->out.reply_addrs, + packet->answers[0].rdata.netbios.addresses[i].ipaddr); + } + io->out.reply_addrs[i] = NULL; + + talloc_steal(mem_ctx, io->out.name.name); + talloc_steal(mem_ctx, io->out.name.scope); + + talloc_free(req); + + return NT_STATUS_OK; +} + +/** + wait for a name query reply +*/ +_PUBLIC_ NTSTATUS nbt_name_query(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, struct nbt_name_query *io) +{ + struct nbt_name_request *req = nbt_name_query_send(nbtsock, io); + return nbt_name_query_recv(req, mem_ctx, io); +} + + +/** + send a nbt name status +*/ +_PUBLIC_ struct nbt_name_request *nbt_name_status_send(struct nbt_name_socket *nbtsock, + struct nbt_name_status *io) +{ + struct nbt_name_request *req; + struct nbt_name_packet *packet; + struct socket_address *dest; + + packet = talloc_zero(nbtsock, struct nbt_name_packet); + if (packet == NULL) return NULL; + + packet->qdcount = 1; + packet->operation = NBT_OPCODE_QUERY; + + 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_STATUS; + packet->questions[0].question_class = NBT_QCLASS_IP; + + dest = socket_address_from_strings(packet, nbtsock->sock->backend_name, + io->in.dest_addr, io->in.dest_port); + if (dest == NULL) goto failed; + req = nbt_name_request_send(nbtsock, nbtsock, dest, packet, + io->in.timeout, io->in.retries, false); + if (req == NULL) goto failed; + + talloc_free(packet); + return req; + +failed: + talloc_free(packet); + return NULL; +} + +/** + wait for a name status reply +*/ +_PUBLIC_ NTSTATUS nbt_name_status_recv(struct nbt_name_request *req, + TALLOC_CTX *mem_ctx, struct nbt_name_status *io) +{ + NTSTATUS status; + struct nbt_name_packet *packet; + int i; + + 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].dest->addr); + + if ((packet->operation & NBT_RCODE) != 0) { + status = nbt_rcode_to_ntstatus(packet->operation & NBT_RCODE); + talloc_free(req); + return status; + } + + if (packet->ancount != 1 || + packet->answers[0].rr_type != NBT_QTYPE_STATUS || + packet->answers[0].rr_class != NBT_QCLASS_IP) { + talloc_free(req); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + io->out.name = packet->answers[0].name; + talloc_steal(mem_ctx, io->out.name.name); + talloc_steal(mem_ctx, io->out.name.scope); + + io->out.status = packet->answers[0].rdata.status; + talloc_steal(mem_ctx, io->out.status.names); + for (i=0;i<io->out.status.num_names;i++) { + talloc_steal(io->out.status.names, io->out.status.names[i].name); + } + + + talloc_free(req); + + return NT_STATUS_OK; +} + +/** + wait for a name status reply +*/ +_PUBLIC_ NTSTATUS nbt_name_status(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, struct nbt_name_status *io) +{ + struct nbt_name_request *req = nbt_name_status_send(nbtsock, io); + return nbt_name_status_recv(req, mem_ctx, io); +} + + diff --git a/libcli/nbt/namerefresh.c b/libcli/nbt/namerefresh.c new file mode 100644 index 0000000..b3aef76 --- /dev/null +++ b/libcli/nbt/namerefresh.c @@ -0,0 +1,347 @@ +/* + Unix SMB/CIFS implementation. + + send out a name refresh 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include <tevent.h> +#include "../libcli/nbt/libnbt.h" +#include "../libcli/nbt/nbt_proto.h" +#include "lib/socket/socket.h" +#include "lib/util/tevent_ntstatus.h" + +/* + send a nbt name refresh request +*/ +struct nbt_name_request *nbt_name_refresh_send(struct nbt_name_socket *nbtsock, + struct nbt_name_refresh *io) +{ + struct nbt_name_request *req; + struct nbt_name_packet *packet; + struct socket_address *dest; + + packet = talloc_zero(nbtsock, struct nbt_name_packet); + if (packet == NULL) return NULL; + + packet->qdcount = 1; + packet->arcount = 1; + packet->operation = NBT_OPCODE_REFRESH; + if (io->in.broadcast) { + packet->operation |= NBT_FLAG_BROADCAST; + } + + 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 = + talloc_strdup(packet->additional, io->in.address); + + dest = socket_address_from_strings(nbtsock, + nbtsock->sock->backend_name, + io->in.dest_addr, io->in.dest_port); + if (dest == NULL) goto failed; + req = nbt_name_request_send(nbtsock, nbtsock, dest, packet, + io->in.timeout, io->in.retries, false); + if (req == NULL) goto failed; + + talloc_free(packet); + return req; + +failed: + talloc_free(packet); + return NULL; +} + +/* + wait for a refresh reply +*/ +_PUBLIC_ NTSTATUS nbt_name_refresh_recv(struct nbt_name_request *req, + TALLOC_CTX *mem_ctx, struct nbt_name_refresh *io) +{ + NTSTATUS status; + struct nbt_name_packet *packet; + + 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].dest->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; + } + io->out.reply_addr = talloc_steal(mem_ctx, + packet->answers[0].rdata.netbios.addresses[0].ipaddr); + 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 refresh request +*/ +_PUBLIC_ NTSTATUS nbt_name_refresh(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, struct nbt_name_refresh *io) +{ + struct nbt_name_request *req = nbt_name_refresh_send(nbtsock, io); + return nbt_name_refresh_recv(req, mem_ctx, io); +} + + + +/** + a wins name refresh with multiple WINS servers and multiple + addresses to refresh. Try each WINS server in turn, until we get a + reply for each address +*/ +struct nbt_name_refresh_wins_state { + struct nbt_name_socket *nbtsock; + struct nbt_name_refresh *io; + char **wins_servers; + uint16_t wins_port; + char **addresses; + int address_idx; +}; + +static void nbt_name_refresh_wins_handler(struct nbt_name_request *subreq); + +/** + the async send call for a multi-server WINS refresh +*/ +_PUBLIC_ struct tevent_req *nbt_name_refresh_wins_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct nbt_name_socket *nbtsock, + struct nbt_name_refresh_wins *io) +{ + struct tevent_req *req; + struct nbt_name_refresh_wins_state *state; + struct nbt_name_request *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct nbt_name_refresh_wins_state); + if (req == NULL) { + return NULL; + } + + state->io = talloc(state, struct nbt_name_refresh); + if (tevent_req_nomem(state->io, req)) { + return tevent_req_post(req, ev); + } + + if (io->in.wins_servers == NULL) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + if (io->in.wins_servers[0] == NULL) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + if (io->in.addresses == NULL) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + if (io->in.addresses[0] == NULL) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + state->wins_port = io->in.wins_port; + state->wins_servers = str_list_copy(state, io->in.wins_servers); + if (tevent_req_nomem(state->wins_servers, req)) { + return tevent_req_post(req, ev); + } + + state->addresses = str_list_copy(state, io->in.addresses); + if (tevent_req_nomem(state->addresses, req)) { + return tevent_req_post(req, ev); + } + + state->io->in.name = io->in.name; + state->io->in.dest_addr = state->wins_servers[0]; + state->io->in.dest_port = state->wins_port; + state->io->in.address = io->in.addresses[0]; + state->io->in.nb_flags = io->in.nb_flags; + state->io->in.broadcast = false; + state->io->in.ttl = io->in.ttl; + state->io->in.timeout = 2; + state->io->in.retries = 2; + + state->nbtsock = nbtsock; + state->address_idx = 0; + + subreq = nbt_name_refresh_send(nbtsock, state->io); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + + subreq->async.fn = nbt_name_refresh_wins_handler; + subreq->async.private_data = req; + + return req; +} + +static void nbt_name_refresh_wins_handler(struct nbt_name_request *subreq) +{ + struct tevent_req *req = + talloc_get_type_abort(subreq->async.private_data, + struct tevent_req); + struct nbt_name_refresh_wins_state *state = + tevent_req_data(req, + struct nbt_name_refresh_wins_state); + NTSTATUS status; + + status = nbt_name_refresh_recv(subreq, state, state->io); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + /* the refresh timed out - try the next WINS server */ + state->wins_servers++; + if (state->wins_servers[0] == NULL) { + tevent_req_nterror(req, status); + return; + } + + state->address_idx = 0; + state->io->in.dest_addr = state->wins_servers[0]; + state->io->in.dest_port = state->wins_port; + state->io->in.address = state->addresses[0]; + + subreq = nbt_name_refresh_send(state->nbtsock, state->io); + if (tevent_req_nomem(subreq, req)) { + return; + } + subreq->async.fn = nbt_name_refresh_wins_handler; + subreq->async.private_data = req; + } else if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + if (state->io->out.rcode == 0 && + state->addresses[state->address_idx+1] != NULL) { + /* refresh our next address */ + state->io->in.address = state->addresses[++(state->address_idx)]; + subreq = nbt_name_refresh_send(state->nbtsock, state->io); + if (tevent_req_nomem(subreq, req)) { + return; + } + subreq->async.fn = nbt_name_refresh_wins_handler; + subreq->async.private_data = req; + return; + } + + tevent_req_done(req); +} + +/* + multi-homed WINS name refresh - recv side +*/ +_PUBLIC_ NTSTATUS nbt_name_refresh_wins_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct nbt_name_refresh_wins *io) +{ + struct nbt_name_refresh_wins_state *state = + tevent_req_data(req, + struct nbt_name_refresh_wins_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + io->out.wins_server = talloc_move(mem_ctx, &state->wins_servers[0]); + io->out.rcode = state->io->out.rcode; + + tevent_req_received(req); + return NT_STATUS_OK; +} + +/* + multi-homed WINS refresh - sync interface +*/ +_PUBLIC_ NTSTATUS nbt_name_refresh_wins(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, + struct nbt_name_refresh_wins *io) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *subreq; + NTSTATUS status; + + /* + * TODO: create a temporary event context + */ + ev = nbtsock->event_ctx; + + subreq = nbt_name_refresh_wins_send(frame, ev, nbtsock, io); + if (subreq == NULL) { + talloc_free(frame); + return NT_STATUS_NO_MEMORY; + } + + if (!tevent_req_poll(subreq, ev)) { + status = map_nt_error_from_unix_common(errno); + talloc_free(frame); + return status; + } + + status = nbt_name_refresh_wins_recv(subreq, mem_ctx, io); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(frame); + return status; + } + + TALLOC_FREE(frame); + return NT_STATUS_OK; +} diff --git a/libcli/nbt/nameregister.c b/libcli/nbt/nameregister.c new file mode 100644 index 0000000..8e8271d --- /dev/null +++ b/libcli/nbt/nameregister.c @@ -0,0 +1,514 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include <tevent.h> +#include "../libcli/nbt/libnbt.h" +#include "../libcli/nbt/nbt_proto.h" +#include "lib/socket/socket.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "../lib/util/tevent_ntstatus.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; + struct socket_address *dest; + + packet = talloc_zero(nbtsock, struct nbt_name_packet); + if (packet == NULL) return NULL; + + packet->qdcount = 1; + packet->arcount = 1; + if (io->in.multi_homed) { + packet->operation = NBT_OPCODE_MULTI_HOME_REG; + } else { + 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 = + talloc_strdup(packet->additional, io->in.address); + if (packet->additional[0].rdata.netbios.addresses[0].ipaddr == NULL) goto failed; + + dest = socket_address_from_strings(packet, nbtsock->sock->backend_name, + io->in.dest_addr, io->in.dest_port); + if (dest == NULL) goto failed; + req = nbt_name_request_send(nbtsock, nbtsock, dest, packet, + io->in.timeout, io->in.retries, false); + if (req == NULL) goto failed; + + talloc_free(packet); + return req; + +failed: + talloc_free(packet); + return NULL; +} + +/* + wait for a registration reply +*/ +_PUBLIC_ 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; + + 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].dest->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; + } + io->out.reply_addr = talloc_steal(mem_ctx, + packet->answers[0].rdata.netbios.addresses[0].ipaddr); + 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 +*/ +_PUBLIC_ 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); +} + + +/* + a 4 step broadcast registration. 3 lots of name registration requests, followed by + a name registration demand +*/ +struct nbt_name_register_bcast_state { + struct nbt_name_socket *nbtsock; + struct nbt_name_register io; +}; + +static void nbt_name_register_bcast_handler(struct nbt_name_request *subreq); + +/* + the async send call for a 4 stage name registration +*/ +_PUBLIC_ struct tevent_req *nbt_name_register_bcast_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct nbt_name_socket *nbtsock, + struct nbt_name_register_bcast *io) +{ + struct tevent_req *req; + struct nbt_name_register_bcast_state *state; + struct nbt_name_request *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct nbt_name_register_bcast_state); + if (req == NULL) { + return NULL; + } + + state->io.in.name = io->in.name; + state->io.in.dest_addr = io->in.dest_addr; + state->io.in.dest_port = io->in.dest_port; + state->io.in.address = io->in.address; + state->io.in.nb_flags = io->in.nb_flags; + state->io.in.register_demand = false; + state->io.in.broadcast = true; + state->io.in.multi_homed = false; + state->io.in.ttl = io->in.ttl; + state->io.in.timeout = 1; + state->io.in.retries = 2; + + state->nbtsock = nbtsock; + + subreq = nbt_name_register_send(nbtsock, &state->io); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + + subreq->async.fn = nbt_name_register_bcast_handler; + subreq->async.private_data = req; + + return req; +} + +static void nbt_name_register_bcast_handler(struct nbt_name_request *subreq) +{ + struct tevent_req *req = + talloc_get_type_abort(subreq->async.private_data, + struct tevent_req); + struct nbt_name_register_bcast_state *state = + tevent_req_data(req, + struct nbt_name_register_bcast_state); + NTSTATUS status; + + status = nbt_name_register_recv(subreq, state, &state->io); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + if (state->io.in.register_demand == true) { + tevent_req_done(req); + return; + } + + /* the registration timed out - good, send the demand */ + state->io.in.register_demand = true; + state->io.in.retries = 0; + + subreq = nbt_name_register_send(state->nbtsock, &state->io); + if (tevent_req_nomem(subreq, req)) { + return; + } + + subreq->async.fn = nbt_name_register_bcast_handler; + subreq->async.private_data = req; + return; + } + + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + DEBUG(3,("Name registration conflict from %s for %s with ip %s - rcode %d\n", + state->io.out.reply_from, + nbt_name_string(state, &state->io.out.name), + state->io.out.reply_addr, + state->io.out.rcode)); + + tevent_req_nterror(req, NT_STATUS_CONFLICTING_ADDRESSES); +} + +/* + broadcast 4 part name register - recv +*/ +_PUBLIC_ NTSTATUS nbt_name_register_bcast_recv(struct tevent_req *req) +{ + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + tevent_req_received(req); + return NT_STATUS_OK; +} + +/* + broadcast 4 part name register - sync interface +*/ +NTSTATUS nbt_name_register_bcast(struct nbt_name_socket *nbtsock, + struct nbt_name_register_bcast *io) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *subreq; + NTSTATUS status; + + /* + * TODO: create a temporary event context + */ + ev = nbtsock->event_ctx; + + subreq = nbt_name_register_bcast_send(frame, ev, nbtsock, io); + if (subreq == NULL) { + talloc_free(frame); + return NT_STATUS_NO_MEMORY; + } + + if (!tevent_req_poll(subreq, ev)) { + status = map_nt_error_from_unix_common(errno); + talloc_free(frame); + return status; + } + + status = nbt_name_register_bcast_recv(subreq); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(frame); + return status; + } + + TALLOC_FREE(frame); + return NT_STATUS_OK; +} + + +/* + a wins name register with multiple WINS servers and multiple + addresses to register. Try each WINS server in turn, until we get a + reply for each address +*/ +struct nbt_name_register_wins_state { + struct nbt_name_socket *nbtsock; + struct nbt_name_register io; + char **wins_servers; + uint16_t wins_port; + char **addresses; + uint32_t address_idx; +}; + +static void nbt_name_register_wins_handler(struct nbt_name_request *subreq); + +/* + the async send call for a multi-server WINS register +*/ +_PUBLIC_ struct tevent_req *nbt_name_register_wins_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct nbt_name_socket *nbtsock, + struct nbt_name_register_wins *io) +{ + struct tevent_req *req; + struct nbt_name_register_wins_state *state; + struct nbt_name_request *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct nbt_name_register_wins_state); + if (req == NULL) { + return NULL; + } + + if (io->in.wins_servers == NULL) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + if (io->in.wins_servers[0] == NULL) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + if (io->in.addresses == NULL) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + if (io->in.addresses[0] == NULL) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + state->wins_port = io->in.wins_port; + state->wins_servers = str_list_copy(state, io->in.wins_servers); + if (tevent_req_nomem(state->wins_servers, req)) { + return tevent_req_post(req, ev); + } + + state->addresses = str_list_copy(state, io->in.addresses); + if (tevent_req_nomem(state->addresses, req)) { + return tevent_req_post(req, ev); + } + + state->io.in.name = io->in.name; + state->io.in.dest_addr = state->wins_servers[0]; + state->io.in.dest_port = state->wins_port; + state->io.in.address = io->in.addresses[0]; + state->io.in.nb_flags = io->in.nb_flags; + state->io.in.broadcast = false; + state->io.in.register_demand = false; + state->io.in.multi_homed = (io->in.nb_flags & NBT_NM_GROUP)?false:true; + state->io.in.ttl = io->in.ttl; + state->io.in.timeout = 3; + state->io.in.retries = 2; + + state->nbtsock = nbtsock; + state->address_idx = 0; + + subreq = nbt_name_register_send(nbtsock, &state->io); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + + subreq->async.fn = nbt_name_register_wins_handler; + subreq->async.private_data = req; + + return req; +} + +/* + state handler for WINS multi-homed multi-server name register +*/ +static void nbt_name_register_wins_handler(struct nbt_name_request *subreq) +{ + struct tevent_req *req = + talloc_get_type_abort(subreq->async.private_data, + struct tevent_req); + struct nbt_name_register_wins_state *state = + tevent_req_data(req, + struct nbt_name_register_wins_state); + NTSTATUS status; + + status = nbt_name_register_recv(subreq, state, &state->io); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + /* the register timed out - try the next WINS server */ + state->wins_servers++; + if (state->wins_servers[0] == NULL) { + tevent_req_nterror(req, status); + return; + } + + state->address_idx = 0; + state->io.in.dest_addr = state->wins_servers[0]; + state->io.in.dest_port = state->wins_port; + state->io.in.address = state->addresses[0]; + + subreq = nbt_name_register_send(state->nbtsock, &state->io); + if (tevent_req_nomem(subreq, req)) { + return; + } + + subreq->async.fn = nbt_name_register_wins_handler; + subreq->async.private_data = req; + return; + } + + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + if (state->io.out.rcode == 0 && + state->addresses[state->address_idx+1] != NULL) { + /* register our next address */ + state->io.in.address = state->addresses[++(state->address_idx)]; + + subreq = nbt_name_register_send(state->nbtsock, &state->io); + if (tevent_req_nomem(subreq, req)) { + return; + } + + subreq->async.fn = nbt_name_register_wins_handler; + subreq->async.private_data = req; + return; + } + + tevent_req_done(req); +} + +/* + multi-homed WINS name register - recv side +*/ +_PUBLIC_ NTSTATUS nbt_name_register_wins_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct nbt_name_register_wins *io) +{ + struct nbt_name_register_wins_state *state = + tevent_req_data(req, + struct nbt_name_register_wins_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + io->out.wins_server = talloc_move(mem_ctx, &state->wins_servers[0]); + io->out.rcode = state->io.out.rcode; + + tevent_req_received(req); + return NT_STATUS_OK; +} + +/* + multi-homed WINS register - sync interface +*/ +_PUBLIC_ NTSTATUS nbt_name_register_wins(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, + struct nbt_name_register_wins *io) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *subreq; + NTSTATUS status; + + /* + * TODO: create a temporary event context + */ + ev = nbtsock->event_ctx; + + subreq = nbt_name_register_wins_send(frame, ev, nbtsock, io); + if (subreq == NULL) { + talloc_free(frame); + return NT_STATUS_NO_MEMORY; + } + + if (!tevent_req_poll(subreq, ev)) { + status = map_nt_error_from_unix_common(errno); + talloc_free(frame); + return status; + } + + status = nbt_name_register_wins_recv(subreq, mem_ctx, io); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(frame); + return status; + } + + TALLOC_FREE(frame); + return NT_STATUS_OK; +} diff --git a/libcli/nbt/namerelease.c b/libcli/nbt/namerelease.c new file mode 100644 index 0000000..68c8252 --- /dev/null +++ b/libcli/nbt/namerelease.c @@ -0,0 +1,134 @@ +/* + Unix SMB/CIFS implementation. + + send out a name release 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../libcli/nbt/libnbt.h" +#include "../libcli/nbt/nbt_proto.h" +#include "lib/socket/socket.h" + +/* + send a nbt name release request +*/ +_PUBLIC_ struct nbt_name_request *nbt_name_release_send(struct nbt_name_socket *nbtsock, + struct nbt_name_release *io) +{ + struct nbt_name_request *req; + struct nbt_name_packet *packet; + struct socket_address *dest; + + packet = talloc_zero(nbtsock, struct nbt_name_packet); + if (packet == NULL) return NULL; + + packet->qdcount = 1; + packet->arcount = 1; + packet->operation = NBT_OPCODE_RELEASE; + if (io->in.broadcast) { + packet->operation |= NBT_FLAG_BROADCAST; + } + + 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 = 0; + 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 = + talloc_strdup(packet->additional, io->in.address); + + dest = socket_address_from_strings(packet, nbtsock->sock->backend_name, + io->in.dest_addr, io->in.dest_port); + if (dest == NULL) goto failed; + req = nbt_name_request_send(nbtsock, nbtsock, dest, packet, + io->in.timeout, io->in.retries, false); + if (req == NULL) goto failed; + + talloc_free(packet); + return req; + +failed: + talloc_free(packet); + return NULL; +} + +/* + wait for a release reply +*/ +_PUBLIC_ NTSTATUS nbt_name_release_recv(struct nbt_name_request *req, + TALLOC_CTX *mem_ctx, struct nbt_name_release *io) +{ + NTSTATUS status; + struct nbt_name_packet *packet; + + 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].dest->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; + } + io->out.reply_addr = talloc_steal(mem_ctx, + packet->answers[0].rdata.netbios.addresses[0].ipaddr); + 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 release request +*/ +_PUBLIC_ NTSTATUS nbt_name_release(struct nbt_name_socket *nbtsock, + TALLOC_CTX *mem_ctx, struct nbt_name_release *io) +{ + struct nbt_name_request *req = nbt_name_release_send(nbtsock, io); + return nbt_name_release_recv(req, mem_ctx, io); +} diff --git a/libcli/nbt/nbt_proto.h b/libcli/nbt/nbt_proto.h new file mode 100644 index 0000000..e6ee46b --- /dev/null +++ b/libcli/nbt/nbt_proto.h @@ -0,0 +1,65 @@ +/* + Unix SMB/CIFS implementation. + + manipulate nbt name structures + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _____LIBCLI_NBT_NBT_PROTO_H__ +#define _____LIBCLI_NBT_NBT_PROTO_H__ + +#undef _PRINTF_ATTRIBUTE +#define _PRINTF_ATTRIBUTE(a1, a2) PRINTF_ATTRIBUTE(a1, a2) +/* This file was automatically generated by mkproto.pl. DO NOT EDIT */ + +/* this file contains prototypes for functions that are private + * to this subsystem or library. These functions should not be + * used outside this particular subsystem! */ + + +/* The following definitions come from ../libcli/nbt/nbtsocket.c */ + +struct nbt_name_request *nbt_name_request_send(TALLOC_CTX *mem_ctx, + struct nbt_name_socket *nbtsock, + struct socket_address *dest, + struct nbt_name_packet *request, + int timeout, int retries, + bool allow_multiple_replies); +NTSTATUS nbt_name_request_recv(struct nbt_name_request *req); + +/* The following definitions come from ../libcli/nbt/namequery.c */ + + +/* The following definitions come from ../libcli/nbt/nameregister.c */ + +struct nbt_name_request *nbt_name_register_send(struct nbt_name_socket *nbtsock, + struct nbt_name_register *io); +NTSTATUS nbt_name_register_bcast(struct nbt_name_socket *nbtsock, + struct nbt_name_register_bcast *io); + +/* The following definitions come from ../libcli/nbt/namerefresh.c */ + +struct nbt_name_request *nbt_name_refresh_send(struct nbt_name_socket *nbtsock, + struct nbt_name_refresh *io); + +/* The following definitions come from ../libcli/nbt/namerelease.c */ + +#undef _PRINTF_ATTRIBUTE +#define _PRINTF_ATTRIBUTE(a1, a2) + +#endif /* _____LIBCLI_NBT_NBT_PROTO_H__ */ + diff --git a/libcli/nbt/nbtname.c b/libcli/nbt/nbtname.c new file mode 100644 index 0000000..ec2b395 --- /dev/null +++ b/libcli/nbt/nbtname.c @@ -0,0 +1,500 @@ +/* + Unix SMB/CIFS implementation. + + manipulate nbt name structures + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + see rfc1002 for the detailed format of compressed names +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "system/locale.h" +#include "lib/util/util_net.h" +#include "libcli/nbt/libnbt.h" + +/* + decompress a 'compressed' name component + */ +static bool decompress_name(char *name, enum nbt_name_type *type) +{ + int i; + for (i=0;name[2*i];i++) { + uint8_t c1 = name[2*i]; + uint8_t c2 = name[1+(2*i)]; + if (c1 < 'A' || c1 > 'P' || + c2 < 'A' || c2 > 'P') { + return false; + } + name[i] = ((c1-'A')<<4) | (c2-'A'); + } + name[i] = 0; + if (i == 16) { + *type = (enum nbt_name_type)(name[15]); + name[15] = 0; + i--; + } else { + *type = NBT_NAME_CLIENT; + } + + /* trim trailing spaces */ + for (;i>0 && name[i-1]==' ';i--) { + name[i-1] = 0; + } + + return true; +} + + +/* + compress a name component + */ +static uint8_t *compress_name(TALLOC_CTX *mem_ctx, + const uint8_t *name, enum nbt_name_type type) +{ + uint8_t *cname; + int i; + uint8_t pad_char; + + if (strlen((const char *)name) > 15) { + return NULL; + } + + cname = talloc_array(mem_ctx, uint8_t, 33); + if (cname == NULL) return NULL; + + for (i=0;name[i];i++) { + cname[2*i] = 'A' + (name[i]>>4); + cname[1+2*i] = 'A' + (name[i]&0xF); + } + if (strcmp((const char *)name, "*") == 0) { + pad_char = 0; + } else { + pad_char = ' '; + } + for (;i<15;i++) { + cname[2*i] = 'A' + (pad_char>>4); + cname[1+2*i] = 'A' + (pad_char&0xF); + } + + pad_char = type; + cname[2*i] = 'A' + (pad_char>>4); + cname[1+2*i] = 'A' + (pad_char&0xF); + + cname[32] = 0; + return cname; +} + + +/** + pull a nbt name from the wire +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_nbt_name(struct ndr_pull *ndr, int ndr_flags, struct nbt_name *r) +{ + uint8_t *scope; + char *cname; + const char *s; + bool ok; + + if (!(ndr_flags & NDR_SCALARS)) { + return NDR_ERR_SUCCESS; + } + + NDR_CHECK(ndr_pull_nbt_string(ndr, ndr_flags, &s)); + + scope = (uint8_t *)strchr(s, '.'); + if (scope) { + *scope = 0; + r->scope = talloc_strdup(ndr->current_mem_ctx, (const char *)&scope[1]); + NDR_ERR_HAVE_NO_MEMORY(r->scope); + } else { + r->scope = NULL; + } + + cname = discard_const_p(char, s); + + /* the first component is limited to 16 bytes in the DOS charset, + which is 32 in the 'compressed' form */ + if (strlen(cname) > 32) { + return ndr_pull_error(ndr, NDR_ERR_STRING, + "NBT NAME cname > 32"); + } + + /* decompress the first component */ + ok = decompress_name(cname, &r->type); + if (!ok) { + return ndr_pull_error(ndr, NDR_ERR_STRING, + "NBT NAME failed to decompress"); + } + + r->name = talloc_strdup(ndr->current_mem_ctx, cname); + NDR_ERR_HAVE_NO_MEMORY(r->name); + + talloc_free(cname); + + return NDR_ERR_SUCCESS; +} + +/** + push a nbt name to the wire +*/ +_PUBLIC_ enum ndr_err_code ndr_push_nbt_name(struct ndr_push *ndr, int ndr_flags, const struct nbt_name *r) +{ + uint8_t *cname, *fullname; + enum ndr_err_code ndr_err; + + if (!(ndr_flags & NDR_SCALARS)) { + return NDR_ERR_SUCCESS; + } + + if (strlen(r->name) > 15) { + return ndr_push_error(ndr, NDR_ERR_STRING, + "nbt_name longer as 15 chars: %s", + r->name); + } + + cname = compress_name(ndr, (const uint8_t *)r->name, r->type); + NDR_ERR_HAVE_NO_MEMORY(cname); + + if (r->scope) { + fullname = (uint8_t *)talloc_asprintf(ndr, "%s.%s", cname, r->scope); + NDR_ERR_HAVE_NO_MEMORY(fullname); + talloc_free(cname); + } else { + fullname = cname; + } + + ndr_err = ndr_push_nbt_string(ndr, ndr_flags, (const char *)fullname); + + return ndr_err; +} + + +/** + copy a nbt name structure +*/ +_PUBLIC_ NTSTATUS nbt_name_dup(TALLOC_CTX *mem_ctx, + const struct nbt_name *name, + struct nbt_name *newname) +{ + *newname = *name; + newname->name = talloc_strdup(mem_ctx, newname->name); + NT_STATUS_HAVE_NO_MEMORY(newname->name); + newname->scope = talloc_strdup(mem_ctx, newname->scope); + if (name->scope) { + NT_STATUS_HAVE_NO_MEMORY(newname->scope); + } + return NT_STATUS_OK; +} + +/** + push a nbt name into a blob +*/ +_PUBLIC_ NTSTATUS nbt_name_to_blob(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct nbt_name *name) +{ + enum ndr_err_code ndr_err; + + ndr_err = ndr_push_struct_blob(blob, mem_ctx, name, (ndr_push_flags_fn_t)ndr_push_nbt_name); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return ndr_map_error2ntstatus(ndr_err); + } + + return NT_STATUS_OK; +} + +/** + pull a nbt name from a blob +*/ +_PUBLIC_ NTSTATUS nbt_name_from_blob(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, struct nbt_name *name) +{ + enum ndr_err_code ndr_err; + + ndr_err = ndr_pull_struct_blob(blob, mem_ctx, name, + (ndr_pull_flags_fn_t)ndr_pull_nbt_name); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return ndr_map_error2ntstatus(ndr_err); + } + + return NT_STATUS_OK; +} + + +/** + choose a name to use when calling a server in a NBT session request. + we use heuristics to see if the name we have been given is a IP + address, or a too-long name. If it is then use *SMBSERVER, or a + truncated name +*/ +_PUBLIC_ void nbt_choose_called_name(TALLOC_CTX *mem_ctx, + struct nbt_name *n, const char *name, int type) +{ + n->scope = NULL; + n->type = type; + + if ((name == NULL) || is_ipaddress(name)) { + n->name = "*SMBSERVER"; + return; + } + if (strlen(name) > 15) { + const char *p = strchr(name, '.'); + char *s; + if (p - name > 15) { + n->name = "*SMBSERVER"; + return; + } + s = talloc_strndup(mem_ctx, name, PTR_DIFF(p, name)); + n->name = talloc_strdup_upper(mem_ctx, s); + return; + } + + n->name = talloc_strdup_upper(mem_ctx, name); +} + + +/* + escape a string into a form containing only a small set of characters, + the rest is hex encoded. This is similar to URL encoding +*/ +static const char *nbt_hex_encode(TALLOC_CTX *mem_ctx, const char *s) +{ + int i, len; + char *ret; + const char *valid_chars = "_-.$@ "; +#define NBT_CHAR_ALLOW(c) (isalnum((unsigned char)c) || strchr(valid_chars, c)) + + for (len=i=0;s[i];i++,len++) { + if (!NBT_CHAR_ALLOW(s[i])) { + len += 2; + } + } + + ret = talloc_array(mem_ctx, char, len+1); + if (ret == NULL) return NULL; + + for (len=i=0;s[i];i++) { + if (NBT_CHAR_ALLOW(s[i])) { + ret[len++] = s[i]; + } else { + snprintf(&ret[len], 4, "%%%02x", (unsigned char)s[i]); + len += 3; + } + } + ret[len] = 0; + + return ret; +} + + +/** + form a string for a NBT name +*/ +_PUBLIC_ char *nbt_name_string(TALLOC_CTX *mem_ctx, const struct nbt_name *name) +{ + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + char *ret; + if (name->scope) { + ret = talloc_asprintf(mem_ctx, "%s<%02x>-%s", + nbt_hex_encode(tmp_ctx, name->name), + name->type, + nbt_hex_encode(tmp_ctx, name->scope)); + } else { + ret = talloc_asprintf(mem_ctx, "%s<%02x>", + nbt_hex_encode(tmp_ctx, name->name), + name->type); + } + talloc_free(tmp_ctx); + return ret; +} + +/** + pull a nbt name, WINS Replication uses another on wire format for nbt name +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_wrepl_nbt_name(struct ndr_pull *ndr, int ndr_flags, struct nbt_name **_r) +{ + struct nbt_name *r; + uint8_t *namebuf; + uint32_t namebuf_len; + + if (!(ndr_flags & NDR_SCALARS)) { + return NDR_ERR_SUCCESS; + } + + NDR_CHECK(ndr_pull_align(ndr, 4)); + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &namebuf_len)); + if (namebuf_len < 1 || namebuf_len > 255) { + return ndr_pull_error(ndr, NDR_ERR_ALLOC, "value out of range"); + } + NDR_PULL_ALLOC_N(ndr, namebuf, namebuf_len); + NDR_CHECK(ndr_pull_array_uint8(ndr, NDR_SCALARS, namebuf, namebuf_len)); + + if ((namebuf_len % 4) == 0) { + /* + * [MS-WINSRA] — v20091104 was wrong + * regarding section "2.2.10.1 Name Record" + * + * If the name buffer is already 4 byte aligned + * Windows (at least 2003 SP1 and 2008) add 4 extra + * bytes. This can happen when the name has a scope. + */ + uint32_t pad; + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &pad)); + } + + NDR_PULL_ALLOC(ndr, r); + + /* oh wow, what a nasty bug in windows ... */ + if (namebuf[0] == 0x1b && namebuf_len >= 16) { + namebuf[0] = namebuf[15]; + namebuf[15] = 0x1b; + } + + if (namebuf_len < 17) { + r->type = 0x00; + + r->name = talloc_strndup(r, (char *)namebuf, namebuf_len); + if (!r->name) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory"); + + r->scope= NULL; + + talloc_free(namebuf); + *_r = r; + return NDR_ERR_SUCCESS; + } + + r->type = namebuf[15]; + + namebuf[15] = '\0'; + trim_string((char *)namebuf, NULL, " "); + r->name = talloc_strdup(r, (char *)namebuf); + if (!r->name) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory"); + + if (namebuf_len > 17) { + r->scope = talloc_strndup(r, (char *)(namebuf+16), namebuf_len-17); + if (!r->scope) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory"); + } else { + r->scope = NULL; + } + + talloc_free(namebuf); + *_r = r; + return NDR_ERR_SUCCESS; +} + +/** + push a nbt name, WINS Replication uses another on wire format for nbt name +*/ +_PUBLIC_ enum ndr_err_code ndr_push_wrepl_nbt_name(struct ndr_push *ndr, int ndr_flags, const struct nbt_name *r) +{ + uint8_t *namebuf; + uint32_t namebuf_len; + uint32_t _name_len; + uint32_t scope_len = 0; + + if (r == NULL) { + return ndr_push_error(ndr, NDR_ERR_INVALID_POINTER, + "wrepl_nbt_name NULL pointer"); + } + + if (!(ndr_flags & NDR_SCALARS)) { + return NDR_ERR_SUCCESS; + } + + _name_len = strlen(r->name); + if (_name_len > 15) { + return ndr_push_error(ndr, NDR_ERR_STRING, + "wrepl_nbt_name longer as 15 chars: %s", + r->name); + } + + if (r->scope) { + scope_len = strlen(r->scope); + } + if (scope_len > 238) { + return ndr_push_error(ndr, NDR_ERR_STRING, + "wrepl_nbt_name scope longer as 238 chars: %s", + r->scope); + } + + namebuf = (uint8_t *)talloc_asprintf(ndr, "%-15s%c%s", + r->name, 'X', + (r->scope?r->scope:"")); + if (!namebuf) return ndr_push_error(ndr, NDR_ERR_ALLOC, "out of memory"); + + namebuf_len = strlen((char *)namebuf) + 1; + + /* + * we need to set the type here, and use a place-holder in the talloc_asprintf() + * as the type can be 0x00, and then the namebuf_len = strlen(namebuf); would give wrong results + */ + namebuf[15] = r->type; + + /* oh wow, what a nasty bug in windows ... */ + if (r->type == 0x1b) { + namebuf[15] = namebuf[0]; + namebuf[0] = 0x1b; + } + + NDR_CHECK(ndr_push_align(ndr, 4)); + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, namebuf_len)); + NDR_CHECK(ndr_push_array_uint8(ndr, NDR_SCALARS, namebuf, namebuf_len)); + + if ((namebuf_len % 4) == 0) { + /* + * [MS-WINSRA] — v20091104 was wrong + * regarding section "2.2.10.1 Name Record" + * + * If the name buffer is already 4 byte aligned + * Windows (at least 2003 SP1 and 2008) add 4 extra + * bytes. This can happen when the name has a scope. + */ + NDR_CHECK(ndr_push_zero(ndr, 4)); + } + + talloc_free(namebuf); + return NDR_ERR_SUCCESS; +} + +_PUBLIC_ void ndr_print_wrepl_nbt_name(struct ndr_print *ndr, const char *name, const struct nbt_name *r) +{ + char *s = nbt_name_string(ndr, r); + ndr_print_string(ndr, name, s); + talloc_free(s); +} + +_PUBLIC_ enum ndr_err_code ndr_push_nbt_res_rec(struct ndr_push *ndr, int ndr_flags, const struct nbt_res_rec *r) +{ + { + uint32_t _flags_save_STRUCT = ndr->flags; + ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX); + if (ndr_flags & NDR_SCALARS) { + NDR_CHECK(ndr_push_align(ndr, 4)); + NDR_CHECK(ndr_push_nbt_name(ndr, NDR_SCALARS, &r->name)); + NDR_CHECK(ndr_push_nbt_qtype(ndr, NDR_SCALARS, r->rr_type)); + NDR_CHECK(ndr_push_nbt_qclass(ndr, NDR_SCALARS, r->rr_class)); + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, r->ttl)); + NDR_CHECK(ndr_push_set_switch_value(ndr, &r->rdata, ((((r->rr_type) == NBT_QTYPE_NETBIOS) && ((r->rdata).data.length == 2))?0:r->rr_type))); + NDR_CHECK(ndr_push_nbt_rdata(ndr, NDR_SCALARS, &r->rdata)); + } + if (ndr_flags & NDR_BUFFERS) { + } + ndr->flags = _flags_save_STRUCT; + } + return NDR_ERR_SUCCESS; +} diff --git a/libcli/nbt/nbtsocket.c b/libcli/nbt/nbtsocket.c new file mode 100644 index 0000000..97b0ca3 --- /dev/null +++ b/libcli/nbt/nbtsocket.c @@ -0,0 +1,564 @@ +/* + Unix SMB/CIFS implementation. + + low level socket handling for nbt requests + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "../lib/util/dlinklist.h" +#include "../libcli/nbt/libnbt.h" +#include "../libcli/nbt/nbt_proto.h" +#include "lib/socket/socket.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "param/param.h" + +#define NBT_MAX_REPLIES 1000 + +/* + destroy a pending request +*/ +static int nbt_name_request_destructor(struct nbt_name_request *req) +{ + if (req->state == NBT_REQUEST_SEND) { + DLIST_REMOVE(req->nbtsock->send_queue, req); + } + if (req->state == NBT_REQUEST_WAIT) { + req->nbtsock->num_pending--; + } + if (req->name_trn_id != 0 && !req->is_reply) { + idr_remove(req->nbtsock->idr, req->name_trn_id); + req->name_trn_id = 0; + } + TALLOC_FREE(req->te); + if (req->nbtsock->send_queue == NULL) { + TEVENT_FD_NOT_WRITEABLE(req->nbtsock->fde); + } + if (req->nbtsock->num_pending == 0 && + req->nbtsock->incoming.handler == NULL) { + TEVENT_FD_NOT_READABLE(req->nbtsock->fde); + } + return 0; +} + + +/* + handle send events on a nbt name socket +*/ +static void nbt_name_socket_send(struct nbt_name_socket *nbtsock) +{ + struct nbt_name_request *req; + TALLOC_CTX *tmp_ctx = talloc_new(nbtsock); + NTSTATUS status; + + while ((req = nbtsock->send_queue)) { + size_t len; + + len = req->encoded.length; + status = socket_sendto(nbtsock->sock, &req->encoded, &len, + req->dest); + if (NT_STATUS_IS_ERR(status)) goto failed; + + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return; + } + + DLIST_REMOVE(nbtsock->send_queue, req); + req->state = NBT_REQUEST_WAIT; + if (req->is_reply) { + talloc_free(req); + } else { + TEVENT_FD_READABLE(nbtsock->fde); + nbtsock->num_pending++; + } + } + + TEVENT_FD_NOT_WRITEABLE(nbtsock->fde); + talloc_free(tmp_ctx); + return; + +failed: + DLIST_REMOVE(nbtsock->send_queue, req); + nbt_name_request_destructor(req); + req->status = status; + req->state = NBT_REQUEST_ERROR; + talloc_free(tmp_ctx); + if (req->async.fn) { + req->async.fn(req); + } else if (req->is_reply) { + talloc_free(req); + } + return; +} + + +/* + handle a request timeout +*/ +static void nbt_name_socket_timeout(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct nbt_name_request *req = talloc_get_type(private_data, + struct nbt_name_request); + + if (req->num_retries != 0) { + req->num_retries--; + req->te = tevent_add_timer(req->nbtsock->event_ctx, req, + timeval_add(&t, req->timeout, 0), + nbt_name_socket_timeout, req); + if (req->state != NBT_REQUEST_SEND) { + req->state = NBT_REQUEST_SEND; + DLIST_ADD_END(req->nbtsock->send_queue, req); + } + TEVENT_FD_WRITEABLE(req->nbtsock->fde); + return; + } + + nbt_name_request_destructor(req); + if (req->num_replies == 0) { + req->state = NBT_REQUEST_TIMEOUT; + req->status = NT_STATUS_IO_TIMEOUT; + } else { + req->state = NBT_REQUEST_DONE; + req->status = NT_STATUS_OK; + } + if (req->async.fn) { + req->async.fn(req); + } else if (req->is_reply) { + talloc_free(req); + } +} + + + +/** + handle recv events on a nbt name socket +*/ +static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock) +{ + TALLOC_CTX *tmp_ctx = talloc_new(nbtsock); + NTSTATUS status; + enum ndr_err_code ndr_err; + struct socket_address *src; + DATA_BLOB blob; + size_t nread, dsize; + struct nbt_name_packet *packet; + struct nbt_name_request *req; + + status = socket_pending(nbtsock->sock, &dsize); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return; + } + + /* + * Given a zero length, data_blob_talloc() returns the + * NULL blob {NULL, 0}. + * + * We only want to error return here on a real out of memory condition + * (i.e. dsize != 0, so the UDP packet has data, but the return of the + * allocation failed, so blob.data==NULL). + * + * Given an actual zero length UDP packet having blob.data == NULL + * isn't an out of memory error condition, that's the defined semantics + * of data_blob_talloc() when asked for zero bytes. + * + * We still need to continue to do the zero-length socket_recvfrom() + * read in order to clear the "read pending" condition on the socket. + */ + blob = data_blob_talloc(tmp_ctx, NULL, dsize); + if (blob.data == NULL && dsize != 0) { + talloc_free(tmp_ctx); + return; + } + + status = socket_recvfrom(nbtsock->sock, blob.data, blob.length, &nread, + tmp_ctx, &src); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return; + } + + packet = talloc(tmp_ctx, struct nbt_name_packet); + if (packet == NULL) { + talloc_free(tmp_ctx); + return; + } + + /* parse the request */ + ndr_err = ndr_pull_struct_blob(&blob, packet, packet, + (ndr_pull_flags_fn_t)ndr_pull_nbt_name_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + DEBUG(2,("Failed to parse incoming NBT name packet - %s\n", + nt_errstr(status))); + talloc_free(tmp_ctx); + return; + } + + if (DEBUGLVL(10)) { + DEBUG(10,("Received nbt packet of length %d from %s:%d\n", + (int)blob.length, src->addr, src->port)); + NDR_PRINT_DEBUG(nbt_name_packet, packet); + } + + /* if its not a reply then pass it off to the incoming request + handler, if any */ + if (!(packet->operation & NBT_FLAG_REPLY)) { + if (nbtsock->incoming.handler) { + nbtsock->incoming.handler(nbtsock, packet, src); + } + talloc_free(tmp_ctx); + return; + } + + /* find the matching request */ + req = (struct nbt_name_request *)idr_find(nbtsock->idr, + packet->name_trn_id); + if (req == NULL) { + if (nbtsock->unexpected.handler) { + nbtsock->unexpected.handler(nbtsock, packet, src); + } else { + DEBUG(10,("Failed to match request for incoming name packet id 0x%04x on %p\n", + packet->name_trn_id, nbtsock)); + } + talloc_free(tmp_ctx); + return; + } + + talloc_steal(req, packet); + talloc_steal(req, src); + talloc_free(tmp_ctx); + nbt_name_socket_handle_response_packet(req, packet, src); +} + +void nbt_name_socket_handle_response_packet(struct nbt_name_request *req, + struct nbt_name_packet *packet, + struct socket_address *src) +{ + /* if this is a WACK response, this we need to go back to waiting, + but perhaps increase the timeout */ + if ((packet->operation & NBT_OPCODE) == NBT_OPCODE_WACK) { + uint32_t ttl; + if (req->received_wack || packet->ancount < 1) { + nbt_name_request_destructor(req); + req->status = NT_STATUS_INVALID_NETWORK_RESPONSE; + req->state = NBT_REQUEST_ERROR; + goto done; + } + talloc_free(req->te); + /* we know we won't need any more retries - the server + has received our request */ + req->num_retries = 0; + req->received_wack = true; + /* + * there is a timeout in the packet, + * it is 5 + 4 * num_old_addresses + * + * although w2k3 screws it up + * and uses num_old_addresses = 0 + * + * so we better fallback to the maximum + * of num_old_addresses = 25 if we got + * a timeout of less than 9s (5 + 4*1) + * or more than 105s (5 + 4*25). + */ + ttl = packet->answers[0].ttl; + if ((ttl < (5 + 4*1)) || (ttl > (5 + 4*25))) { + ttl = 5 + 4*25; + } + req->timeout = ttl; + req->te = tevent_add_timer(req->nbtsock->event_ctx, req, + timeval_current_ofs(req->timeout, 0), + nbt_name_socket_timeout, req); + return; + } + + + req->replies = talloc_realloc(req, req->replies, struct nbt_name_reply, req->num_replies+1); + if (req->replies == NULL) { + nbt_name_request_destructor(req); + req->state = NBT_REQUEST_ERROR; + req->status = NT_STATUS_NO_MEMORY; + goto done; + } + + talloc_steal(req, src); + req->replies[req->num_replies].dest = src; + talloc_steal(req, packet); + req->replies[req->num_replies].packet = packet; + req->num_replies++; + + /* if we don't want multiple replies then we are done */ + if (req->allow_multiple_replies && + req->num_replies < NBT_MAX_REPLIES) { + return; + } + + nbt_name_request_destructor(req); + req->state = NBT_REQUEST_DONE; + req->status = NT_STATUS_OK; + +done: + if (req->async.fn) { + req->async.fn(req); + } +} + +/* + handle fd events on a nbt_name_socket +*/ +static void nbt_name_socket_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct nbt_name_socket *nbtsock = talloc_get_type(private_data, + struct nbt_name_socket); + if (flags & TEVENT_FD_WRITE) { + nbt_name_socket_send(nbtsock); + } + if (flags & TEVENT_FD_READ) { + nbt_name_socket_recv(nbtsock); + } +} + + +/* + initialise a nbt_name_socket. The event_ctx is optional, if provided + then operations will use that event context +*/ +_PUBLIC_ struct nbt_name_socket *nbt_name_socket_init(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx) +{ + struct nbt_name_socket *nbtsock; + NTSTATUS status; + + nbtsock = talloc(mem_ctx, struct nbt_name_socket); + if (nbtsock == NULL) goto failed; + + nbtsock->event_ctx = event_ctx; + if (nbtsock->event_ctx == NULL) goto failed; + + status = socket_create(nbtsock, "ip", SOCKET_TYPE_DGRAM, + &nbtsock->sock, 0); + if (!NT_STATUS_IS_OK(status)) goto failed; + + socket_set_option(nbtsock->sock, "SO_BROADCAST", "1"); + + nbtsock->idr = idr_init(nbtsock); + if (nbtsock->idr == NULL) goto failed; + + nbtsock->send_queue = NULL; + nbtsock->num_pending = 0; + nbtsock->incoming.handler = NULL; + nbtsock->unexpected.handler = NULL; + + nbtsock->fde = tevent_add_fd(nbtsock->event_ctx, nbtsock, + socket_get_fd(nbtsock->sock), 0, + nbt_name_socket_handler, nbtsock); + + return nbtsock; + +failed: + talloc_free(nbtsock); + return NULL; +} + +/* + send off a nbt name request +*/ +struct nbt_name_request *nbt_name_request_send(TALLOC_CTX *mem_ctx, + struct nbt_name_socket *nbtsock, + struct socket_address *dest, + struct nbt_name_packet *request, + int timeout, int retries, + bool allow_multiple_replies) +{ + struct nbt_name_request *req; + int id; + enum ndr_err_code ndr_err; + + req = talloc_zero(mem_ctx, struct nbt_name_request); + if (req == NULL) goto failed; + + req->nbtsock = nbtsock; + req->allow_multiple_replies = allow_multiple_replies; + req->state = NBT_REQUEST_SEND; + req->is_reply = false; + req->timeout = timeout; + req->num_retries = retries; + req->dest = socket_address_copy(req, dest); + if (req->dest == NULL) goto failed; + + /* we select a random transaction id unless the user supplied one */ + if (request->name_trn_id == 0) { + id = idr_get_new_random(req->nbtsock->idr, req, UINT16_MAX); + } else { + if (idr_find(req->nbtsock->idr, request->name_trn_id)) goto failed; + id = idr_get_new_above(req->nbtsock->idr, req, request->name_trn_id, + UINT16_MAX); + } + if (id == -1) goto failed; + + request->name_trn_id = id; + req->name_trn_id = id; + + req->te = tevent_add_timer(nbtsock->event_ctx, req, + timeval_current_ofs(req->timeout, 0), + nbt_name_socket_timeout, req); + + talloc_set_destructor(req, nbt_name_request_destructor); + + ndr_err = ndr_push_struct_blob(&req->encoded, req, + request, + (ndr_push_flags_fn_t)ndr_push_nbt_name_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) goto failed; + + DLIST_ADD_END(nbtsock->send_queue, req); + + if (DEBUGLVL(10)) { + DEBUG(10,("Queueing nbt packet to %s:%d\n", + req->dest->addr, req->dest->port)); + NDR_PRINT_DEBUG(nbt_name_packet, request); + } + + TEVENT_FD_WRITEABLE(nbtsock->fde); + + return req; + +failed: + talloc_free(req); + return NULL; +} + + +/* + send off a nbt name reply +*/ +_PUBLIC_ NTSTATUS nbt_name_reply_send(struct nbt_name_socket *nbtsock, + struct socket_address *dest, + struct nbt_name_packet *request) +{ + struct nbt_name_request *req; + enum ndr_err_code ndr_err; + + req = talloc_zero(nbtsock, struct nbt_name_request); + NT_STATUS_HAVE_NO_MEMORY(req); + + req->nbtsock = nbtsock; + req->dest = socket_address_copy(req, dest); + if (req->dest == NULL) goto failed; + req->state = NBT_REQUEST_SEND; + req->is_reply = true; + + talloc_set_destructor(req, nbt_name_request_destructor); + + if (DEBUGLVL(10)) { + NDR_PRINT_DEBUG(nbt_name_packet, request); + } + + ndr_err = ndr_push_struct_blob(&req->encoded, req, + request, + (ndr_push_flags_fn_t)ndr_push_nbt_name_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(req); + return ndr_map_error2ntstatus(ndr_err); + } + + DLIST_ADD_END(nbtsock->send_queue, req); + + TEVENT_FD_WRITEABLE(nbtsock->fde); + + return NT_STATUS_OK; + +failed: + talloc_free(req); + return NT_STATUS_NO_MEMORY; +} + +/* + wait for a nbt request to complete +*/ +NTSTATUS nbt_name_request_recv(struct nbt_name_request *req) +{ + if (!req) return NT_STATUS_NO_MEMORY; + + while (req->state < NBT_REQUEST_DONE) { + if (tevent_loop_once(req->nbtsock->event_ctx) != 0) { + req->state = NBT_REQUEST_ERROR; + req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + break; + } + } + return req->status; +} + + +/* + setup a handler for incoming requests +*/ +_PUBLIC_ NTSTATUS nbt_set_incoming_handler(struct nbt_name_socket *nbtsock, + void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *, + struct socket_address *), + void *private_data) +{ + nbtsock->incoming.handler = handler; + nbtsock->incoming.private_data = private_data; + TEVENT_FD_READABLE(nbtsock->fde); + return NT_STATUS_OK; +} + +/* + setup a handler for unexpected requests +*/ +NTSTATUS nbt_set_unexpected_handler(struct nbt_name_socket *nbtsock, + void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *, + struct socket_address *), + void *private_data) +{ + nbtsock->unexpected.handler = handler; + nbtsock->unexpected.private_data = private_data; + TEVENT_FD_READABLE(nbtsock->fde); + return NT_STATUS_OK; +} + +/* + turn a NBT rcode into a NTSTATUS +*/ +_PUBLIC_ NTSTATUS nbt_rcode_to_ntstatus(uint8_t rcode) +{ + size_t i; + struct { + enum nbt_rcode rcode; + NTSTATUS status; + } map[] = { + { NBT_RCODE_FMT, NT_STATUS_INVALID_PARAMETER }, + { NBT_RCODE_SVR, NT_STATUS_SERVER_DISABLED }, + { NBT_RCODE_NAM, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NBT_RCODE_IMP, NT_STATUS_NOT_SUPPORTED }, + { NBT_RCODE_RFS, NT_STATUS_ACCESS_DENIED }, + { NBT_RCODE_ACT, NT_STATUS_ADDRESS_ALREADY_EXISTS }, + { NBT_RCODE_CFT, NT_STATUS_CONFLICTING_ADDRESSES } + }; + for (i=0;i<ARRAY_SIZE(map);i++) { + if (map[i].rcode == rcode) { + return map[i].status; + } + } + return NT_STATUS_UNSUCCESSFUL; +} diff --git a/libcli/nbt/pynbt.c b/libcli/nbt/pynbt.c new file mode 100644 index 0000000..0908a6b --- /dev/null +++ b/libcli/nbt/pynbt.c @@ -0,0 +1,447 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright © Jelmer Vernooij <jelmer@samba.org> 2008 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include <Python.h> +#include "includes.h" +#include "python/py3compat.h" +#include "libcli/util/pyerrors.h" +#include "python/modules.h" +#include "../libcli/nbt/libnbt.h" +#include "lib/events/events.h" + +void initnetbios(void); + +extern PyTypeObject nbt_node_Type; + +typedef struct { + PyObject_HEAD + TALLOC_CTX *mem_ctx; + struct nbt_name_socket *socket; +} nbt_node_Object; + +static void py_nbt_node_dealloc(nbt_node_Object *self) +{ + talloc_free(self->mem_ctx); + Py_TYPE(self)->tp_free(self); +} + +static PyObject *py_nbt_node_init(PyTypeObject *self, PyObject *args, PyObject *kwargs) +{ + struct tevent_context *ev; + nbt_node_Object *ret = PyObject_New(nbt_node_Object, &nbt_node_Type); + + ret->mem_ctx = talloc_new(NULL); + if (ret->mem_ctx == NULL) + return NULL; + + ev = s4_event_context_init(ret->mem_ctx); + ret->socket = nbt_name_socket_init(ret->mem_ctx, ev); + return (PyObject *)ret; +} + +static bool PyObject_AsDestinationTuple(PyObject *obj, const char **dest_addr, uint16_t *dest_port) +{ + if (PyUnicode_Check(obj)) { + *dest_addr = PyUnicode_AsUTF8(obj); + *dest_port = NBT_NAME_SERVICE_PORT; + return true; + } + + if (PyTuple_Check(obj)) { + if (PyTuple_Size(obj) < 1) { + PyErr_SetString(PyExc_TypeError, "Destination tuple size invalid"); + return false; + } + + if (!PyUnicode_Check(PyTuple_GetItem(obj, 0))) { + PyErr_SetString(PyExc_TypeError, "Destination tuple first element not string"); + return false; + } + + *dest_addr = PyUnicode_AsUTF8(obj); + + if (PyTuple_Size(obj) == 1) { + *dest_port = NBT_NAME_SERVICE_PORT; + return true; + } else if (PyLong_Check(PyTuple_GetItem(obj, 1))) { + *dest_port = PyLong_AsLong(PyTuple_GetItem(obj, 1)); + return true; + } else { + PyErr_SetString(PyExc_TypeError, "Destination tuple second element not a port"); + return false; + } + } + + PyErr_SetString(PyExc_TypeError, "Destination tuple second element not a port"); + return false; +} + +static bool PyObject_AsNBTName(PyObject *obj, struct nbt_name_socket *name_socket, struct nbt_name *name) +{ + if (PyTuple_Check(obj)) { + if (PyTuple_Size(obj) == 2) { + name->name = PyUnicode_AsUTF8(PyTuple_GetItem(obj, 0)); + if (name->name == NULL) { + goto err; + } + name->type = PyLong_AsLong(PyTuple_GetItem(obj, 1)); + if (name->type == -1 && PyErr_Occurred()) { + goto err; + } + name->scope = NULL; + return true; + } else if (PyTuple_Size(obj) == 3) { + name->name = PyUnicode_AsUTF8(PyTuple_GetItem(obj, 0)); + if (name->name == NULL) { + goto err; + } + name->scope = PyUnicode_AsUTF8(PyTuple_GetItem(obj, 1)); + if (name->scope == NULL) { + goto err; + } + name->type = PyLong_AsLong(PyTuple_GetItem(obj, 2)); + if (name->type == -1 && PyErr_Occurred()) { + goto err; + } + return true; + } else { + PyErr_SetString(PyExc_TypeError, "Invalid tuple size"); + return false; + } + } + + if (PyUnicode_Check(obj)) { + /* FIXME: Parse string to be able to interpret things like RHONWYN<02> ? */ + name->name = PyUnicode_AsUTF8(obj); + if (name->name == NULL) { + goto err; + } + name->scope = NULL; + name->type = 0; + return true; + } +err: + PyErr_SetString(PyExc_TypeError, "Invalid type for object"); + return false; +} + +static PyObject *PyObject_FromNBTName(struct nbt_name_socket *name_socket, + struct nbt_name *name) +{ + if (name->scope) { + return Py_BuildValue("(ssi)", name->name, name->scope, name->type); + } else { + return Py_BuildValue("(si)", name->name, name->type); + } +} + +static PyObject *py_nbt_name_query(PyObject *self, PyObject *args, PyObject *kwargs) +{ + nbt_node_Object *node = (nbt_node_Object *)self; + PyObject *ret, *reply_addrs, *py_dest, *py_name; + struct nbt_name_query io; + NTSTATUS status; + int i; + + const char *kwnames[] = { "name", "dest", "broadcast", "wins", "timeout", + "retries", NULL }; + io.in.broadcast = true; + io.in.wins_lookup = false; + io.in.timeout = 0; + io.in.retries = 3; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bbii:query_name", + discard_const_p(char *, kwnames), + &py_name, &py_dest, + &io.in.broadcast, &io.in.wins_lookup, + &io.in.timeout, &io.in.retries)) { + return NULL; + } + + if (!PyObject_AsDestinationTuple(py_dest, &io.in.dest_addr, &io.in.dest_port)) + return NULL; + + if (!PyObject_AsNBTName(py_name, node->socket, &io.in.name)) + return NULL; + + status = nbt_name_query(node->socket, NULL, &io); + + if (NT_STATUS_IS_ERR(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + + ret = PyTuple_New(3); + if (ret == NULL) + return NULL; + PyTuple_SetItem(ret, 0, PyUnicode_FromString(io.out.reply_from)); + + py_name = PyObject_FromNBTName(node->socket, &io.out.name); + if (py_name == NULL) + return NULL; + + PyTuple_SetItem(ret, 1, py_name); + + reply_addrs = PyList_New(io.out.num_addrs); + if (reply_addrs == NULL) { + Py_DECREF(ret); + return NULL; + } + + for (i = 0; i < io.out.num_addrs; i++) { + PyList_SetItem(reply_addrs, i, PyUnicode_FromString(io.out.reply_addrs[i])); + } + + PyTuple_SetItem(ret, 2, reply_addrs); + return ret; +} + +static PyObject *py_nbt_name_status(PyObject *self, PyObject *args, PyObject *kwargs) +{ + nbt_node_Object *node = (nbt_node_Object *)self; + PyObject *ret, *py_dest, *py_name, *py_names; + struct nbt_name_status io; + int i; + NTSTATUS status; + + const char *kwnames[] = { "name", "dest", "timeout", "retries", NULL }; + + io.in.timeout = 0; + io.in.retries = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|ii:name_status", + discard_const_p(char *, kwnames), + &py_name, &py_dest, + &io.in.timeout, &io.in.retries)) { + return NULL; + } + + if (!PyObject_AsDestinationTuple(py_dest, &io.in.dest_addr, &io.in.dest_port)) + return NULL; + + if (!PyObject_AsNBTName(py_name, node->socket, &io.in.name)) + return NULL; + + status = nbt_name_status(node->socket, NULL, &io); + + if (NT_STATUS_IS_ERR(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + + ret = PyTuple_New(3); + if (ret == NULL) + return NULL; + PyTuple_SetItem(ret, 0, PyUnicode_FromString(io.out.reply_from)); + + py_name = PyObject_FromNBTName(node->socket, &io.out.name); + if (py_name == NULL) + return NULL; + + PyTuple_SetItem(ret, 1, py_name); + + py_names = PyList_New(io.out.status.num_names); + + for (i = 0; i < io.out.status.num_names; i++) { + PyList_SetItem(py_names, i, Py_BuildValue("(sii)", + io.out.status.names[i].name, + io.out.status.names[i].nb_flags, + io.out.status.names[i].type)); + } + + PyTuple_SetItem(ret, 2, py_names); + + return ret; +} + +static PyObject *py_nbt_name_register(PyObject *self, PyObject *args, PyObject *kwargs) +{ + nbt_node_Object *node = (nbt_node_Object *)self; + PyObject *ret, *py_dest, *py_name; + struct nbt_name_register io; + NTSTATUS status; + + const char *kwnames[] = { "name", "address", "dest", "register_demand", "broadcast", + "multi_homed", "ttl", "timeout", "retries", NULL }; + + io.in.broadcast = true; + io.in.multi_homed = true; + io.in.register_demand = true; + io.in.ttl = 0; + io.in.timeout = 0; + io.in.retries = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OsO|bbbiii:query_name", + discard_const_p(char *, kwnames), + &py_name, &io.in.address, &py_dest, + &io.in.register_demand, + &io.in.broadcast, &io.in.multi_homed, + &io.in.ttl, &io.in.timeout, &io.in.retries)) { + return NULL; + } + + if (!PyObject_AsDestinationTuple(py_dest, &io.in.dest_addr, &io.in.dest_port)) + return NULL; + + if (!PyObject_AsNBTName(py_name, node->socket, &io.in.name)) + return NULL; + + status = nbt_name_register(node->socket, NULL, &io); + + if (NT_STATUS_IS_ERR(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + + ret = PyTuple_New(4); + if (ret == NULL) + return NULL; + PyTuple_SetItem(ret, 0, PyUnicode_FromString(io.out.reply_from)); + + py_name = PyObject_FromNBTName(node->socket, &io.out.name); + if (py_name == NULL) + return NULL; + + PyTuple_SetItem(ret, 1, py_name); + + PyTuple_SetItem(ret, 2, PyUnicode_FromString(io.out.reply_addr)); + + PyTuple_SetItem(ret, 3, PyLong_FromLong(io.out.rcode)); + + return ret; +} + +static PyObject *py_nbt_name_refresh(PyObject *self, PyObject *args, PyObject *kwargs) +{ + nbt_node_Object *node = (nbt_node_Object *)self; + PyObject *ret, *py_dest, *py_name; + struct nbt_name_refresh io; + NTSTATUS status; + + const char *kwnames[] = { "name", "address", "dest", "nb_flags", "broadcast", + "ttl", "timeout", "retries", NULL }; + + io.in.broadcast = true; + io.in.nb_flags = 0; + io.in.ttl = 0; + io.in.timeout = 0; + io.in.retries = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OsO|ibiii:query_name", + discard_const_p(char *, kwnames), + &py_name, &io.in.address, &py_dest, + &io.in.nb_flags, + &io.in.broadcast, + &io.in.ttl, &io.in.timeout, &io.in.retries)) { + return NULL; + } + + if (!PyObject_AsDestinationTuple(py_dest, &io.in.dest_addr, &io.in.dest_port)) + return NULL; + + if (!PyObject_AsNBTName(py_name, node->socket, &io.in.name)) + return NULL; + + status = nbt_name_refresh(node->socket, NULL, &io); + + if (NT_STATUS_IS_ERR(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + + ret = PyTuple_New(3); + if (ret == NULL) + return NULL; + PyTuple_SetItem(ret, 0, PyUnicode_FromString(io.out.reply_from)); + + py_name = PyObject_FromNBTName(node->socket, &io.out.name); + if (py_name == NULL) + return NULL; + + PyTuple_SetItem(ret, 1, py_name); + + PyTuple_SetItem(ret, 2, PyUnicode_FromString(io.out.reply_addr)); + + PyTuple_SetItem(ret, 3, PyLong_FromLong(io.out.rcode)); + + return ret; +} + +static PyObject *py_nbt_name_release(PyObject *self, PyObject *args, PyObject *kwargs) +{ + Py_RETURN_NONE; /* FIXME */ +} + +static PyMethodDef py_nbt_methods[] = { + { "query_name", PY_DISCARD_FUNC_SIG(PyCFunction, py_nbt_name_query), + METH_VARARGS|METH_KEYWORDS, + "S.query_name(name, dest, broadcast=True, wins=False, timeout=0, retries=3) -> (reply_from, name, reply_addr)\n" + "Query for a NetBIOS name" }, + { "register_name", PY_DISCARD_FUNC_SIG(PyCFunction, + py_nbt_name_register), + METH_VARARGS|METH_KEYWORDS, + "S.register_name(name, address, dest, register_demand=True, broadcast=True, multi_homed=True, ttl=0, timeout=0, retries=0) -> (reply_from, name, reply_addr, rcode)\n" + "Register a new name" }, + { "release_name", PY_DISCARD_FUNC_SIG(PyCFunction, py_nbt_name_release), + METH_VARARGS|METH_KEYWORDS, "S.release_name(name, address, dest, nb_flags=0, broadcast=true, timeout=0, retries=3) -> (reply_from, name, reply_addr, rcode)\n" + "release a previously registered name" }, + { "refresh_name", PY_DISCARD_FUNC_SIG(PyCFunction, py_nbt_name_refresh), + METH_VARARGS|METH_KEYWORDS, "S.refresh_name(name, address, dest, nb_flags=0, broadcast=True, ttl=0, timeout=0, retries=0) -> (reply_from, name, reply_addr, rcode)\n" + "release a previously registered name" }, + { "name_status", PY_DISCARD_FUNC_SIG(PyCFunction, py_nbt_name_status), + METH_VARARGS|METH_KEYWORDS, + "S.name_status(name, dest, timeout=0, retries=0) -> (reply_from, name, status)\n" + "Find the status of a name" }, + + {0} +}; + +PyTypeObject nbt_node_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "netbios.Node", + .tp_basicsize = sizeof(nbt_node_Object), + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_new = py_nbt_node_init, + .tp_dealloc = (destructor)py_nbt_node_dealloc, + .tp_methods = py_nbt_methods, + .tp_doc = "Node()\n" + "Create a new NetBIOS node\n" +}; + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + .m_name = "netbios", + .m_doc = "NetBIOS over TCP/IP support", + .m_size = -1, + .m_methods = NULL, +}; + +MODULE_INIT_FUNC(netbios) +{ + PyObject *mod = NULL; + if (PyType_Ready(&nbt_node_Type) < 0) + return mod; + + mod = PyModule_Create(&moduledef); + + + Py_INCREF((PyObject *)&nbt_node_Type); + PyModule_AddObject(mod, "Node", (PyObject *)&nbt_node_Type); + return mod; +} diff --git a/libcli/nbt/tools/nmblookup.c b/libcli/nbt/tools/nmblookup.c new file mode 100644 index 0000000..6ca38fa --- /dev/null +++ b/libcli/nbt/tools/nmblookup.c @@ -0,0 +1,477 @@ +/* + Unix SMB/CIFS implementation. + + NBT client - used to lookup netbios names + + Copyright (C) Andrew Tridgell 1994-2005 + Copyright (C) Jelmer Vernooij 2003 (Conversion to popt) + + 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 3 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, see <http://www.gnu.org/licenses/>. + +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "lib/socket/socket.h" +#include "lib/events/events.h" +#include "system/network.h" +#include "system/locale.h" +#include "lib/socket/netif.h" +#include "librpc/gen_ndr/nbt.h" +#include "../libcli/nbt/libnbt.h" +#include "param/param.h" + +#include <string.h> + +#define MAX_NETBIOSNAME_LEN 16 + +/* command line options */ +static struct { + const char *broadcast_address; + const char *unicast_address; + bool find_master; + bool wins_lookup; + bool node_status; + bool root_port; + bool lookup_by_ip; + bool case_sensitive; +} options; + +/* + clean any binary from a node name +*/ +static const char *clean_name(TALLOC_CTX *mem_ctx, const char *name) +{ + char *ret = talloc_strdup(mem_ctx, name); + int i; + for (i=0;ret[i];i++) { + if (!isprint((unsigned char)ret[i])) ret[i] = '.'; + } + return ret; +} + +/* + turn a node status flags field into a string +*/ +static char *node_status_flags(TALLOC_CTX *mem_ctx, uint16_t flags) +{ + char *ret; + const char *group = " "; + const char *type = "B"; + + if (flags & NBT_NM_GROUP) { + group = "<GROUP>"; + } + + switch (flags & NBT_NM_OWNER_TYPE) { + case NBT_NODE_B: + type = "B"; + break; + case NBT_NODE_P: + type = "P"; + break; + case NBT_NODE_M: + type = "M"; + break; + case NBT_NODE_H: + type = "H"; + break; + } + + ret = talloc_asprintf(mem_ctx, "%s %s", group, type); + + if (flags & NBT_NM_DEREGISTER) { + ret = talloc_asprintf_append_buffer(ret, " <DEREGISTERING>"); + } + if (flags & NBT_NM_CONFLICT) { + ret = talloc_asprintf_append_buffer(ret, " <CONFLICT>"); + } + if (flags & NBT_NM_ACTIVE) { + ret = talloc_asprintf_append_buffer(ret, " <ACTIVE>"); + } + if (flags & NBT_NM_PERMANENT) { + ret = talloc_asprintf_append_buffer(ret, " <PERMANENT>"); + } + + return ret; +} + +/* do a single node status */ +static bool do_node_status(struct nbt_name_socket *nbtsock, + const char *addr, uint16_t port) +{ + struct nbt_name_status io; + NTSTATUS status; + + io.in.name.name = "*"; + io.in.name.type = NBT_NAME_CLIENT; + io.in.name.scope = NULL; + io.in.dest_addr = addr; + io.in.dest_port = port; + io.in.timeout = 1; + io.in.retries = 2; + + status = nbt_name_status(nbtsock, nbtsock, &io); + if (NT_STATUS_IS_OK(status)) { + int i; + printf("Node status reply from %s\n", + io.out.reply_from); + for (i=0;i<io.out.status.num_names;i++) { + d_printf("\t%-16s <%02x> %s\n", + clean_name(nbtsock, io.out.status.names[i].name), + io.out.status.names[i].type, + node_status_flags(nbtsock, io.out.status.names[i].nb_flags)); + } + printf("\n\tMAC Address = %02X-%02X-%02X-%02X-%02X-%02X\n", + io.out.status.statistics.unit_id[0], + io.out.status.statistics.unit_id[1], + io.out.status.statistics.unit_id[2], + io.out.status.statistics.unit_id[3], + io.out.status.statistics.unit_id[4], + io.out.status.statistics.unit_id[5]); + return true; + } + + return false; +} + +/* do a single node query */ +static NTSTATUS do_node_query(struct nbt_name_socket *nbtsock, + const char *addr, + uint16_t port, + const char *node_name, + enum nbt_name_type node_type, + bool broadcast) +{ + struct nbt_name_query io; + NTSTATUS status; + int i; + + io.in.name.name = node_name; + io.in.name.type = node_type; + io.in.name.scope = NULL; + io.in.dest_addr = addr; + io.in.dest_port = port; + io.in.broadcast = broadcast; + io.in.wins_lookup = options.wins_lookup; + io.in.timeout = 1; + io.in.retries = 2; + + status = nbt_name_query(nbtsock, nbtsock, &io); + NT_STATUS_NOT_OK_RETURN(status); + + for (i=0;i<io.out.num_addrs;i++) { + printf("%s %s<%02x>\n", + io.out.reply_addrs[i], + io.out.name.name, + io.out.name.type); + } + if (options.node_status && io.out.num_addrs > 0) { + do_node_status(nbtsock, io.out.reply_addrs[0], port); + } + + return status; +} + + +static bool process_one(struct loadparm_context *lp_ctx, struct tevent_context *ev, + struct interface *ifaces, const char *name, int nbt_port) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + enum nbt_name_type node_type = NBT_NAME_CLIENT; + char *node_name, *p; + struct socket_address *all_zero_addr; + struct nbt_name_socket *nbtsock; + NTSTATUS status = NT_STATUS_OK; + size_t nbt_len; + bool ret = true; + + if (!options.case_sensitive) { + name = strupper_talloc(tmp_ctx, name); + } + + if (options.find_master) { + node_type = NBT_NAME_MASTER; + if (*name == '-' || *name == '_') { + name = "\01\02__MSBROWSE__\02"; + node_type = NBT_NAME_MS; + } + } + + p = strchr(name, '#'); + if (p) { + node_name = talloc_strndup(tmp_ctx, name, PTR_DIFF(p,name)); + node_type = (enum nbt_name_type)strtol(p+1, NULL, 16); + } else { + node_name = talloc_strdup(tmp_ctx, name); + } + + nbt_len = strlen(node_name); + if (nbt_len > MAX_NETBIOSNAME_LEN - 1) { + printf("The specified netbios name [%s] is too long.\n", + node_name); + talloc_free(tmp_ctx); + return false; + } + + nbtsock = nbt_name_socket_init(tmp_ctx, ev); + + if (options.root_port) { + all_zero_addr = socket_address_from_strings(tmp_ctx, nbtsock->sock->backend_name, + "0.0.0.0", NBT_NAME_SERVICE_PORT); + + if (!all_zero_addr) { + talloc_free(tmp_ctx); + return false; + } + + status = socket_listen(nbtsock->sock, all_zero_addr, 0, 0); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to bind to local port 137 - %s\n", nt_errstr(status)); + talloc_free(tmp_ctx); + return false; + } + } + + if (options.lookup_by_ip) { + ret = do_node_status(nbtsock, name, nbt_port); + talloc_free(tmp_ctx); + return ret; + } + + if (options.broadcast_address) { + status = do_node_query(nbtsock, options.broadcast_address, nbt_port, + node_name, node_type, true); + } else if (options.unicast_address) { + status = do_node_query(nbtsock, options.unicast_address, + nbt_port, node_name, node_type, false); + } else { + int i, num_interfaces; + + num_interfaces = iface_list_count(ifaces); + for (i=0;i<num_interfaces;i++) { + const char *bcast = iface_list_n_bcast(ifaces, i); + if (bcast == NULL) continue; + status = do_node_query(nbtsock, bcast, nbt_port, + node_name, node_type, true); + if (NT_STATUS_IS_OK(status)) break; + } + } + + if (!NT_STATUS_IS_OK(status)) { + printf("Lookup failed - %s\n", nt_errstr(status)); + ret = false; + } + + talloc_free(tmp_ctx); + return ret; +} + +/* + main program +*/ +int main(int argc, const char *argv[]) +{ + bool ret = true; + struct interface *ifaces; + struct tevent_context *ev; + poptContext pc; + int opt; + struct loadparm_context *lp_ctx = NULL; + TALLOC_CTX *mem_ctx = NULL; + bool ok; + enum { + OPT_BROADCAST_ADDRESS = 1000, + OPT_UNICAST_ADDRESS, + OPT_FIND_MASTER, + OPT_WINS_LOOKUP, + OPT_NODE_STATUS, + OPT_ROOT_PORT, + OPT_LOOKUP_BY_IP, + OPT_CASE_SENSITIVE + }; + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "broadcast", + .shortName = 'B', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = OPT_BROADCAST_ADDRESS, + .descrip = "Specify address to use for broadcasts", + .argDescrip = "BROADCAST-ADDRESS" + }, + { + .longName = "unicast", + .shortName = 'U', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = OPT_UNICAST_ADDRESS, + .descrip = "Specify address to use for unicast", + .argDescrip = NULL + }, + { + .longName = "master-browser", + .shortName = 'M', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = OPT_FIND_MASTER, + .descrip = "Search for a master browser", + .argDescrip = NULL + }, + { + .longName = "wins", + .shortName = 'W', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = OPT_WINS_LOOKUP, + .descrip = "Do a WINS lookup", + .argDescrip = NULL + }, + { + .longName = "status", + .shortName = 'S', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = OPT_NODE_STATUS, + .descrip = "Lookup node status as well", + .argDescrip = NULL + }, + { + .longName = "root-port", + .shortName = 'r', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = OPT_ROOT_PORT, + .descrip = "Use root port 137 (Win95 only replies to this)", + .argDescrip = NULL + }, + { + .longName = "lookup-by-ip", + .shortName = 'A', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = OPT_LOOKUP_BY_IP, + .descrip = "Do a node status on <name> as an IP Address", + .argDescrip = NULL + }, + { + .longName = "case-sensitive", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = OPT_CASE_SENSITIVE, + .descrip = "Don't uppercase the name before sending", + .argDescrip = NULL + }, + POPT_COMMON_SAMBA + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + mem_ctx = talloc_init("nmblookup.c/main"); + if (mem_ctx == NULL) { + exit(ENOMEM); + } + + ok = samba_cmdline_init(mem_ctx, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(mem_ctx); + exit(1); + } + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(mem_ctx); + exit(1); + } + + poptSetOtherOptionHelp(pc, "<NODE> ..."); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + case OPT_BROADCAST_ADDRESS: + options.broadcast_address = poptGetOptArg(pc); + break; + case OPT_UNICAST_ADDRESS: + options.unicast_address = poptGetOptArg(pc); + break; + case OPT_FIND_MASTER: + options.find_master = true; + break; + case OPT_WINS_LOOKUP: + options.wins_lookup = true; + break; + case OPT_NODE_STATUS: + options.node_status = true; + break; + case OPT_ROOT_PORT: + options.root_port = true; + break; + case OPT_LOOKUP_BY_IP: + options.lookup_by_ip = true; + break; + case OPT_CASE_SENSITIVE: + options.case_sensitive = true; + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + /* swallow argv[0] */ + poptGetArg(pc); + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + TALLOC_FREE(mem_ctx); + exit(1); + } + + lp_ctx = samba_cmdline_get_lp_ctx(); + + load_interface_list(mem_ctx, lp_ctx, &ifaces); + + ev = s4_event_context_init(mem_ctx); + + while (poptPeekArg(pc)) { + const char *name = poptGetArg(pc); + + ret &= process_one(lp_ctx, + ev, + ifaces, + name, + lpcfg_nbt_port(lp_ctx)); + } + + poptFreeContext(pc); + TALLOC_FREE(mem_ctx); + + if (!ret) { + return 1; + } + + return 0; +} diff --git a/libcli/nbt/wscript_build b/libcli/nbt/wscript_build new file mode 100644 index 0000000..0e2a13f --- /dev/null +++ b/libcli/nbt/wscript_build @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +bld.SAMBA_SUBSYSTEM('NDR_NBT_BUF', + source='nbtname.c', + deps='talloc', + autoproto='nbtname.h' + ) + +bld.SAMBA_SUBSYSTEM('lmhosts', + source='lmhosts.c', + deps='replace talloc' + ) + +bld.SAMBA_LIBRARY('cli-nbt', + source='nbtsocket.c namequery.c nameregister.c namerefresh.c namerelease.c', + public_deps='ndr ndr_nbt tevent tevent-util NDR_SECURITY samba_socket samba-util lmhosts', + private_library=True + ) + +bld.SAMBA_BINARY('nmblookup' + bld.env.suffix4, + source='tools/nmblookup.c', + manpages='man/nmblookup4.1', + deps='samba-hostconfig samba-util cli-nbt popt CMDLINE_S4 netif LIBCLI_RESOLVE', + install=False, + ) + +bld.SAMBA_PYTHON('python_netbios', + source='pynbt.c', + public_deps='cli-nbt DYNCONFIG samba-hostconfig', + realname='samba/netbios.so' + ) + |