diff options
author | Günther Deschner <gd@samba.org> | 2008-09-23 06:58:17 +0200 |
---|---|---|
committer | Günther Deschner <gd@samba.org> | 2008-09-23 09:37:24 +0200 |
commit | 6f33f3e4c26564602d5d13c7459938955b56a16e (patch) | |
tree | feb713280e1bb12228b2982db5123158b4faabae /libcli/nbt | |
parent | be8b72dd5541182e24f522026bafbaef044a47e3 (diff) | |
download | samba-6f33f3e4c26564602d5d13c7459938955b56a16e.tar.gz samba-6f33f3e4c26564602d5d13c7459938955b56a16e.tar.bz2 samba-6f33f3e4c26564602d5d13c7459938955b56a16e.zip |
s4-nbt: move libcli/nbt up one level.
Guenther
Diffstat (limited to 'libcli/nbt')
-rw-r--r-- | libcli/nbt/libnbt.h | 351 | ||||
-rw-r--r-- | libcli/nbt/namequery.c | 235 | ||||
-rw-r--r-- | libcli/nbt/namerefresh.c | 302 | ||||
-rw-r--r-- | libcli/nbt/nameregister.c | 442 | ||||
-rw-r--r-- | libcli/nbt/namerelease.c | 135 | ||||
-rw-r--r-- | libcli/nbt/nbtname.c | 648 | ||||
-rw-r--r-- | libcli/nbt/nbtsocket.c | 521 | ||||
-rw-r--r-- | libcli/nbt/pynbt.c | 408 |
8 files changed, 3042 insertions, 0 deletions
diff --git a/libcli/nbt/libnbt.h b/libcli/nbt/libnbt.h new file mode 100644 index 0000000000..4ef4e9d60d --- /dev/null +++ b/libcli/nbt/libnbt.h @@ -0,0 +1,351 @@ +/* + 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 timed_event *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 event_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 fd_event *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 event_context *event_ctx, + struct smb_iconv_convenience *iconv_convenience); +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_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/namequery.c b/libcli/nbt/namequery.c new file mode 100644 index 0000000000..ed3d8a2492 --- /dev/null +++ b/libcli/nbt/namequery.c @@ -0,0 +1,235 @@ +/* + 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" +#include "param/param.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..37bf0fc8f1 --- /dev/null +++ b/libcli/nbt/namerefresh.c @@ -0,0 +1,302 @@ +/* + 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" +#include "param/param.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 = str_list_copy(state, io->in.wins_servers); + if (state->wins_servers == NULL || + state->wins_servers[0] == NULL) goto failed; + + state->addresses = 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..d4728a8e02 --- /dev/null +++ b/libcli/nbt/nameregister.c @@ -0,0 +1,442 @@ +/* + 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" +#include "param/param.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 = str_list_copy(state, io->in.wins_servers); + if (state->wins_servers == NULL || + state->wins_servers[0] == NULL) goto failed; + + state->addresses = 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..e57a2f396e --- /dev/null +++ b/libcli/nbt/namerelease.c @@ -0,0 +1,135 @@ +/* + 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" +#include "param/param.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/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..57531d2e7b --- /dev/null +++ b/libcli/nbt/nbtsocket.c @@ -0,0 +1,521 @@ +/* + 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 "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 event_context *ev, struct timed_event *te, + struct timeval t, void *private) +{ + struct nbt_name_request *req = talloc_get_type(private, + 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; + } + + /* 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) { + 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; + /* although there can be a timeout in the packet, w2k3 screws it up, + so better to set it ourselves */ + req->timeout = lp_parm_int(global_loadparm, NULL, "nbt", "wack_timeout", 30); + req->te = event_add_timed(req->nbtsock->event_ctx, req, + timeval_current_ofs(req->timeout, 0), + nbt_name_socket_timeout, req); + talloc_free(tmp_ctx); + 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) { + talloc_free(tmp_ctx); + return; + } + + nbt_name_request_destructor(req); + req->state = NBT_REQUEST_DONE; + req->status = NT_STATUS_OK; + +done: + talloc_free(tmp_ctx); + if (req->async.fn) { + req->async.fn(req); + } +} + +/* + handle fd events on a nbt_name_socket +*/ +static void nbt_name_socket_handler(struct event_context *ev, struct fd_event *fde, + uint16_t flags, void *private) +{ + struct nbt_name_socket *nbtsock = talloc_get_type(private, + 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 event_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) +{ + nbtsock->incoming.handler = handler; + nbtsock->incoming.private_data = private; + 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..e49c1776a3 --- /dev/null +++ b/libcli/nbt/pynbt.c @@ -0,0 +1,408 @@ +/* + 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 "../libcli/nbt/libnbt.h" +#include "lib/events/events.h" +#include "param/param.h" + +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 event_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, lp_iconv_convenience(global_loadparm)); + 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 *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 *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, lp_iconv_convenience(global_loadparm), &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, lp_iconv_convenience(global_loadparm), &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, lp_iconv_convenience(global_loadparm), &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, lp_iconv_convenience(global_loadparm), &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) +{ + return Py_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); +} |