diff options
author | bubulle <bubulle@alioth.debian.org> | 2009-07-06 18:17:09 +0000 |
---|---|---|
committer | bubulle <bubulle@alioth.debian.org> | 2009-07-06 18:17:09 +0000 |
commit | 1e4514a1edfdd1dde65a7567a7d7328de40e3493 (patch) | |
tree | 889204356442a0e13d8b3b2deaa1e0b310cea940 /libcli/nbt | |
parent | 956e238a6ed95a84c85c14ac898ffee166c35287 (diff) | |
download | samba-1e4514a1edfdd1dde65a7567a7d7328de40e3493.tar.gz |
merge upstream 3.4.0
git-svn-id: svn://svn.debian.org/svn/pkg-samba/trunk/samba@2936 fc4039ab-9d04-0410-8cac-899223bdd6b0
Diffstat (limited to 'libcli/nbt')
-rw-r--r-- | libcli/nbt/libnbt.h | 358 | ||||
-rw-r--r-- | libcli/nbt/man/nmblookup.1.xml | 223 | ||||
-rw-r--r-- | libcli/nbt/namequery.c | 234 | ||||
-rw-r--r-- | libcli/nbt/namerefresh.c | 301 | ||||
-rw-r--r-- | libcli/nbt/nameregister.c | 441 | ||||
-rw-r--r-- | libcli/nbt/namerelease.c | 134 | ||||
-rw-r--r-- | libcli/nbt/nbt_proto.h | 43 | ||||
-rw-r--r-- | libcli/nbt/nbtname.c | 648 | ||||
-rw-r--r-- | libcli/nbt/nbtsocket.c | 557 | ||||
-rw-r--r-- | libcli/nbt/pynbt.c | 412 | ||||
-rw-r--r-- | libcli/nbt/tools/nmblookup.c | 381 |
11 files changed, 3732 insertions, 0 deletions
diff --git a/libcli/nbt/libnbt.h b/libcli/nbt/libnbt.h new file mode 100644 index 0000000000..a37a213b08 --- /dev/null +++ b/libcli/nbt/libnbt.h @@ -0,0 +1,358 @@ +/* + 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; + struct smb_iconv_convenience *iconv_convenience; + + /* 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, + struct smb_iconv_convenience *iconv_convenience); +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, struct nbt_name *name, struct nbt_name *newname); +NTSTATUS nbt_name_to_blob(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, 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_PROTO(wrepl_nbt_name, const struct nbt_name *) +NDR_SCALAR_PROTO(nbt_string, const char *) +NDR_BUFFER_PROTO(nbt_name, struct nbt_name) +NTSTATUS nbt_rcode_to_ntstatus(uint8_t rcode); + +struct composite_context; +struct composite_context *nbt_name_register_bcast_send(struct nbt_name_socket *nbtsock, + struct nbt_name_register_bcast *io); +NTSTATUS nbt_name_register_bcast_recv(struct composite_context *c); +struct composite_context *nbt_name_register_wins_send(struct nbt_name_socket *nbtsock, + struct nbt_name_register_wins *io); +NTSTATUS nbt_name_refresh_wins_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, + struct nbt_name_refresh_wins *io); +struct composite_context *nbt_name_refresh_wins_send(struct nbt_name_socket *nbtsock, + struct nbt_name_refresh_wins *io); +NTSTATUS nbt_name_register_wins_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, + struct nbt_name_register_wins *io); + + +#endif /* __LIBNBT_H__ */ diff --git a/libcli/nbt/man/nmblookup.1.xml b/libcli/nbt/man/nmblookup.1.xml new file mode 100644 index 0000000000..85640da033 --- /dev/null +++ b/libcli/nbt/man/nmblookup.1.xml @@ -0,0 +1,223 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc"> +<refentry id="nmblookup"> + +<refmeta> + <refentrytitle>nmblookup</refentrytitle> + <manvolnum>1</manvolnum> + <refmiscinfo class="source">Samba</refmiscinfo> + <refmiscinfo class="manual">User Commands</refmiscinfo> + <refmiscinfo class="version">3.2</refmiscinfo> +</refmeta> + + +<refnamediv> + <refname>nmblookup</refname> + <refpurpose>NetBIOS over TCP/IP client used to lookup NetBIOS + names</refpurpose> +</refnamediv> + +<refsynopsisdiv> + <cmdsynopsis> + <command>nmblookup</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>nmblookup</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 name <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>nmblookup -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> + + + + &popt.common.connection; + &stdarg.help; + + <varlistentry> + <term>-B <broadcast address></term> + <listitem><para>Send the query to the given broadcast address. Without + this option the default behavior of nmblookup 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> + + &stdarg.server.debug; + &popt.common.samba; + + <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>nmblookup</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>nmblookup</command> + must be called like this:</para> + + <para><command>nmblookup -U server -R 'name'</command></para> + + <para>For example, running :</para> + + <para><command>nmblookup -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 0000000000..e344235faa --- /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, 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, 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 0000000000..ec63df9bf6 --- /dev/null +++ b/libcli/nbt/namerefresh.c @@ -0,0 +1,301 @@ +/* + 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 "../libcli/nbt/libnbt.h" +#include "../libcli/nbt/nbt_proto.h" +#include "libcli/composite/composite.h" +#include "lib/socket/socket.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, 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 refresh_wins_state { + struct nbt_name_socket *nbtsock; + struct nbt_name_refresh *io; + const char **wins_servers; + uint16_t wins_port; + const char **addresses; + int address_idx; + struct nbt_name_request *req; +}; + + +/** + state handler for WINS multi-homed multi-server name refresh +*/ +static void name_refresh_wins_handler(struct nbt_name_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, + struct composite_context); + struct refresh_wins_state *state = talloc_get_type(c->private_data, + struct refresh_wins_state); + NTSTATUS status; + + status = nbt_name_refresh_recv(state->req, state, state->io); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + /* the refresh timed out - try the next WINS server */ + state->wins_servers++; + state->address_idx = 0; + if (state->wins_servers[0] == NULL) { + c->state = COMPOSITE_STATE_ERROR; + c->status = status; + goto done; + } + state->io->in.dest_addr = state->wins_servers[0]; + state->io->in.dest_port = state->wins_port; + state->io->in.address = state->addresses[0]; + state->req = nbt_name_refresh_send(state->nbtsock, state->io); + if (state->req == NULL) { + c->state = COMPOSITE_STATE_ERROR; + c->status = NT_STATUS_NO_MEMORY; + } else { + state->req->async.fn = name_refresh_wins_handler; + state->req->async.private_data = c; + } + } else if (!NT_STATUS_IS_OK(status)) { + c->state = COMPOSITE_STATE_ERROR; + c->status = status; + } else { + 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)]; + state->req = nbt_name_refresh_send(state->nbtsock, state->io); + if (state->req == NULL) { + c->state = COMPOSITE_STATE_ERROR; + c->status = NT_STATUS_NO_MEMORY; + } else { + state->req->async.fn = name_refresh_wins_handler; + state->req->async.private_data = c; + } + } else { + c->state = COMPOSITE_STATE_DONE; + c->status = NT_STATUS_OK; + } + } + +done: + if (c->state >= COMPOSITE_STATE_DONE && + c->async.fn) { + c->async.fn(c); + } +} + +/** + the async send call for a multi-server WINS refresh +*/ +_PUBLIC_ struct composite_context *nbt_name_refresh_wins_send(struct nbt_name_socket *nbtsock, + struct nbt_name_refresh_wins *io) +{ + struct composite_context *c; + struct refresh_wins_state *state; + + c = talloc_zero(nbtsock, struct composite_context); + if (c == NULL) goto failed; + + state = talloc(c, struct refresh_wins_state); + if (state == NULL) goto failed; + + state->io = talloc(state, struct nbt_name_refresh); + if (state->io == NULL) goto failed; + + state->wins_port = io->in.wins_port; + state->wins_servers = (const char **)str_list_copy(state, io->in.wins_servers); + if (state->wins_servers == NULL || + state->wins_servers[0] == NULL) goto failed; + + state->addresses = (const char **)str_list_copy(state, io->in.addresses); + if (state->addresses == NULL || + state->addresses[0] == NULL) goto failed; + + 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; + + state->req = nbt_name_refresh_send(nbtsock, state->io); + if (state->req == NULL) goto failed; + + state->req->async.fn = name_refresh_wins_handler; + state->req->async.private_data = c; + + c->private_data = state; + c->state = COMPOSITE_STATE_IN_PROGRESS; + c->event_ctx = nbtsock->event_ctx; + + return c; + +failed: + talloc_free(c); + return NULL; +} + +/* + multi-homed WINS name refresh - recv side +*/ +_PUBLIC_ NTSTATUS nbt_name_refresh_wins_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, + struct nbt_name_refresh_wins *io) +{ + NTSTATUS status; + status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + struct refresh_wins_state *state = + talloc_get_type(c->private_data, struct refresh_wins_state); + io->out.wins_server = talloc_steal(mem_ctx, state->wins_servers[0]); + io->out.rcode = state->io->out.rcode; + } + talloc_free(c); + return status; +} + +/* + 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) +{ + struct composite_context *c = nbt_name_refresh_wins_send(nbtsock, io); + return nbt_name_refresh_wins_recv(c, mem_ctx, io); +} diff --git a/libcli/nbt/nameregister.c b/libcli/nbt/nameregister.c new file mode 100644 index 0000000000..39ef05640a --- /dev/null +++ b/libcli/nbt/nameregister.c @@ -0,0 +1,441 @@ +/* + 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 "../libcli/nbt/libnbt.h" +#include "../libcli/nbt/nbt_proto.h" +#include "libcli/composite/composite.h" +#include "lib/socket/socket.h" +#include "librpc/gen_ndr/ndr_nbt.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, 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 register_bcast_state { + struct nbt_name_socket *nbtsock; + struct nbt_name_register *io; + struct nbt_name_request *req; +}; + + +/* + state handler for 4 stage name registration +*/ +static void name_register_bcast_handler(struct nbt_name_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, struct composite_context); + struct register_bcast_state *state = talloc_get_type(c->private_data, struct register_bcast_state); + NTSTATUS status; + + status = nbt_name_register_recv(state->req, state, state->io); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + if (state->io->in.register_demand == true) { + /* all done */ + c->state = COMPOSITE_STATE_DONE; + c->status = NT_STATUS_OK; + goto done; + } + + /* the registration timed out - good, send the demand */ + state->io->in.register_demand = true; + state->io->in.retries = 0; + state->req = nbt_name_register_send(state->nbtsock, state->io); + if (state->req == NULL) { + c->state = COMPOSITE_STATE_ERROR; + c->status = NT_STATUS_NO_MEMORY; + } else { + state->req->async.fn = name_register_bcast_handler; + state->req->async.private_data = c; + } + } else if (!NT_STATUS_IS_OK(status)) { + c->state = COMPOSITE_STATE_ERROR; + c->status = status; + } else { + c->state = COMPOSITE_STATE_ERROR; + c->status = NT_STATUS_CONFLICTING_ADDRESSES; + 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)); + } + +done: + if (c->state >= COMPOSITE_STATE_DONE && + c->async.fn) { + c->async.fn(c); + } +} + +/* + the async send call for a 4 stage name registration +*/ +_PUBLIC_ struct composite_context *nbt_name_register_bcast_send(struct nbt_name_socket *nbtsock, + struct nbt_name_register_bcast *io) +{ + struct composite_context *c; + struct register_bcast_state *state; + + c = talloc_zero(nbtsock, struct composite_context); + if (c == NULL) goto failed; + + state = talloc(c, struct register_bcast_state); + if (state == NULL) goto failed; + + state->io = talloc(state, struct nbt_name_register); + if (state->io == NULL) goto failed; + + 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; + + state->req = nbt_name_register_send(nbtsock, state->io); + if (state->req == NULL) goto failed; + + state->req->async.fn = name_register_bcast_handler; + state->req->async.private_data = c; + + c->private_data = state; + c->state = COMPOSITE_STATE_IN_PROGRESS; + c->event_ctx = nbtsock->event_ctx; + + return c; + +failed: + talloc_free(c); + return NULL; +} + +/* + broadcast 4 part name register - recv +*/ +_PUBLIC_ NTSTATUS nbt_name_register_bcast_recv(struct composite_context *c) +{ + NTSTATUS status; + status = composite_wait(c); + talloc_free(c); + return status; +} + +/* + broadcast 4 part name register - sync interface +*/ +NTSTATUS nbt_name_register_bcast(struct nbt_name_socket *nbtsock, + struct nbt_name_register_bcast *io) +{ + struct composite_context *c = nbt_name_register_bcast_send(nbtsock, io); + return nbt_name_register_bcast_recv(c); +} + + +/* + 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 register_wins_state { + struct nbt_name_socket *nbtsock; + struct nbt_name_register *io; + const char **wins_servers; + uint16_t wins_port; + const char **addresses; + int address_idx; + struct nbt_name_request *req; +}; + + +/* + state handler for WINS multi-homed multi-server name register +*/ +static void name_register_wins_handler(struct nbt_name_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, + struct composite_context); + struct register_wins_state *state = talloc_get_type(c->private_data, + struct register_wins_state); + NTSTATUS status; + + status = nbt_name_register_recv(state->req, state, state->io); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + /* the register timed out - try the next WINS server */ + state->wins_servers++; + state->address_idx = 0; + if (state->wins_servers[0] == NULL) { + c->state = COMPOSITE_STATE_ERROR; + c->status = status; + goto done; + } + state->io->in.dest_addr = state->wins_servers[0]; + state->io->in.dest_port = state->wins_port; + state->io->in.address = state->addresses[0]; + state->req = nbt_name_register_send(state->nbtsock, state->io); + if (state->req == NULL) { + c->state = COMPOSITE_STATE_ERROR; + c->status = NT_STATUS_NO_MEMORY; + } else { + state->req->async.fn = name_register_wins_handler; + state->req->async.private_data = c; + } + } else if (!NT_STATUS_IS_OK(status)) { + c->state = COMPOSITE_STATE_ERROR; + c->status = status; + } else { + 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)]; + state->req = nbt_name_register_send(state->nbtsock, state->io); + if (state->req == NULL) { + c->state = COMPOSITE_STATE_ERROR; + c->status = NT_STATUS_NO_MEMORY; + } else { + state->req->async.fn = name_register_wins_handler; + state->req->async.private_data = c; + } + } else { + c->state = COMPOSITE_STATE_DONE; + c->status = NT_STATUS_OK; + } + } + +done: + if (c->state >= COMPOSITE_STATE_DONE && + c->async.fn) { + c->async.fn(c); + } +} + +/* + the async send call for a multi-server WINS register +*/ +_PUBLIC_ struct composite_context *nbt_name_register_wins_send(struct nbt_name_socket *nbtsock, + struct nbt_name_register_wins *io) +{ + struct composite_context *c; + struct register_wins_state *state; + + c = talloc_zero(nbtsock, struct composite_context); + if (c == NULL) goto failed; + + state = talloc(c, struct register_wins_state); + if (state == NULL) goto failed; + + state->io = talloc(state, struct nbt_name_register); + if (state->io == NULL) goto failed; + + state->wins_port = io->in.wins_port; + state->wins_servers = (const char **)str_list_copy(state, io->in.wins_servers); + if (state->wins_servers == NULL || + state->wins_servers[0] == NULL) goto failed; + + state->addresses = (const char **)str_list_copy(state, io->in.addresses); + if (state->addresses == NULL || + state->addresses[0] == NULL) goto failed; + + 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; + + state->req = nbt_name_register_send(nbtsock, state->io); + if (state->req == NULL) goto failed; + + state->req->async.fn = name_register_wins_handler; + state->req->async.private_data = c; + + c->private_data = state; + c->state = COMPOSITE_STATE_IN_PROGRESS; + c->event_ctx = nbtsock->event_ctx; + + return c; + +failed: + talloc_free(c); + return NULL; +} + +/* + multi-homed WINS name register - recv side +*/ +_PUBLIC_ NTSTATUS nbt_name_register_wins_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, + struct nbt_name_register_wins *io) +{ + NTSTATUS status; + status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + struct register_wins_state *state = + talloc_get_type(c->private_data, struct register_wins_state); + io->out.wins_server = talloc_steal(mem_ctx, state->wins_servers[0]); + io->out.rcode = state->io->out.rcode; + } + talloc_free(c); + return status; +} + +/* + 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) +{ + struct composite_context *c = nbt_name_register_wins_send(nbtsock, io); + return nbt_name_register_wins_recv(c, mem_ctx, io); +} diff --git a/libcli/nbt/namerelease.c b/libcli/nbt/namerelease.c new file mode 100644 index 0000000000..8f4698169d --- /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, 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 0000000000..b2e8d8525a --- /dev/null +++ b/libcli/nbt/nbt_proto.h @@ -0,0 +1,43 @@ +#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(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 0000000000..338cb21089 --- /dev/null +++ b/libcli/nbt/nbtname.c @@ -0,0 +1,648 @@ +/* + 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" + +/* don't allow an unlimited number of name components */ +#define MAX_COMPONENTS 10 + +/** + print a nbt string +*/ +_PUBLIC_ void ndr_print_nbt_string(struct ndr_print *ndr, const char *name, const char *s) +{ + ndr_print_string(ndr, name, s); +} + +/* + pull one component of a nbt_string +*/ +static enum ndr_err_code ndr_pull_component(struct ndr_pull *ndr, + uint8_t **component, + uint32_t *offset, + uint32_t *max_offset) +{ + uint8_t len; + uint_t loops = 0; + while (loops < 5) { + if (*offset >= ndr->data_size) { + return ndr_pull_error(ndr, NDR_ERR_STRING, + "BAD NBT NAME component"); + } + len = ndr->data[*offset]; + if (len == 0) { + *offset += 1; + *max_offset = MAX(*max_offset, *offset); + *component = NULL; + return NDR_ERR_SUCCESS; + } + if ((len & 0xC0) == 0xC0) { + /* its a label pointer */ + if (1 + *offset >= ndr->data_size) { + return ndr_pull_error(ndr, NDR_ERR_STRING, + "BAD NBT NAME component"); + } + *max_offset = MAX(*max_offset, *offset + 2); + *offset = ((len&0x3F)<<8) | ndr->data[1 + *offset]; + *max_offset = MAX(*max_offset, *offset); + loops++; + continue; + } + if ((len & 0xC0) != 0) { + /* its a reserved length field */ + return ndr_pull_error(ndr, NDR_ERR_STRING, + "BAD NBT NAME component"); + } + if (*offset + len + 2 > ndr->data_size) { + return ndr_pull_error(ndr, NDR_ERR_STRING, + "BAD NBT NAME component"); + } + *component = (uint8_t*)talloc_strndup(ndr, (const char *)&ndr->data[1 + *offset], len); + NDR_ERR_HAVE_NO_MEMORY(*component); + *offset += len + 1; + *max_offset = MAX(*max_offset, *offset); + return NDR_ERR_SUCCESS; + } + + /* too many pointers */ + return ndr_pull_error(ndr, NDR_ERR_STRING, "BAD NBT NAME component"); +} + +/** + pull a nbt_string from the wire +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_nbt_string(struct ndr_pull *ndr, int ndr_flags, const char **s) +{ + uint32_t offset = ndr->offset; + uint32_t max_offset = offset; + unsigned num_components; + char *name; + + if (!(ndr_flags & NDR_SCALARS)) { + return NDR_ERR_SUCCESS; + } + + name = NULL; + + /* break up name into a list of components */ + for (num_components=0;num_components<MAX_COMPONENTS;num_components++) { + uint8_t *component = NULL; + NDR_CHECK(ndr_pull_component(ndr, &component, &offset, &max_offset)); + if (component == NULL) break; + if (name) { + name = talloc_asprintf_append_buffer(name, ".%s", component); + NDR_ERR_HAVE_NO_MEMORY(name); + } else { + name = (char *)component; + } + } + if (num_components == MAX_COMPONENTS) { + return ndr_pull_error(ndr, NDR_ERR_STRING, + "BAD NBT NAME too many components"); + } + if (num_components == 0) { + name = talloc_strdup(ndr, ""); + NDR_ERR_HAVE_NO_MEMORY(name); + } + + (*s) = name; + ndr->offset = max_offset; + + return NDR_ERR_SUCCESS; +} + +/** + push a nbt string to the wire +*/ +_PUBLIC_ enum ndr_err_code ndr_push_nbt_string(struct ndr_push *ndr, int ndr_flags, const char *s) +{ + if (!(ndr_flags & NDR_SCALARS)) { + return NDR_ERR_SUCCESS; + } + + while (s && *s) { + enum ndr_err_code ndr_err; + char *compname; + size_t complen; + uint32_t offset; + + /* see if we have pushed the remaing string allready, + * if so we use a label pointer to this string + */ + ndr_err = ndr_token_retrieve_cmp_fn(&ndr->nbt_string_list, s, &offset, (comparison_fn_t)strcmp, false); + if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + uint8_t b[2]; + + if (offset > 0x3FFF) { + return ndr_push_error(ndr, NDR_ERR_STRING, + "offset for nbt string label pointer %u[%08X] > 0x00003FFF", + offset, offset); + } + + b[0] = 0xC0 | (offset>>8); + b[1] = (offset & 0xFF); + + return ndr_push_bytes(ndr, b, 2); + } + + complen = strcspn(s, "."); + + /* we need to make sure the length fits into 6 bytes */ + if (complen >= 0x3F) { + return ndr_push_error(ndr, NDR_ERR_STRING, + "component length %u[%08X] > 0x00003F", + (unsigned)complen, (unsigned)complen); + } + + compname = talloc_asprintf(ndr, "%c%*.*s", + (unsigned char)complen, + (unsigned char)complen, + (unsigned char)complen, s); + NDR_ERR_HAVE_NO_MEMORY(compname); + + /* remember the current componemt + the rest of the string + * so it can be reused later + */ + NDR_CHECK(ndr_token_store(ndr, &ndr->nbt_string_list, s, ndr->offset)); + + /* push just this component into the blob */ + NDR_CHECK(ndr_push_bytes(ndr, (const uint8_t *)compname, complen+1)); + talloc_free(compname); + + s += complen; + if (*s == '.') s++; + } + + /* if we reach the end of the string and have pushed the last component + * without using a label pointer, we need to terminate the string + */ + return ndr_push_bytes(ndr, (const uint8_t *)"", 1); +} + + +/* + 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, 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, struct smb_iconv_convenience *iconv_convenience, DATA_BLOB *blob, struct nbt_name *name) +{ + enum ndr_err_code ndr_err; + + ndr_err = ndr_push_struct_blob(blob, mem_ctx, iconv_convenience, 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, NULL, 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, const 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)); + + 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 > 18) { + r->scope = talloc_strndup(r, (char *)(namebuf+17), 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)); + + 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 0000000000..a3e295d3df --- /dev/null +++ b/libcli/nbt/nbtsocket.c @@ -0,0 +1,557 @@ +/* + 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; + } + if (req->te) { + talloc_free(req->te); + req->te = NULL; + } + if (req->nbtsock->send_queue == NULL) { + EVENT_FD_NOT_WRITEABLE(req->nbtsock->fde); + } + if (req->nbtsock->num_pending == 0 && + req->nbtsock->incoming.handler == NULL) { + EVENT_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 = nbtsock->send_queue; + 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 { + EVENT_FD_READABLE(nbtsock->fde); + nbtsock->num_pending++; + } + } + + EVENT_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 = event_add_timed(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, + struct nbt_name_request *); + } + EVENT_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; + } + + blob = data_blob_talloc(tmp_ctx, NULL, dsize); + if (blob.data == NULL) { + 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, nbtsock->iconv_convenience, 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 = event_add_timed(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 & EVENT_FD_WRITE) { + nbt_name_socket_send(nbtsock); + } + if (flags & EVENT_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 smb_iconv_convenience *iconv_convenience) +{ + struct nbt_name_socket *nbtsock; + NTSTATUS status; + + nbtsock = talloc(mem_ctx, struct nbt_name_socket); + if (nbtsock == NULL) goto failed; + + nbtsock->event_ctx = talloc_reference(nbtsock, event_ctx); + if (nbtsock->event_ctx == NULL) goto failed; + + status = socket_create("ip", SOCKET_TYPE_DGRAM, &nbtsock->sock, 0); + if (!NT_STATUS_IS_OK(status)) goto failed; + + socket_set_option(nbtsock->sock, "SO_BROADCAST", "1"); + + talloc_steal(nbtsock, nbtsock->sock); + + 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->iconv_convenience = iconv_convenience; + + nbtsock->fde = event_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(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(nbtsock, 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 = dest; + if (talloc_reference(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 = event_add_timed(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, + req->nbtsock->iconv_convenience, + 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, struct nbt_name_request *); + + 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); + } + + EVENT_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 = dest; + if (talloc_reference(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, + req->nbtsock->iconv_convenience, + 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, struct nbt_name_request *); + + EVENT_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 (event_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; + EVENT_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; + EVENT_FD_READABLE(nbtsock->fde); + return NT_STATUS_OK; +} + +/* + turn a NBT rcode into a NTSTATUS +*/ +_PUBLIC_ NTSTATUS nbt_rcode_to_ntstatus(uint8_t rcode) +{ + int 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 0000000000..9be6c79a8b --- /dev/null +++ b/libcli/nbt/pynbt.c @@ -0,0 +1,412 @@ +/* + 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 "includes.h" +#include <Python.h> +#include "libcli/util/pyerrors.h" +#include "scripting/python/modules.h" +#include "../libcli/nbt/libnbt.h" +#include "lib/events/events.h" + +#ifndef Py_RETURN_NONE +#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None +#endif + +PyAPI_DATA(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(PyObject *obj) +{ + talloc_free(((nbt_node_Object *)obj)->mem_ctx); + PyObject_Del(obj); +} + +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, py_iconv_convenience(ret->mem_ctx)); + return (PyObject *)ret; +} + +static bool PyObject_AsDestinationTuple(PyObject *obj, const char **dest_addr, uint16_t *dest_port) +{ + if (PyString_Check(obj)) { + *dest_addr = PyString_AsString(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 (!PyString_Check(PyTuple_GetItem(obj, 0))) { + PyErr_SetString(PyExc_TypeError, "Destination tuple first element not string"); + return false; + } + + *dest_addr = PyString_AsString(obj); + + if (PyTuple_Size(obj) == 1) { + *dest_port = NBT_NAME_SERVICE_PORT; + return true; + } else if (PyInt_Check(PyTuple_GetItem(obj, 1))) { + *dest_port = PyInt_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 = PyString_AsString(PyTuple_GetItem(obj, 0)); + name->type = PyInt_AsLong(PyTuple_GetItem(obj, 1)); + name->scope = NULL; + return true; + } else if (PyTuple_Size(obj) == 3) { + name->name = PyString_AsString(PyTuple_GetItem(obj, 0)); + name->scope = PyString_AsString(PyTuple_GetItem(obj, 1)); + name->type = PyInt_AsLong(PyTuple_GetItem(obj, 2)); + return true; + } else { + PyErr_SetString(PyExc_TypeError, "Invalid tuple size"); + return false; + } + } + + if (PyString_Check(obj)) { + /* FIXME: Parse string to be able to interpret things like RHONWYN<02> ? */ + name->name = PyString_AsString(obj); + name->scope = NULL; + name->type = 0; + return true; + } + + PyErr_SetString(PyExc_TypeError, "Invalid type for object"); + return false; +} + +static PyObject *PyObject_FromNBTName(struct nbt_name_socket *name_socket, struct smb_iconv_convenience *ic, + 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, PyString_FromString(io.out.reply_from)); + + py_name = PyObject_FromNBTName(node->socket, py_iconv_convenience(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, PyString_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, PyString_FromString(io.out.reply_from)); + + py_name = PyObject_FromNBTName(node->socket, py_iconv_convenience(NULL), &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.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(3); + if (ret == NULL) + return NULL; + PyTuple_SetItem(ret, 0, PyString_FromString(io.out.reply_from)); + + py_name = PyObject_FromNBTName(node->socket, py_iconv_convenience(NULL), &io.out.name); + if (py_name == NULL) + return NULL; + + PyTuple_SetItem(ret, 1, py_name); + + PyTuple_SetItem(ret, 2, PyString_FromString(io.out.reply_addr)); + + PyTuple_SetItem(ret, 3, PyInt_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.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, PyString_FromString(io.out.reply_from)); + + py_name = PyObject_FromNBTName(node->socket, py_iconv_convenience(NULL), &io.out.name); + if (py_name == NULL) + return NULL; + + PyTuple_SetItem(ret, 1, py_name); + + PyTuple_SetItem(ret, 2, PyString_FromString(io.out.reply_addr)); + + PyTuple_SetItem(ret, 3, PyInt_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", (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", (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", (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", (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", (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" }, + + { NULL } +}; + +PyTypeObject nbt_node_Type = { + PyObject_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 = py_nbt_node_dealloc, + .tp_methods = py_nbt_methods, + .tp_doc = "Node()\n" + "Create a new NetBIOS node\n" +}; + +void initnetbios(void) +{ + PyObject *mod; + if (PyType_Ready(&nbt_node_Type) < 0) + return; + + mod = Py_InitModule3("netbios", NULL, "NetBIOS over TCP/IP support"); + + Py_INCREF((PyObject *)&nbt_node_Type); + PyModule_AddObject(mod, "Node", (PyObject *)&nbt_node_Type); +} diff --git a/libcli/nbt/tools/nmblookup.c b/libcli/nbt/tools/nmblookup.c new file mode 100644 index 0000000000..96023f64c3 --- /dev/null +++ b/libcli/nbt/tools/nmblookup.c @@ -0,0 +1,381 @@ +/* + 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/popt_common.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" + +/* 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; + 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); + } + + nbtsock = nbt_name_socket_init(tmp_ctx, ev, lp_iconv_convenience(lp_ctx)); + + 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_count(ifaces); + for (i=0;i<num_interfaces;i++) { + const char *bcast = iface_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; + 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 + { "broadcast", 'B', POPT_ARG_STRING, NULL, OPT_BROADCAST_ADDRESS, + "Specify address to use for broadcasts", "BROADCAST-ADDRESS" }, + + { "unicast", 'U', POPT_ARG_STRING, NULL, OPT_UNICAST_ADDRESS, + "Specify address to use for unicast", NULL }, + + { "master-browser", 'M', POPT_ARG_NONE, NULL, OPT_FIND_MASTER, + "Search for a master browser", NULL }, + + { "wins", 'W', POPT_ARG_NONE, NULL, OPT_WINS_LOOKUP, + "Do a WINS lookup", NULL }, + + { "status", 'S', POPT_ARG_NONE, NULL, OPT_NODE_STATUS, + "Lookup node status as well", NULL }, + + { "root-port", 'r', POPT_ARG_NONE, NULL, OPT_ROOT_PORT, + "Use root port 137 (Win95 only replies to this)", NULL }, + + { "lookup-by-ip", 'A', POPT_ARG_NONE, NULL, OPT_LOOKUP_BY_IP, + "Do a node status on <name> as an IP Address", NULL }, + + { "case-sensitive", 0, POPT_ARG_NONE, NULL, OPT_CASE_SENSITIVE, + "Don't uppercase the name before sending", NULL }, + + POPT_COMMON_SAMBA + { 0, 0, 0, 0 } + }; + + pc = poptGetContext("nmblookup", argc, argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + + 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; + } + } + + /* swallow argv[0] */ + poptGetArg(pc); + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + exit(1); + } + + load_interfaces(NULL, lp_interfaces(cmdline_lp_ctx), &ifaces); + + ev = s4_event_context_init(talloc_autofree_context()); + + while (poptPeekArg(pc)) { + const char *name = poptGetArg(pc); + + ret &= process_one(cmdline_lp_ctx, ev, ifaces, name, lp_nbt_port(cmdline_lp_ctx)); + } + + talloc_free(ev); + + talloc_free(ifaces); + + poptFreeContext(pc); + + if (!ret) { + return 1; + } + + return 0; +} |