2 Unix SMB/CIFS implementation.
4 low level WINS replication client code
6 Copyright (C) Andrew Tridgell 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "lib/events/events.h"
25 #include "dlinklist.h"
26 #include "lib/socket/socket.h"
27 #include "libcli/wrepl/winsrepl.h"
30 mark all pending requests as dead - called when a socket error happens
32 static void wrepl_socket_dead(struct wrepl_socket *wrepl_socket, NTSTATUS status)
34 wrepl_socket->dead = True;
36 if (wrepl_socket->fde) {
37 talloc_free(wrepl_socket->fde);
38 wrepl_socket->fde = NULL;
41 if (wrepl_socket->sock) {
42 talloc_free(wrepl_socket->sock);
43 wrepl_socket->sock = NULL;
46 if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
47 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
49 while (wrepl_socket->send_queue) {
50 struct wrepl_request *req = wrepl_socket->send_queue;
51 DLIST_REMOVE(wrepl_socket->send_queue, req);
52 req->state = WREPL_REQUEST_ERROR;
58 while (wrepl_socket->recv_queue) {
59 struct wrepl_request *req = wrepl_socket->recv_queue;
60 DLIST_REMOVE(wrepl_socket->recv_queue, req);
61 req->state = WREPL_REQUEST_ERROR;
69 static void wrepl_request_timeout_handler(struct event_context *ev, struct timed_event *te,
70 struct timeval t, void *ptr)
72 struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request);
73 wrepl_socket_dead(req->wrepl_socket, NT_STATUS_IO_TIMEOUT);
79 static void wrepl_handler_send(struct wrepl_socket *wrepl_socket)
81 while (wrepl_socket->send_queue) {
82 struct wrepl_request *req = wrepl_socket->send_queue;
86 status = socket_send(wrepl_socket->sock, &req->buffer, &nsent, 0);
87 if (NT_STATUS_IS_ERR(status)) {
88 wrepl_socket_dead(wrepl_socket, status);
91 if (!NT_STATUS_IS_OK(status) || nsent == 0) return;
93 req->buffer.data += nsent;
94 req->buffer.length -= nsent;
95 if (req->buffer.length != 0) {
99 DLIST_REMOVE(wrepl_socket->send_queue, req);
100 DLIST_ADD_END(wrepl_socket->recv_queue, req, struct wrepl_request *);
101 req->state = WREPL_REQUEST_RECV;
103 EVENT_FD_READABLE(wrepl_socket->fde);
106 EVENT_FD_NOT_WRITEABLE(wrepl_socket->fde);
113 static void wrepl_handler_recv(struct wrepl_socket *wrepl_socket)
116 struct wrepl_request *req = wrepl_socket->recv_queue;
122 EVENT_FD_NOT_READABLE(wrepl_socket->fde);
124 status = socket_recv(wrepl_socket->sock, NULL, 0, &nread, 0);
125 if (NT_STATUS_EQUAL(NT_STATUS_END_OF_FILE,status)) return;
126 if (NT_STATUS_IS_ERR(status)) {
127 wrepl_socket_dead(wrepl_socket, status);
133 if (req->buffer.length == 0) {
134 req->buffer = data_blob_talloc(req, NULL, 4);
135 if (req->buffer.data == NULL) {
136 req->status = NT_STATUS_NO_MEMORY;
142 /* read in the packet length */
143 if (req->num_read < 4) {
146 req->status = socket_recv(wrepl_socket->sock,
147 req->buffer.data + req->num_read,
150 if (NT_STATUS_IS_ERR(req->status)) {
151 wrepl_socket_dead(wrepl_socket, req->status);
154 if (!NT_STATUS_IS_OK(req->status)) return;
156 req->num_read += nread;
157 if (req->num_read != 4) return;
159 req_length = RIVAL(req->buffer.data, 0) + 4;
161 req->buffer.data = talloc_realloc(req, req->buffer.data,
162 uint8_t, req_length);
163 if (req->buffer.data == NULL) {
164 req->status = NT_STATUS_NO_MEMORY;
167 req->buffer.length = req_length;
170 /* read in the body */
171 req->status = socket_recv(wrepl_socket->sock,
172 req->buffer.data + req->num_read,
173 req->buffer.length - req->num_read,
175 if (NT_STATUS_IS_ERR(req->status)) {
176 wrepl_socket_dead(wrepl_socket, req->status);
179 if (!NT_STATUS_IS_OK(req->status)) return;
181 req->num_read += nread;
182 if (req->num_read != req->buffer.length) return;
184 req->packet = talloc(req, struct wrepl_packet);
185 if (req->packet == NULL) {
186 req->status = NT_STATUS_NO_MEMORY;
190 blob.data = req->buffer.data + 4;
191 blob.length = req->buffer.length - 4;
193 /* we have a full request - parse it */
194 req->status = ndr_pull_struct_blob(&blob,
195 req->packet, req->packet,
196 (ndr_pull_flags_fn_t)ndr_pull_wrepl_packet);
197 if (!NT_STATUS_IS_OK(req->status)) {
198 DEBUG(2,("Failed to parse incoming WINS packet - %s\n",
199 nt_errstr(req->status)));
200 DEBUG(10,("packet length %d\n", (int)req->buffer.length));
201 NDR_PRINT_DEBUG(wrepl_packet, req->packet);
206 DEBUG(10,("Received WINS packet of length %d\n", (int)req->buffer.length));
207 NDR_PRINT_DEBUG(wrepl_packet, req->packet);
210 DLIST_REMOVE(wrepl_socket->recv_queue, req);
211 req->state = WREPL_REQUEST_DONE;
218 if (req->state == WREPL_REQUEST_RECV) {
219 DLIST_REMOVE(wrepl_socket->recv_queue, req);
221 req->state = WREPL_REQUEST_ERROR;
229 handler for winrepl events
231 static void wrepl_handler(struct event_context *ev, struct fd_event *fde,
232 uint16_t flags, void *private)
234 struct wrepl_socket *wrepl_socket = talloc_get_type(private,
235 struct wrepl_socket);
236 if (flags & EVENT_FD_WRITE) {
237 wrepl_handler_send(wrepl_socket);
239 if (flags & EVENT_FD_READ) {
240 wrepl_handler_recv(wrepl_socket);
246 handler for winrepl connection completion
248 static void wrepl_connect_handler(struct event_context *ev, struct fd_event *fde,
249 uint16_t flags, void *private)
251 struct wrepl_socket *wrepl_socket = talloc_get_type(private,
252 struct wrepl_socket);
253 struct wrepl_request *req = wrepl_socket->recv_queue;
255 talloc_free(wrepl_socket->fde);
256 wrepl_socket->fde = NULL;
258 if (req == NULL) return;
260 req->status = socket_connect_complete(wrepl_socket->sock, 0);
261 if (NT_STATUS_IS_ERR(req->status)) goto failed;
263 if (!NT_STATUS_IS_OK(req->status)) return;
265 wrepl_socket->fde = event_add_fd(wrepl_socket->event_ctx, wrepl_socket,
266 socket_get_fd(wrepl_socket->sock),
268 wrepl_handler, wrepl_socket);
269 if (wrepl_socket->fde == NULL) {
270 req->status = NT_STATUS_NO_MEMORY;
275 DLIST_REMOVE(wrepl_socket->recv_queue, req);
276 if (!NT_STATUS_IS_OK(req->status)) {
277 req->state = WREPL_REQUEST_ERROR;
279 req->state = WREPL_REQUEST_DONE;
287 destroy a wrepl_socket destructor
289 static int wrepl_socket_destructor(void *ptr)
291 struct wrepl_socket *sock = talloc_get_type(ptr, struct wrepl_socket);
292 wrepl_socket_dead(sock, NT_STATUS_CONNECTION_DISCONNECTED);
297 initialise a wrepl_socket. The event_ctx is optional, if provided then
298 operations will use that event context
300 struct wrepl_socket *wrepl_socket_init(TALLOC_CTX *mem_ctx,
301 struct event_context *event_ctx)
303 struct wrepl_socket *wrepl_socket;
306 wrepl_socket = talloc(mem_ctx, struct wrepl_socket);
307 if (wrepl_socket == NULL) goto failed;
309 if (event_ctx == NULL) {
310 wrepl_socket->event_ctx = event_context_init(wrepl_socket);
312 wrepl_socket->event_ctx = talloc_reference(wrepl_socket, event_ctx);
314 if (wrepl_socket->event_ctx == NULL) goto failed;
316 status = socket_create("ip", SOCKET_TYPE_STREAM, &wrepl_socket->sock, 0);
317 if (!NT_STATUS_IS_OK(status)) goto failed;
319 talloc_steal(wrepl_socket, wrepl_socket->sock);
321 wrepl_socket->send_queue = NULL;
322 wrepl_socket->recv_queue = NULL;
323 wrepl_socket->request_timeout = WREPL_SOCKET_REQUEST_TIMEOUT;
324 wrepl_socket->dead = False;
326 wrepl_socket->fde = event_add_fd(wrepl_socket->event_ctx, wrepl_socket,
327 socket_get_fd(wrepl_socket->sock),
329 wrepl_connect_handler, wrepl_socket);
331 set_blocking(socket_get_fd(wrepl_socket->sock), False);
333 talloc_set_destructor(wrepl_socket, wrepl_socket_destructor);
338 talloc_free(wrepl_socket);
343 destroy a wrepl_request
345 static int wrepl_request_destructor(void *ptr)
347 struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request);
348 if (req->state == WREPL_REQUEST_SEND) {
349 DLIST_REMOVE(req->wrepl_socket->send_queue, req);
351 if (req->state == WREPL_REQUEST_RECV) {
352 DLIST_REMOVE(req->wrepl_socket->recv_queue, req);
354 req->state = WREPL_REQUEST_ERROR;
359 wait for a request to complete
361 static NTSTATUS wrepl_request_wait(struct wrepl_request *req)
363 NT_STATUS_HAVE_NO_MEMORY(req);
364 while (req->state < WREPL_REQUEST_DONE) {
365 event_loop_once(req->wrepl_socket->event_ctx);
372 connect a wrepl_socket to a WINS server
374 struct wrepl_request *wrepl_connect_send(struct wrepl_socket *wrepl_socket,
377 struct wrepl_request *req;
380 req = talloc_zero(wrepl_socket, struct wrepl_request);
381 if (req == NULL) goto failed;
383 req->wrepl_socket = wrepl_socket;
384 req->state = WREPL_REQUEST_RECV;
386 DLIST_ADD(wrepl_socket->recv_queue, req);
388 talloc_set_destructor(req, wrepl_request_destructor);
390 status = socket_connect(wrepl_socket->sock, iface_best_ip(address), 0, address,
391 WINS_REPLICATION_PORT, 0);
392 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) goto failed;
402 connect a wrepl_socket to a WINS server - recv side
404 NTSTATUS wrepl_connect_recv(struct wrepl_request *req)
406 return wrepl_request_wait(req);
411 connect a wrepl_socket to a WINS server - sync API
413 NTSTATUS wrepl_connect(struct wrepl_socket *wrepl_socket, const char *address)
415 struct wrepl_request *req = wrepl_connect_send(wrepl_socket, address);
416 return wrepl_connect_recv(req);
420 callback from wrepl_request_trigger()
422 static void wrepl_request_trigger_handler(struct event_context *ev, struct timed_event *te,
423 struct timeval t, void *ptr)
425 struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request);
432 trigger an immediate event on a wrepl_request
434 static void wrepl_request_trigger(struct wrepl_request *req)
436 /* a zero timeout means immediate */
437 event_add_timed(req->wrepl_socket->event_ctx,
439 wrepl_request_trigger_handler, req);
444 send a generic wins replication request
446 struct wrepl_request *wrepl_request_send(struct wrepl_socket *wrepl_socket,
447 struct wrepl_packet *packet)
449 struct wrepl_request *req;
450 struct wrepl_wrap wrap;
452 req = talloc_zero(wrepl_socket, struct wrepl_request);
453 if (req == NULL) goto failed;
455 if (wrepl_socket->dead) {
456 req->wrepl_socket = wrepl_socket;
457 req->state = WREPL_REQUEST_ERROR;
458 req->status = NT_STATUS_INVALID_CONNECTION;
459 wrepl_request_trigger(req);
463 req->wrepl_socket = wrepl_socket;
464 req->state = WREPL_REQUEST_SEND;
466 wrap.packet = *packet;
467 req->status = ndr_push_struct_blob(&req->buffer, req, &wrap,
468 (ndr_push_flags_fn_t)ndr_push_wrepl_wrap);
469 if (!NT_STATUS_IS_OK(req->status)) goto failed;
472 DEBUG(10,("Sending WINS packet of length %d\n", (int)req->buffer.length));
473 NDR_PRINT_DEBUG(wrepl_packet, &wrap.packet);
476 DLIST_ADD(wrepl_socket->send_queue, req);
478 talloc_set_destructor(req, wrepl_request_destructor);
480 if (wrepl_socket->request_timeout > 0) {
481 req->te = event_add_timed(wrepl_socket->event_ctx, req,
482 timeval_current_ofs(wrepl_socket->request_timeout, 0),
483 wrepl_request_timeout_handler, req);
486 EVENT_FD_WRITEABLE(wrepl_socket->fde);
496 receive a generic WINS replication reply
498 NTSTATUS wrepl_request_recv(struct wrepl_request *req,
500 struct wrepl_packet **packet)
502 NTSTATUS status = wrepl_request_wait(req);
503 if (NT_STATUS_IS_OK(status)) {
504 *packet = talloc_steal(mem_ctx, req->packet);
511 a full WINS replication request/response
513 NTSTATUS wrepl_request(struct wrepl_socket *wrepl_socket,
515 struct wrepl_packet *req_packet,
516 struct wrepl_packet **reply_packet)
518 struct wrepl_request *req = wrepl_request_send(wrepl_socket, req_packet);
519 return wrepl_request_recv(req, mem_ctx, reply_packet);
524 setup an association - send
526 struct wrepl_request *wrepl_associate_send(struct wrepl_socket *wrepl_socket,
527 struct wrepl_associate *io)
529 struct wrepl_packet *packet;
530 struct wrepl_request *req;
532 packet = talloc_zero(wrepl_socket, struct wrepl_packet);
533 if (packet == NULL) return NULL;
535 packet->opcode = WREPL_OPCODE_BITS;
536 packet->mess_type = WREPL_START_ASSOCIATION;
537 packet->message.start.minor_version = 2;
538 packet->message.start.major_version = 5;
540 req = wrepl_request_send(wrepl_socket, packet);
548 setup an association - recv
550 NTSTATUS wrepl_associate_recv(struct wrepl_request *req,
551 struct wrepl_associate *io)
553 struct wrepl_packet *packet=NULL;
555 status = wrepl_request_recv(req, req->wrepl_socket, &packet);
556 NT_STATUS_NOT_OK_RETURN(status);
557 if (packet->mess_type != WREPL_START_ASSOCIATION_REPLY) {
558 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
560 if (NT_STATUS_IS_OK(status)) {
561 io->out.assoc_ctx = packet->message.start_reply.assoc_ctx;
568 setup an association - sync api
570 NTSTATUS wrepl_associate(struct wrepl_socket *wrepl_socket,
571 struct wrepl_associate *io)
573 struct wrepl_request *req = wrepl_associate_send(wrepl_socket, io);
574 return wrepl_associate_recv(req, io);
579 fetch the partner tables - send
581 struct wrepl_request *wrepl_pull_table_send(struct wrepl_socket *wrepl_socket,
582 struct wrepl_pull_table *io)
584 struct wrepl_packet *packet;
585 struct wrepl_request *req;
587 packet = talloc_zero(wrepl_socket, struct wrepl_packet);
588 if (packet == NULL) return NULL;
590 packet->opcode = WREPL_OPCODE_BITS;
591 packet->assoc_ctx = io->in.assoc_ctx;
592 packet->mess_type = WREPL_REPLICATION;
593 packet->message.replication.command = WREPL_REPL_TABLE_QUERY;
595 req = wrepl_request_send(wrepl_socket, packet);
604 fetch the partner tables - recv
606 NTSTATUS wrepl_pull_table_recv(struct wrepl_request *req,
608 struct wrepl_pull_table *io)
610 struct wrepl_packet *packet=NULL;
612 struct wrepl_table *table;
615 status = wrepl_request_recv(req, req->wrepl_socket, &packet);
616 NT_STATUS_NOT_OK_RETURN(status);
617 if (packet->mess_type != WREPL_REPLICATION) {
618 status = NT_STATUS_NETWORK_ACCESS_DENIED;
619 } else if (packet->message.replication.command != WREPL_REPL_TABLE_REPLY) {
620 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
622 if (!NT_STATUS_IS_OK(status)) goto failed;
624 table = &packet->message.replication.info.table;
625 io->out.num_partners = table->partner_count;
626 io->out.partners = talloc_steal(mem_ctx, table->partners);
627 for (i=0;i<io->out.num_partners;i++) {
628 talloc_steal(io->out.partners, io->out.partners[i].address);
638 fetch the partner table - sync api
640 NTSTATUS wrepl_pull_table(struct wrepl_socket *wrepl_socket,
642 struct wrepl_pull_table *io)
644 struct wrepl_request *req = wrepl_pull_table_send(wrepl_socket, io);
645 return wrepl_pull_table_recv(req, mem_ctx, io);
650 fetch the names for a WINS partner - send
652 struct wrepl_request *wrepl_pull_names_send(struct wrepl_socket *wrepl_socket,
653 struct wrepl_pull_names *io)
655 struct wrepl_packet *packet;
656 struct wrepl_request *req;
658 packet = talloc_zero(wrepl_socket, struct wrepl_packet);
659 if (packet == NULL) return NULL;
661 packet->opcode = WREPL_OPCODE_BITS;
662 packet->assoc_ctx = io->in.assoc_ctx;
663 packet->mess_type = WREPL_REPLICATION;
664 packet->message.replication.command = WREPL_REPL_SEND_REQUEST;
665 packet->message.replication.info.owner = io->in.partner;
667 req = wrepl_request_send(wrepl_socket, packet);
675 fetch the names for a WINS partner - recv
677 NTSTATUS wrepl_pull_names_recv(struct wrepl_request *req,
679 struct wrepl_pull_names *io)
681 struct wrepl_packet *packet=NULL;
685 status = wrepl_request_recv(req, req->wrepl_socket, &packet);
686 NT_STATUS_NOT_OK_RETURN(status);
687 if (packet->mess_type != WREPL_REPLICATION ||
688 packet->message.replication.command != WREPL_REPL_SEND_REPLY) {
689 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
691 if (!NT_STATUS_IS_OK(status)) goto failed;
693 io->out.num_names = packet->message.replication.info.reply.num_names;
695 status = NT_STATUS_NO_MEMORY;
697 io->out.names = talloc_array(packet, struct wrepl_name, io->out.num_names);
698 if (io->out.names == NULL) goto nomem;
700 /* convert the list of names and addresses to a sane format */
701 for (i=0;i<io->out.num_names;i++) {
702 struct wrepl_wins_name *wname = &packet->message.replication.info.reply.names[i];
703 struct wrepl_name *name = &io->out.names[i];
705 name->name = wname->name;
706 talloc_steal(io->out.names, wname->name.name);
707 talloc_steal(io->out.names, wname->name.scope);
708 name->type = WREPL_NAME_TYPE(wname->flags);
709 name->state = WREPL_NAME_STATE(wname->flags);
710 name->node = WREPL_NBT_NODE(wname->flags);
711 name->is_static = WREPL_NAME_IS_STATIC(wname->flags);
712 name->raw_flags = wname->flags;
713 name->version_id= wname->id;
714 name->owner = talloc_strdup(io->out.names, io->in.partner.address);
715 if (name->owner == NULL) goto nomem;
717 /* trying to save 1 or 2 bytes on the wire isn't a good idea */
718 if (wname->flags & 2) {
721 name->num_addresses = wname->addresses.addresses.num_ips;
722 name->addresses = talloc_array(io->out.names,
723 struct wrepl_address,
724 name->num_addresses);
725 if (name->addresses == NULL) goto nomem;
726 for (j=0;j<name->num_addresses;j++) {
727 name->addresses[j].owner =
728 talloc_steal(name->addresses,
729 wname->addresses.addresses.ips[j].owner);
730 name->addresses[j].address =
731 talloc_steal(name->addresses,
732 wname->addresses.addresses.ips[j].ip);
735 name->num_addresses = 1;
736 name->addresses = talloc(io->out.names, struct wrepl_address);
737 if (name->addresses == NULL) goto nomem;
738 name->addresses[0].owner = talloc_strdup(name->addresses,io->in.partner.address);
739 if (name->addresses[0].owner == NULL) goto nomem;
740 name->addresses[0].address = talloc_steal(name->addresses,
741 wname->addresses.ip);
745 talloc_steal(mem_ctx, io->out.names);
749 status = NT_STATUS_NO_MEMORY;
758 fetch the names for a WINS partner - sync api
760 NTSTATUS wrepl_pull_names(struct wrepl_socket *wrepl_socket,
762 struct wrepl_pull_names *io)
764 struct wrepl_request *req = wrepl_pull_names_send(wrepl_socket, io);
765 return wrepl_pull_names_recv(req, mem_ctx, io);