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);
330 if (wrepl_socket->fde == NULL) {
334 set_blocking(socket_get_fd(wrepl_socket->sock), False);
336 talloc_set_destructor(wrepl_socket, wrepl_socket_destructor);
341 talloc_free(wrepl_socket);
346 initialise a wrepl_socket from an already existing connection
348 struct wrepl_socket *wrepl_socket_merge(TALLOC_CTX *mem_ctx,
349 struct event_context *event_ctx,
350 struct socket_context *socket)
352 struct wrepl_socket *wrepl_socket;
354 wrepl_socket = talloc(mem_ctx, struct wrepl_socket);
355 if (wrepl_socket == NULL) goto failed;
357 wrepl_socket->event_ctx = talloc_reference(wrepl_socket, event_ctx);
358 if (wrepl_socket->event_ctx == NULL) goto failed;
360 wrepl_socket->sock = socket;
361 talloc_steal(wrepl_socket, wrepl_socket->sock);
363 wrepl_socket->send_queue = NULL;
364 wrepl_socket->recv_queue = NULL;
365 wrepl_socket->request_timeout = WREPL_SOCKET_REQUEST_TIMEOUT;
366 wrepl_socket->dead = False;
368 wrepl_socket->fde = event_add_fd(wrepl_socket->event_ctx, wrepl_socket,
369 socket_get_fd(wrepl_socket->sock),
371 wrepl_handler, wrepl_socket);
372 if (wrepl_socket->fde == NULL) {
376 talloc_set_destructor(wrepl_socket, wrepl_socket_destructor);
381 talloc_free(wrepl_socket);
386 destroy a wrepl_request
388 static int wrepl_request_destructor(void *ptr)
390 struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request);
391 if (req->state == WREPL_REQUEST_SEND) {
392 DLIST_REMOVE(req->wrepl_socket->send_queue, req);
394 if (req->state == WREPL_REQUEST_RECV) {
395 DLIST_REMOVE(req->wrepl_socket->recv_queue, req);
397 req->state = WREPL_REQUEST_ERROR;
402 wait for a request to complete
404 static NTSTATUS wrepl_request_wait(struct wrepl_request *req)
406 NT_STATUS_HAVE_NO_MEMORY(req);
407 while (req->state < WREPL_REQUEST_DONE) {
408 event_loop_once(req->wrepl_socket->event_ctx);
415 connect a wrepl_socket to a WINS server
417 struct wrepl_request *wrepl_connect_send(struct wrepl_socket *wrepl_socket,
420 struct wrepl_request *req;
423 req = talloc_zero(wrepl_socket, struct wrepl_request);
424 if (req == NULL) goto failed;
426 req->wrepl_socket = wrepl_socket;
427 req->state = WREPL_REQUEST_RECV;
429 DLIST_ADD(wrepl_socket->recv_queue, req);
431 talloc_set_destructor(req, wrepl_request_destructor);
433 status = socket_connect(wrepl_socket->sock, iface_best_ip(address), 0, address,
434 WINS_REPLICATION_PORT, 0);
435 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) goto failed;
445 connect a wrepl_socket to a WINS server - recv side
447 NTSTATUS wrepl_connect_recv(struct wrepl_request *req)
449 return wrepl_request_wait(req);
454 connect a wrepl_socket to a WINS server - sync API
456 NTSTATUS wrepl_connect(struct wrepl_socket *wrepl_socket, const char *address)
458 struct wrepl_request *req = wrepl_connect_send(wrepl_socket, address);
459 return wrepl_connect_recv(req);
463 callback from wrepl_request_trigger()
465 static void wrepl_request_trigger_handler(struct event_context *ev, struct timed_event *te,
466 struct timeval t, void *ptr)
468 struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request);
475 trigger an immediate event on a wrepl_request
477 static void wrepl_request_trigger(struct wrepl_request *req)
479 /* a zero timeout means immediate */
480 event_add_timed(req->wrepl_socket->event_ctx,
482 wrepl_request_trigger_handler, req);
487 send a generic wins replication request
489 struct wrepl_request *wrepl_request_send(struct wrepl_socket *wrepl_socket,
490 struct wrepl_packet *packet)
492 struct wrepl_request *req;
493 struct wrepl_wrap wrap;
495 req = talloc_zero(wrepl_socket, struct wrepl_request);
496 if (req == NULL) goto failed;
498 if (wrepl_socket->dead) {
499 req->wrepl_socket = wrepl_socket;
500 req->state = WREPL_REQUEST_ERROR;
501 req->status = NT_STATUS_INVALID_CONNECTION;
502 wrepl_request_trigger(req);
506 req->wrepl_socket = wrepl_socket;
507 req->state = WREPL_REQUEST_SEND;
509 wrap.packet = *packet;
510 req->status = ndr_push_struct_blob(&req->buffer, req, &wrap,
511 (ndr_push_flags_fn_t)ndr_push_wrepl_wrap);
512 if (!NT_STATUS_IS_OK(req->status)) goto failed;
515 DEBUG(10,("Sending WINS packet of length %d\n", (int)req->buffer.length));
516 NDR_PRINT_DEBUG(wrepl_packet, &wrap.packet);
519 DLIST_ADD(wrepl_socket->send_queue, req);
521 talloc_set_destructor(req, wrepl_request_destructor);
523 if (wrepl_socket->request_timeout > 0) {
524 req->te = event_add_timed(wrepl_socket->event_ctx, req,
525 timeval_current_ofs(wrepl_socket->request_timeout, 0),
526 wrepl_request_timeout_handler, req);
529 EVENT_FD_WRITEABLE(wrepl_socket->fde);
539 receive a generic WINS replication reply
541 NTSTATUS wrepl_request_recv(struct wrepl_request *req,
543 struct wrepl_packet **packet)
545 NTSTATUS status = wrepl_request_wait(req);
546 if (NT_STATUS_IS_OK(status)) {
547 *packet = talloc_steal(mem_ctx, req->packet);
554 a full WINS replication request/response
556 NTSTATUS wrepl_request(struct wrepl_socket *wrepl_socket,
558 struct wrepl_packet *req_packet,
559 struct wrepl_packet **reply_packet)
561 struct wrepl_request *req = wrepl_request_send(wrepl_socket, req_packet);
562 return wrepl_request_recv(req, mem_ctx, reply_packet);
567 setup an association - send
569 struct wrepl_request *wrepl_associate_send(struct wrepl_socket *wrepl_socket,
570 struct wrepl_associate *io)
572 struct wrepl_packet *packet;
573 struct wrepl_request *req;
575 packet = talloc_zero(wrepl_socket, struct wrepl_packet);
576 if (packet == NULL) return NULL;
578 packet->opcode = WREPL_OPCODE_BITS;
579 packet->mess_type = WREPL_START_ASSOCIATION;
580 packet->message.start.minor_version = 2;
581 packet->message.start.major_version = 5;
583 req = wrepl_request_send(wrepl_socket, packet);
591 setup an association - recv
593 NTSTATUS wrepl_associate_recv(struct wrepl_request *req,
594 struct wrepl_associate *io)
596 struct wrepl_packet *packet=NULL;
598 status = wrepl_request_recv(req, req->wrepl_socket, &packet);
599 NT_STATUS_NOT_OK_RETURN(status);
600 if (packet->mess_type != WREPL_START_ASSOCIATION_REPLY) {
601 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
603 if (NT_STATUS_IS_OK(status)) {
604 io->out.assoc_ctx = packet->message.start_reply.assoc_ctx;
611 setup an association - sync api
613 NTSTATUS wrepl_associate(struct wrepl_socket *wrepl_socket,
614 struct wrepl_associate *io)
616 struct wrepl_request *req = wrepl_associate_send(wrepl_socket, io);
617 return wrepl_associate_recv(req, io);
622 fetch the partner tables - send
624 struct wrepl_request *wrepl_pull_table_send(struct wrepl_socket *wrepl_socket,
625 struct wrepl_pull_table *io)
627 struct wrepl_packet *packet;
628 struct wrepl_request *req;
630 packet = talloc_zero(wrepl_socket, struct wrepl_packet);
631 if (packet == NULL) return NULL;
633 packet->opcode = WREPL_OPCODE_BITS;
634 packet->assoc_ctx = io->in.assoc_ctx;
635 packet->mess_type = WREPL_REPLICATION;
636 packet->message.replication.command = WREPL_REPL_TABLE_QUERY;
638 req = wrepl_request_send(wrepl_socket, packet);
647 fetch the partner tables - recv
649 NTSTATUS wrepl_pull_table_recv(struct wrepl_request *req,
651 struct wrepl_pull_table *io)
653 struct wrepl_packet *packet=NULL;
655 struct wrepl_table *table;
658 status = wrepl_request_recv(req, req->wrepl_socket, &packet);
659 NT_STATUS_NOT_OK_RETURN(status);
660 if (packet->mess_type != WREPL_REPLICATION) {
661 status = NT_STATUS_NETWORK_ACCESS_DENIED;
662 } else if (packet->message.replication.command != WREPL_REPL_TABLE_REPLY) {
663 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
665 if (!NT_STATUS_IS_OK(status)) goto failed;
667 table = &packet->message.replication.info.table;
668 io->out.num_partners = table->partner_count;
669 io->out.partners = talloc_steal(mem_ctx, table->partners);
670 for (i=0;i<io->out.num_partners;i++) {
671 talloc_steal(io->out.partners, io->out.partners[i].address);
681 fetch the partner table - sync api
683 NTSTATUS wrepl_pull_table(struct wrepl_socket *wrepl_socket,
685 struct wrepl_pull_table *io)
687 struct wrepl_request *req = wrepl_pull_table_send(wrepl_socket, io);
688 return wrepl_pull_table_recv(req, mem_ctx, io);
693 fetch the names for a WINS partner - send
695 struct wrepl_request *wrepl_pull_names_send(struct wrepl_socket *wrepl_socket,
696 struct wrepl_pull_names *io)
698 struct wrepl_packet *packet;
699 struct wrepl_request *req;
701 packet = talloc_zero(wrepl_socket, struct wrepl_packet);
702 if (packet == NULL) return NULL;
704 packet->opcode = WREPL_OPCODE_BITS;
705 packet->assoc_ctx = io->in.assoc_ctx;
706 packet->mess_type = WREPL_REPLICATION;
707 packet->message.replication.command = WREPL_REPL_SEND_REQUEST;
708 packet->message.replication.info.owner = io->in.partner;
710 req = wrepl_request_send(wrepl_socket, packet);
718 fetch the names for a WINS partner - recv
720 NTSTATUS wrepl_pull_names_recv(struct wrepl_request *req,
722 struct wrepl_pull_names *io)
724 struct wrepl_packet *packet=NULL;
728 status = wrepl_request_recv(req, req->wrepl_socket, &packet);
729 NT_STATUS_NOT_OK_RETURN(status);
730 if (packet->mess_type != WREPL_REPLICATION ||
731 packet->message.replication.command != WREPL_REPL_SEND_REPLY) {
732 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
734 if (!NT_STATUS_IS_OK(status)) goto failed;
736 io->out.num_names = packet->message.replication.info.reply.num_names;
738 status = NT_STATUS_NO_MEMORY;
740 io->out.names = talloc_array(packet, struct wrepl_name, io->out.num_names);
741 if (io->out.names == NULL) goto nomem;
743 /* convert the list of names and addresses to a sane format */
744 for (i=0;i<io->out.num_names;i++) {
745 struct wrepl_wins_name *wname = &packet->message.replication.info.reply.names[i];
746 struct wrepl_name *name = &io->out.names[i];
748 name->name = *wname->name;
749 talloc_steal(io->out.names, wname->name);
750 name->type = WREPL_NAME_TYPE(wname->flags);
751 name->state = WREPL_NAME_STATE(wname->flags);
752 name->node = WREPL_NAME_NODE(wname->flags);
753 name->is_static = WREPL_NAME_IS_STATIC(wname->flags);
754 name->raw_flags = wname->flags;
755 name->version_id= wname->id;
756 name->owner = talloc_strdup(io->out.names, io->in.partner.address);
757 if (name->owner == NULL) goto nomem;
759 /* trying to save 1 or 2 bytes on the wire isn't a good idea */
760 if (wname->flags & 2) {
763 name->num_addresses = wname->addresses.addresses.num_ips;
764 name->addresses = talloc_array(io->out.names,
765 struct wrepl_address,
766 name->num_addresses);
767 if (name->addresses == NULL) goto nomem;
768 for (j=0;j<name->num_addresses;j++) {
769 name->addresses[j].owner =
770 talloc_steal(name->addresses,
771 wname->addresses.addresses.ips[j].owner);
772 name->addresses[j].address =
773 talloc_steal(name->addresses,
774 wname->addresses.addresses.ips[j].ip);
777 name->num_addresses = 1;
778 name->addresses = talloc(io->out.names, struct wrepl_address);
779 if (name->addresses == NULL) goto nomem;
780 name->addresses[0].owner = talloc_strdup(name->addresses,io->in.partner.address);
781 if (name->addresses[0].owner == NULL) goto nomem;
782 name->addresses[0].address = talloc_steal(name->addresses,
783 wname->addresses.ip);
787 talloc_steal(mem_ctx, io->out.names);
791 status = NT_STATUS_NO_MEMORY;
800 fetch the names for a WINS partner - sync api
802 NTSTATUS wrepl_pull_names(struct wrepl_socket *wrepl_socket,
804 struct wrepl_pull_names *io)
806 struct wrepl_request *req = wrepl_pull_names_send(wrepl_socket, io);
807 return wrepl_pull_names_recv(req, mem_ctx, io);