r10115: bind client connection to the best interface, to the partner
[bbaumbach/samba-autobuild/.git] / source4 / libcli / wrepl / winsrepl.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    low level WINS replication client code
5
6    Copyright (C) Andrew Tridgell 2005
7    
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.
12    
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.
17    
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.
21 */
22
23 #include "includes.h"
24 #include "lib/events/events.h"
25 #include "dlinklist.h"
26 #include "lib/socket/socket.h"
27 #include "libcli/wrepl/winsrepl.h"
28
29 /*
30   mark all pending requests as dead - called when a socket error happens
31 */
32 static void wrepl_socket_dead(struct wrepl_socket *wrepl_socket)
33 {
34         event_set_fd_flags(wrepl_socket->fde, 0);
35
36         while (wrepl_socket->send_queue) {
37                 struct wrepl_request *req = wrepl_socket->send_queue;
38                 DLIST_REMOVE(wrepl_socket->send_queue, req);
39                 req->state = WREPL_REQUEST_ERROR;
40                 req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
41                 if (req->async.fn) {
42                         req->async.fn(req);
43                 }
44         }
45         while (wrepl_socket->recv_queue) {
46                 struct wrepl_request *req = wrepl_socket->recv_queue;
47                 DLIST_REMOVE(wrepl_socket->recv_queue, req);
48                 req->state = WREPL_REQUEST_ERROR;
49                 req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
50                 if (req->async.fn) {
51                         req->async.fn(req);
52                 }
53         }
54 }
55
56 /*
57   handle send events 
58 */
59 static void wrepl_handler_send(struct wrepl_socket *wrepl_socket)
60 {
61         while (wrepl_socket->send_queue) {
62                 struct wrepl_request *req = wrepl_socket->send_queue;
63                 size_t nsent;
64                 NTSTATUS status;
65
66                 status = socket_send(wrepl_socket->sock, &req->buffer, &nsent, 0);
67                 if (NT_STATUS_IS_ERR(status)) {
68                         wrepl_socket_dead(wrepl_socket);
69                         return;
70                 }
71                 if (!NT_STATUS_IS_OK(status) || nsent == 0) return;
72
73                 req->buffer.data   += nsent;
74                 req->buffer.length -= nsent;
75                 if (req->buffer.length != 0) {
76                         return;
77                 }
78
79                 DLIST_REMOVE(wrepl_socket->send_queue, req);
80                 DLIST_ADD_END(wrepl_socket->recv_queue, req, struct wrepl_request *);
81                 req->state = WREPL_REQUEST_RECV;
82
83                 EVENT_FD_READABLE(wrepl_socket->fde);
84         }
85
86         EVENT_FD_NOT_WRITEABLE(wrepl_socket->fde);
87 }
88
89
90 /*
91   handle recv events 
92 */
93 static void wrepl_handler_recv(struct wrepl_socket *wrepl_socket)
94 {
95         size_t nread;
96         struct wrepl_request *req = wrepl_socket->recv_queue;
97         DATA_BLOB blob;
98
99         if (req == NULL) {
100                 EVENT_FD_NOT_READABLE(wrepl_socket->fde);
101                 return;
102         }
103
104         if (req->buffer.length == 0) {
105                 req->buffer = data_blob_talloc(req, NULL, 4);
106                 if (req->buffer.data == NULL) {
107                         req->status = NT_STATUS_NO_MEMORY;
108                         goto failed;
109                 }
110                 req->num_read = 0;
111         }
112
113         /* read in the packet length */
114         if (req->num_read < 4) {
115                 uint32_t req_length;
116
117                 req->status = socket_recv(wrepl_socket->sock, 
118                                           req->buffer.data + req->num_read,
119                                           4 - req->num_read,
120                                           &nread, 0);
121                 if (NT_STATUS_IS_ERR(req->status)) goto failed;
122                 if (!NT_STATUS_IS_OK(req->status)) return;
123
124                 req->num_read += nread;
125                 if (req->num_read != 4) return;
126
127                 req_length = RIVAL(req->buffer.data, 0) + 4;
128
129                 req->buffer.data = talloc_realloc(req, req->buffer.data, 
130                                                   uint8_t, req_length);
131                 if (req->buffer.data == NULL) {
132                         req->status = NT_STATUS_NO_MEMORY;
133                         goto failed;
134                 }
135                 req->buffer.length = req_length;
136         }
137
138         /* read in the body */
139         req->status = socket_recv(wrepl_socket->sock, 
140                                   req->buffer.data + req->num_read,
141                                   req->buffer.length - req->num_read,
142                                   &nread, 0);
143         if (NT_STATUS_IS_ERR(req->status)) goto failed;
144         if (!NT_STATUS_IS_OK(req->status)) return;
145
146         req->num_read += nread;
147         if (req->num_read != req->buffer.length) return;
148
149         req->packet = talloc(req, struct wrepl_packet);
150         if (req->packet == NULL) {
151                 req->status = NT_STATUS_NO_MEMORY;
152                 goto failed;
153         }
154
155         blob.data = req->buffer.data + 4;
156         blob.length = req->buffer.length - 4;
157         
158         /* we have a full request - parse it */
159         req->status = ndr_pull_struct_blob(&blob,
160                                            req->packet, req->packet,
161                                            (ndr_pull_flags_fn_t)ndr_pull_wrepl_packet);
162         if (!NT_STATUS_IS_OK(req->status)) {
163                 DEBUG(2,("Failed to parse incoming WINS packet - %s\n",
164                          nt_errstr(req->status)));
165                 DEBUG(10,("packet length %d\n", (int)req->buffer.length));
166                 NDR_PRINT_DEBUG(wrepl_packet, req->packet);
167                 goto failed;
168         }
169
170         if (DEBUGLVL(10)) {
171                 DEBUG(10,("Received WINS packet of length %d\n", (int)req->buffer.length));
172                 NDR_PRINT_DEBUG(wrepl_packet, req->packet);
173         }
174
175         DLIST_REMOVE(wrepl_socket->recv_queue, req);
176         req->state = WREPL_REQUEST_DONE;
177         if (req->async.fn) {
178                 req->async.fn(req);
179         }
180         return;
181
182 failed:
183         if (req->state == WREPL_REQUEST_RECV) {
184                 DLIST_REMOVE(wrepl_socket->recv_queue, req);
185         }
186         req->state = WREPL_REQUEST_ERROR;
187         if (req->async.fn) {
188                 req->async.fn(req);
189         }
190 }
191
192
193 /*
194   handler for winrepl events
195 */
196 static void wrepl_handler(struct event_context *ev, struct fd_event *fde, 
197                           uint16_t flags, void *private)
198 {
199         struct wrepl_socket *wrepl_socket = talloc_get_type(private, 
200                                                             struct wrepl_socket);
201         if (flags & EVENT_FD_WRITE) {
202                 wrepl_handler_send(wrepl_socket);
203         }
204         if (flags & EVENT_FD_READ) {
205                 wrepl_handler_recv(wrepl_socket);
206         }
207 }
208
209
210 /*
211   handler for winrepl connection completion
212 */
213 static void wrepl_connect_handler(struct event_context *ev, struct fd_event *fde, 
214                                   uint16_t flags, void *private)
215 {
216         struct wrepl_socket *wrepl_socket = talloc_get_type(private, 
217                                                             struct wrepl_socket);
218         struct wrepl_request *req = wrepl_socket->recv_queue;
219
220         talloc_free(fde);
221
222         if (req == NULL) return;
223
224         req->status = socket_connect_complete(wrepl_socket->sock, 0);
225         if (NT_STATUS_IS_ERR(req->status)) goto failed;
226
227         if (!NT_STATUS_IS_OK(req->status)) return;
228
229         wrepl_socket->fde = event_add_fd(wrepl_socket->event_ctx, wrepl_socket, 
230                                          socket_get_fd(wrepl_socket->sock), 
231                                          0,
232                                          wrepl_handler, wrepl_socket);
233         if (wrepl_socket->fde == NULL) {
234                 req->status = NT_STATUS_NO_MEMORY;
235         }
236
237
238 failed:
239         DLIST_REMOVE(wrepl_socket->recv_queue, req);
240         if (!NT_STATUS_IS_OK(req->status)) {
241                 req->state = WREPL_REQUEST_ERROR;
242         } else {
243                 req->state = WREPL_REQUEST_DONE;
244         }
245         if (req->async.fn) {
246                 req->async.fn(req);
247         }
248 }
249
250
251 /*
252   initialise a wrepl_socket. The event_ctx is optional, if provided then
253   operations will use that event context
254 */
255 struct wrepl_socket *wrepl_socket_init(TALLOC_CTX *mem_ctx, 
256                                        struct event_context *event_ctx)
257 {
258         struct wrepl_socket *wrepl_socket;
259         NTSTATUS status;
260
261         wrepl_socket = talloc(mem_ctx, struct wrepl_socket);
262         if (wrepl_socket == NULL) goto failed;
263
264         if (event_ctx == NULL) {
265                 wrepl_socket->event_ctx = event_context_init(wrepl_socket);
266         } else {
267                 wrepl_socket->event_ctx = talloc_reference(wrepl_socket, event_ctx);
268         }
269         if (wrepl_socket->event_ctx == NULL) goto failed;
270
271         status = socket_create("ip", SOCKET_TYPE_STREAM, &wrepl_socket->sock, 0);
272         if (!NT_STATUS_IS_OK(status)) goto failed;
273
274         talloc_steal(wrepl_socket, wrepl_socket->sock);
275
276         wrepl_socket->send_queue = NULL;
277         wrepl_socket->recv_queue = NULL;
278
279         wrepl_socket->fde = event_add_fd(wrepl_socket->event_ctx, wrepl_socket, 
280                                          socket_get_fd(wrepl_socket->sock), 
281                                          EVENT_FD_WRITE,
282                                          wrepl_connect_handler, wrepl_socket);
283
284         set_blocking(socket_get_fd(wrepl_socket->sock), False);
285         
286         return wrepl_socket;
287
288 failed:
289         talloc_free(wrepl_socket);
290         return NULL;
291 }
292
293
294 /*
295   destroy a wrepl_request
296 */
297 static int wrepl_request_destructor(void *ptr)
298 {
299         struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request);
300         if (req->state == WREPL_REQUEST_SEND) {
301                 DLIST_REMOVE(req->wrepl_socket->send_queue, req);
302         }
303         if (req->state == WREPL_REQUEST_RECV) {
304                 DLIST_REMOVE(req->wrepl_socket->recv_queue, req);
305         }
306         req->state = WREPL_REQUEST_ERROR;
307         return 0;
308 }
309
310 /*
311   wait for a request to complete
312 */
313 static NTSTATUS wrepl_request_wait(struct wrepl_request *req)
314 {
315         NT_STATUS_HAVE_NO_MEMORY(req);
316         while (req->state < WREPL_REQUEST_DONE) {
317                 event_loop_once(req->wrepl_socket->event_ctx);
318         }
319         return req->status;
320 }
321
322
323 /*
324   connect a wrepl_socket to a WINS server
325 */
326 struct wrepl_request *wrepl_connect_send(struct wrepl_socket *wrepl_socket,
327                                          const char *address)
328 {
329         struct wrepl_request *req;
330         NTSTATUS status;
331
332         req = talloc_zero(wrepl_socket, struct wrepl_request);
333         if (req == NULL) goto failed;
334
335         req->wrepl_socket = wrepl_socket;
336         req->state        = WREPL_REQUEST_RECV;
337
338         DLIST_ADD(wrepl_socket->recv_queue, req);
339
340         talloc_set_destructor(req, wrepl_request_destructor);
341         
342         status = socket_connect(wrepl_socket->sock, iface_best_ip(address), 0, address, 
343                                 WINS_REPLICATION_PORT, 0);
344         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) goto failed;
345
346         return req;
347
348 failed:
349         talloc_free(req);
350         return NULL;
351 }
352
353 /*
354   connect a wrepl_socket to a WINS server - recv side
355 */
356 NTSTATUS wrepl_connect_recv(struct wrepl_request *req)
357 {
358         return wrepl_request_wait(req);
359 }
360
361
362 /*
363   connect a wrepl_socket to a WINS server - sync API
364 */
365 NTSTATUS wrepl_connect(struct wrepl_socket *wrepl_socket, const char *address)
366 {
367         struct wrepl_request *req = wrepl_connect_send(wrepl_socket, address);
368         return wrepl_connect_recv(req);
369 }
370
371
372 /*
373   send a generic wins replication request
374 */
375 struct wrepl_request *wrepl_request_send(struct wrepl_socket *wrepl_socket,
376                                          struct wrepl_packet *packet)
377 {
378         struct wrepl_request *req;
379         struct wrepl_wrap wrap;
380
381         req = talloc_zero(wrepl_socket, struct wrepl_request);
382         if (req == NULL) goto failed;
383
384         req->wrepl_socket = wrepl_socket;
385         req->state        = WREPL_REQUEST_SEND;
386
387         wrap.packet = *packet;
388         req->status = ndr_push_struct_blob(&req->buffer, req, &wrap,
389                                            (ndr_push_flags_fn_t)ndr_push_wrepl_wrap);   
390         if (!NT_STATUS_IS_OK(req->status)) goto failed;
391
392         if (DEBUGLVL(10)) {
393                 DEBUG(10,("Sending WINS packet of length %d\n", (int)req->buffer.length));
394                 NDR_PRINT_DEBUG(wrepl_packet, &wrap.packet);
395         }
396
397         DLIST_ADD(wrepl_socket->send_queue, req);
398
399         talloc_set_destructor(req, wrepl_request_destructor);
400
401         EVENT_FD_WRITEABLE(wrepl_socket->fde);
402         
403         return req;
404
405 failed:
406         talloc_free(req);
407         return NULL;
408 }
409
410 /*
411   receive a generic WINS replication reply
412 */
413 NTSTATUS wrepl_request_recv(struct wrepl_request *req,
414                             TALLOC_CTX *mem_ctx,
415                             struct wrepl_packet **packet)
416 {
417         NTSTATUS status = wrepl_request_wait(req);
418         if (NT_STATUS_IS_OK(status)) {
419                 *packet = talloc_steal(mem_ctx, req->packet);
420         }
421         talloc_free(req);
422         return status;
423 }
424
425 /*
426   a full WINS replication request/response
427 */
428 NTSTATUS wrepl_request(struct wrepl_socket *wrepl_socket,
429                        TALLOC_CTX *mem_ctx,
430                        struct wrepl_packet *req_packet,
431                        struct wrepl_packet **reply_packet)
432 {
433         struct wrepl_request *req = wrepl_request_send(wrepl_socket, req_packet);
434         return wrepl_request_recv(req, mem_ctx, reply_packet);
435 }
436
437
438 /*
439   setup an association - send
440 */
441 struct wrepl_request *wrepl_associate_send(struct wrepl_socket *wrepl_socket,
442                                            struct wrepl_associate *io)
443 {
444         struct wrepl_packet *packet;
445         struct wrepl_request *req;
446
447         packet = talloc_zero(wrepl_socket, struct wrepl_packet);
448         if (packet == NULL) return NULL;
449
450         packet->opcode                      = WREPL_OPCODE_BITS;
451         packet->mess_type                   = WREPL_START_ASSOCIATION;
452         packet->message.start.minor_version = 2;
453         packet->message.start.major_version = 5;
454
455         req = wrepl_request_send(wrepl_socket, packet);
456
457         talloc_free(packet);
458
459         return req;     
460 }
461
462 /*
463   setup an association - recv
464 */
465 NTSTATUS wrepl_associate_recv(struct wrepl_request *req,
466                               struct wrepl_associate *io)
467 {
468         struct wrepl_packet *packet=NULL;
469         NTSTATUS status;
470         status = wrepl_request_recv(req, req->wrepl_socket, &packet);
471         if (packet->mess_type != WREPL_START_ASSOCIATION_REPLY) {
472                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
473         }
474         if (NT_STATUS_IS_OK(status)) {
475                 io->out.assoc_ctx = packet->message.start_reply.assoc_ctx;
476         }
477         talloc_free(packet);
478         return status;
479 }
480
481 /*
482   setup an association - sync api
483 */
484 NTSTATUS wrepl_associate(struct wrepl_socket *wrepl_socket,
485                          struct wrepl_associate *io)
486 {
487         struct wrepl_request *req = wrepl_associate_send(wrepl_socket, io);
488         return wrepl_associate_recv(req, io);
489 }
490
491
492 /*
493   fetch the partner tables - send
494 */
495 struct wrepl_request *wrepl_pull_table_send(struct wrepl_socket *wrepl_socket,
496                                             struct wrepl_pull_table *io)
497 {
498         struct wrepl_packet *packet;
499         struct wrepl_request *req;
500
501         packet = talloc_zero(wrepl_socket, struct wrepl_packet);
502         if (packet == NULL) return NULL;
503
504         packet->opcode                      = WREPL_OPCODE_BITS;
505         packet->assoc_ctx                   = io->in.assoc_ctx;
506         packet->mess_type                   = WREPL_REPLICATION;
507         packet->message.replication.command = WREPL_REPL_TABLE_QUERY;
508
509         req = wrepl_request_send(wrepl_socket, packet);
510
511         talloc_free(packet);
512
513         return req;     
514 }
515
516
517 /*
518   fetch the partner tables - recv
519 */
520 NTSTATUS wrepl_pull_table_recv(struct wrepl_request *req,
521                                TALLOC_CTX *mem_ctx,
522                                struct wrepl_pull_table *io)
523 {
524         struct wrepl_packet *packet=NULL;
525         NTSTATUS status;
526         struct wrepl_table *table;
527         int i;
528
529         status = wrepl_request_recv(req, req->wrepl_socket, &packet);
530         if (packet->mess_type != WREPL_REPLICATION) {
531                 status = NT_STATUS_NETWORK_ACCESS_DENIED;
532         } else if (packet->message.replication.command != WREPL_REPL_TABLE_REPLY) {
533                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
534         }
535         if (!NT_STATUS_IS_OK(status)) goto failed;
536
537         table = &packet->message.replication.info.table;
538         io->out.num_partners = table->partner_count;
539         io->out.partners = talloc_steal(mem_ctx, table->partners);
540         for (i=0;i<io->out.num_partners;i++) {
541                 talloc_steal(io->out.partners, io->out.partners[i].address);
542         }
543
544 failed:
545         talloc_free(packet);
546         return status;
547 }
548
549
550 /*
551   fetch the partner table - sync api
552 */
553 NTSTATUS wrepl_pull_table(struct wrepl_socket *wrepl_socket,
554                           TALLOC_CTX *mem_ctx,
555                           struct wrepl_pull_table *io)
556 {
557         struct wrepl_request *req = wrepl_pull_table_send(wrepl_socket, io);
558         return wrepl_pull_table_recv(req, mem_ctx, io);
559 }
560
561
562 /*
563   fetch the names for a WINS partner - send
564 */
565 struct wrepl_request *wrepl_pull_names_send(struct wrepl_socket *wrepl_socket,
566                                             struct wrepl_pull_names *io)
567 {
568         struct wrepl_packet *packet;
569         struct wrepl_request *req;
570
571         packet = talloc_zero(wrepl_socket, struct wrepl_packet);
572         if (packet == NULL) return NULL;
573
574         packet->opcode                         = WREPL_OPCODE_BITS;
575         packet->assoc_ctx                      = io->in.assoc_ctx;
576         packet->mess_type                      = WREPL_REPLICATION;
577         packet->message.replication.command    = WREPL_REPL_SEND_REQUEST;
578         packet->message.replication.info.owner = io->in.partner;
579
580         req = wrepl_request_send(wrepl_socket, packet);
581
582         talloc_free(packet);
583
584         return req;     
585 }
586
587
588 /*
589   extract a nbt_name from a WINS name buffer
590 */
591 static NTSTATUS wrepl_extract_name(struct nbt_name *name,
592                                    TALLOC_CTX *mem_ctx,
593                                    uint8_t *namebuf, uint32_t len)
594 {
595         char *s;
596
597         /* oh wow, what a nasty bug in windows ... */
598         if (namebuf[0] == 0x1b && len >= 16) {
599                 namebuf[0] = namebuf[15];
600                 namebuf[15] = 0x1b;
601         }
602
603         if (len < 17) {
604                 make_nbt_name_client(name, talloc_strndup(mem_ctx, namebuf, len));
605                 return NT_STATUS_OK;
606         }
607
608         s = talloc_strndup(mem_ctx, namebuf, 15);
609         trim_string(s, NULL, " ");
610         name->name = s;
611         name->type = namebuf[15];
612         if (len > 18) {
613                 name->scope = talloc_strndup(mem_ctx, namebuf+17, len-17);
614         } else {
615                 name->scope = NULL;
616         }
617
618         return NT_STATUS_OK;
619 }
620
621 /*
622   fetch the names for a WINS partner - recv
623 */
624 NTSTATUS wrepl_pull_names_recv(struct wrepl_request *req,
625                                TALLOC_CTX *mem_ctx,
626                                struct wrepl_pull_names *io)
627 {
628         struct wrepl_packet *packet=NULL;
629         NTSTATUS status;
630         int i;
631
632         status = wrepl_request_recv(req, req->wrepl_socket, &packet);
633         if (packet->mess_type != WREPL_REPLICATION ||
634             packet->message.replication.command != WREPL_REPL_SEND_REPLY) {
635                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
636         }
637         if (!NT_STATUS_IS_OK(status)) goto failed;
638
639         io->out.num_names = packet->message.replication.info.reply.num_names;
640
641         status = NT_STATUS_NO_MEMORY;
642
643         io->out.names = talloc_array(packet, struct wrepl_name, io->out.num_names);
644         if (io->out.names == NULL) goto failed;
645
646         /* convert the list of names and addresses to a sane format */
647         for (i=0;i<io->out.num_names;i++) {
648                 struct wrepl_wins_name *wname = &packet->message.replication.info.reply.names[i];
649                 struct wrepl_name *name = &io->out.names[i];
650                 status = wrepl_extract_name(&name->name, io->out.names, 
651                                             wname->name, wname->name_len);
652                 if (!NT_STATUS_IS_OK(status)) goto failed;
653
654                 /* trying to save 1 or 2 bytes on the wire isn't a good idea */
655                 if (wname->flags & 2) {
656                         int j;
657
658                         name->num_addresses = wname->addresses.addresses.num_ips;
659                         name->addresses = talloc_array(io->out.names, 
660                                                        struct wrepl_address, 
661                                                        name->num_addresses);
662                         if (name->addresses == NULL) goto failed;
663                         for (j=0;j<name->num_addresses;j++) {
664                                 name->addresses[j].owner = 
665                                         talloc_steal(name->addresses, 
666                                                      wname->addresses.addresses.ips[j].owner);
667                                 name->addresses[j].address = 
668                                         talloc_steal(name->addresses, 
669                                                      wname->addresses.addresses.ips[j].ip);
670                         }
671                 } else {
672                         name->num_addresses = 1;
673                         name->addresses = talloc(io->out.names, struct wrepl_address);
674                         if (name->addresses == NULL) goto failed;
675                         name->addresses[0].owner = talloc_steal(name->addresses, 
676                                                                 wname->addresses.address.owner);
677                         name->addresses[0].address = talloc_steal(name->addresses,
678                                                                   wname->addresses.address.ip);
679                 }
680         }
681
682         talloc_steal(mem_ctx, io->out.names);
683         status = NT_STATUS_OK;
684
685 failed:
686         talloc_free(packet);
687         return status;
688 }
689
690
691
692 /*
693   fetch the names for a WINS partner - sync api
694 */
695 NTSTATUS wrepl_pull_names(struct wrepl_socket *wrepl_socket,
696                           TALLOC_CTX *mem_ctx,
697                           struct wrepl_pull_names *io)
698 {
699         struct wrepl_request *req = wrepl_pull_names_send(wrepl_socket, io);
700         return wrepl_pull_names_recv(req, mem_ctx, io);
701 }