35661d913e6cb79fa475b492945374975ac1bca7
[abartlet/samba.git/.git] / source4 / rpc_server / dcerpc_server.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    server side dcerpc core code
5
6    Copyright (C) Andrew Tridgell 2003
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
25 /*
26   find the set of endpoint operations for an endpoint server
27 */
28 static const struct dcesrv_endpoint_ops *find_endpoint(struct server_context *smb,
29                                                        const struct dcesrv_endpoint *endpoint)
30 {
31         struct dce_endpoint *ep;
32         for (ep=smb->dcesrv.endpoint_list; ep; ep=ep->next) {
33                 if (ep->endpoint_ops->query_endpoint(endpoint)) {
34                         return ep->endpoint_ops;
35                 }
36         }
37         return NULL;
38 }
39
40 /*
41   find a call that is pending in our call list
42 */
43 static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_state *dce, uint16 call_id)
44 {
45         struct dcesrv_call_state *c;
46         for (c=dce->call_list;c;c=c->next) {
47                 if (c->pkt.call_id == call_id) {
48                         return c;
49                 }
50         }
51         return NULL;
52 }
53
54 /*
55   register an endpoint server
56 */
57 BOOL dcesrv_endpoint_register(struct server_context *smb, 
58                               const struct dcesrv_endpoint_ops *ops)
59 {
60         struct dce_endpoint *ep;
61         ep = malloc(sizeof(*ep));
62         if (!ep) {
63                 return False;
64         }
65         ep->endpoint_ops = ops;
66         DLIST_ADD(smb->dcesrv.endpoint_list, ep);
67         return True;
68 }
69
70 /*
71   connect to a dcerpc endpoint
72 */
73 NTSTATUS dcesrv_endpoint_connect(struct server_context *smb,
74                                  const struct dcesrv_endpoint *endpoint,
75                                  struct dcesrv_state **p)
76 {
77         TALLOC_CTX *mem_ctx;
78         NTSTATUS status;
79         const struct dcesrv_endpoint_ops *ops;
80
81         /* make sure this endpoint exists */
82         ops = find_endpoint(smb, endpoint);
83         if (!ops) {
84                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
85         }
86
87         mem_ctx = talloc_init("dcesrv_endpoint_connect");
88         if (!mem_ctx) {
89                 return NT_STATUS_NO_MEMORY;
90         }
91
92         *p = talloc_p(mem_ctx, struct dcesrv_state);
93         if (! *p) {
94                 talloc_destroy(mem_ctx);
95                 return NT_STATUS_NO_MEMORY;
96         }
97
98         (*p)->smb = smb;
99         (*p)->mem_ctx = mem_ctx;
100         (*p)->endpoint = *endpoint;
101         (*p)->ops = ops;
102         (*p)->private = NULL;
103         (*p)->call_list = NULL;
104         (*p)->cli_max_recv_frag = 0;
105         (*p)->ndr = NULL;
106         (*p)->dispatch = NULL;
107         (*p)->handles = NULL;
108         (*p)->next_handle = 0;
109
110         /* make sure the endpoint server likes the connection */
111         status = ops->connect(*p);
112         if (!NT_STATUS_IS_OK(status)) {
113                 talloc_destroy(mem_ctx);
114                 return status;
115         }
116         
117         return NT_STATUS_OK;
118 }
119
120
121 /*
122   disconnect a link to an endpoint
123 */
124 void dcesrv_endpoint_disconnect(struct dcesrv_state *p)
125 {
126         p->ops->disconnect(p);
127
128         /* destroy any handles */
129         while (p->handles) {
130                 TALLOC_CTX *m = p->handles->mem_ctx;
131                 DLIST_REMOVE(p->handles, p->handles);
132                 talloc_destroy(m);
133         }
134         
135         talloc_destroy(p->mem_ctx);
136 }
137
138 /*
139   return a dcerpc fault
140 */
141 static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code)
142 {
143         struct ndr_push *push;
144         struct dcerpc_packet pkt;
145         struct dcesrv_call_reply *rep;
146         NTSTATUS status;
147
148         /* setup a bind_ack */
149         pkt.rpc_vers = 5;
150         pkt.rpc_vers_minor = 0;
151         pkt.drep[0] = 0x10; /* Little endian */
152         pkt.drep[1] = 0;
153         pkt.drep[2] = 0;
154         pkt.drep[3] = 0;
155         pkt.auth_length = 0;
156         pkt.call_id = call->pkt.call_id;
157         pkt.ptype = DCERPC_PKT_FAULT;
158         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
159         pkt.u.fault.alloc_hint = 0;
160         pkt.u.fault.context_id = 0;
161         pkt.u.fault.cancel_count = 0;
162         pkt.u.fault.status = fault_code;
163
164         /* now form the NDR for the fault */
165         push = ndr_push_init_ctx(call->mem_ctx);
166         if (!push) {
167                 return NT_STATUS_NO_MEMORY;
168         }
169
170         status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
171         if (!NT_STATUS_IS_OK(status)) {
172                 return status;
173         }
174
175         rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
176         if (!rep) {
177                 return NT_STATUS_NO_MEMORY;
178         }
179
180         rep->data = ndr_push_blob(push);
181         SSVAL(rep->data.data,  DCERPC_FRAG_LEN_OFFSET, rep->data.length);
182
183         DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
184
185         return NT_STATUS_OK;    
186 }
187
188
189 /*
190   return a dcerpc fault from a ntstatus code
191 */
192 static NTSTATUS dcesrv_fault_nt(struct dcesrv_call_state *call, NTSTATUS status)
193 {
194         uint32 fault_code = DCERPC_FAULT_OTHER;
195
196         /* TODO: we need to expand this table to include more mappings */
197         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
198                 fault_code = DCERPC_FAULT_CONTEXT_MISMATCH;
199         }
200
201         return dcesrv_fault(call, fault_code);
202 }
203
204
205 /*
206   return a dcerpc bind_nak
207 */
208 static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32 reason)
209 {
210         struct ndr_push *push;
211         struct dcerpc_packet pkt;
212         struct dcesrv_call_reply *rep;
213         NTSTATUS status;
214
215         /* setup a bind_ack */
216         pkt.rpc_vers = 5;
217         pkt.rpc_vers_minor = 0;
218         pkt.drep[0] = 0x10; /* Little endian */
219         pkt.drep[1] = 0;
220         pkt.drep[2] = 0;
221         pkt.drep[3] = 0;
222         pkt.auth_length = 0;
223         pkt.call_id = call->pkt.call_id;
224         pkt.ptype = DCERPC_PKT_BIND_NAK;
225         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
226         pkt.u.bind_nak.reject_reason = reason;
227         pkt.u.bind_nak.num_versions = 0;
228
229         /* now form the NDR for the bind_nak */
230         push = ndr_push_init_ctx(call->mem_ctx);
231         if (!push) {
232                 return NT_STATUS_NO_MEMORY;
233         }
234
235         status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
236         if (!NT_STATUS_IS_OK(status)) {
237                 return status;
238         }
239
240         rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
241         if (!rep) {
242                 return NT_STATUS_NO_MEMORY;
243         }
244
245         rep->data = ndr_push_blob(push);
246         SSVAL(rep->data.data,  DCERPC_FRAG_LEN_OFFSET, rep->data.length);
247
248         DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
249
250         return NT_STATUS_OK;    
251 }
252
253
254 /*
255   handle a bind request
256 */
257 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
258 {
259         const char *uuid, *transfer_syntax;
260         uint32 if_version, transfer_syntax_version;
261         struct dcerpc_packet pkt;
262         struct ndr_push *push;
263         struct dcesrv_call_reply *rep;
264         NTSTATUS status;
265         uint32 result=0, reason=0;
266
267         if (call->pkt.u.bind.num_contexts != 1 ||
268             call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
269                 return dcesrv_bind_nak(call, 0);
270         }
271
272         if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.major_version;
273         uuid = GUID_string(call->mem_ctx, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
274         if (!uuid) {
275                 return dcesrv_bind_nak(call, 0);
276         }
277
278         transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].major_version;
279         transfer_syntax = GUID_string(call->mem_ctx, 
280                                       &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
281         if (!transfer_syntax ||
282             strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
283             NDR_GUID_VERSION != transfer_syntax_version) {
284                 /* we only do NDR encoded dcerpc */
285                 return dcesrv_bind_nak(call, 0);
286         }
287
288         if (!call->dce->ops->set_interface(call->dce, uuid, if_version)) {
289                 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
290                 /* we don't know about that interface */
291                 result = DCERPC_BIND_PROVIDER_REJECT;
292                 reason = DCERPC_BIND_REASON_ASYNTAX;
293         }
294
295         if (call->dce->cli_max_recv_frag == 0) {
296                 call->dce->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
297         }
298
299         /* setup a bind_ack */
300         pkt.rpc_vers = 5;
301         pkt.rpc_vers_minor = 0;
302         pkt.drep[0] = 0x10; /* Little endian */
303         pkt.drep[1] = 0;
304         pkt.drep[2] = 0;
305         pkt.drep[3] = 0;
306         pkt.auth_length = 0;
307         pkt.call_id = call->pkt.call_id;
308         pkt.ptype = DCERPC_PKT_BIND_ACK;
309         pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
310         pkt.u.bind_ack.max_xmit_frag = 0x2000;
311         pkt.u.bind_ack.max_recv_frag = 0x2000;
312         pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
313         if (call->dce->ndr) {
314                 pkt.u.bind_ack.secondary_address = talloc_asprintf(call->mem_ctx, "\\PIPE\\%s", 
315                                                                    call->dce->ndr->name);
316         } else {
317                 pkt.u.bind_ack.secondary_address = "";
318         }
319         pkt.u.bind_ack.num_results = 1;
320         pkt.u.bind_ack.ctx_list = talloc_p(call->mem_ctx, struct dcerpc_ack_ctx);
321         if (!pkt.u.bind_ack.ctx_list) {
322                 return NT_STATUS_NO_MEMORY;
323         }
324         pkt.u.bind_ack.ctx_list[0].result = result;
325         pkt.u.bind_ack.ctx_list[0].reason = reason;
326         GUID_from_string(uuid, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
327         pkt.u.bind_ack.ctx_list[0].syntax.major_version = if_version;
328         pkt.u.bind_ack.ctx_list[0].syntax.minor_version = 0;
329         pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
330
331         /* now form the NDR for the bind_ack */
332         push = ndr_push_init_ctx(call->mem_ctx);
333         if (!push) {
334                 return NT_STATUS_NO_MEMORY;
335         }
336
337         status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
338         if (!NT_STATUS_IS_OK(status)) {
339                 return status;
340         }
341
342         rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
343         if (!rep) {
344                 return NT_STATUS_NO_MEMORY;
345         }
346
347         rep->data = ndr_push_blob(push);
348         SSVAL(rep->data.data,  DCERPC_FRAG_LEN_OFFSET, rep->data.length);
349
350         DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
351
352         return NT_STATUS_OK;
353 }
354
355
356 /*
357   handle a dcerpc request packet
358 */
359 static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
360 {
361         struct ndr_pull *pull;
362         struct ndr_push *push;
363         uint16 opnum;
364         void *r;
365         NTSTATUS status;
366         DATA_BLOB stub;
367
368         opnum = call->pkt.u.request.opnum;
369
370         if (opnum >= call->dce->ndr->num_calls) {
371                 return dcesrv_fault(call, DCERPC_FAULT_OP_RNG_ERROR);
372         }
373
374         pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call->mem_ctx);
375         if (!pull) {
376                 return NT_STATUS_NO_MEMORY;
377         }
378
379         r = talloc(call->mem_ctx, call->dce->ndr->calls[opnum].struct_size);
380         if (!r) {
381                 return NT_STATUS_NO_MEMORY;
382         }
383
384         /* unravel the NDR for the packet */
385         status = call->dce->ndr->calls[opnum].ndr_pull(pull, NDR_IN, r);
386         if (!NT_STATUS_IS_OK(status)) {
387                 return dcesrv_fault(call, DCERPC_FAULT_NDR);
388         }
389
390         /* call the dispatch function */
391         status = call->dce->dispatch[opnum](call->dce, call->mem_ctx, r);
392         if (!NT_STATUS_IS_OK(status)) {
393                 return dcesrv_fault_nt(call, status);
394         }
395
396         /* form the reply NDR */
397         push = ndr_push_init_ctx(call->mem_ctx);
398         if (!push) {
399                 return NT_STATUS_NO_MEMORY;
400         }
401
402         status = call->dce->ndr->calls[opnum].ndr_push(push, NDR_OUT, r);
403         if (!NT_STATUS_IS_OK(status)) {
404                 return dcesrv_fault(call, DCERPC_FAULT_NDR);
405         }
406
407         stub = ndr_push_blob(push);
408
409         do {
410                 uint32 length;
411                 struct dcesrv_call_reply *rep;
412                 struct dcerpc_packet pkt;
413
414                 rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
415                 if (!rep) {
416                         return NT_STATUS_NO_MEMORY;
417                 }
418
419                 length = stub.length;
420                 if (length + DCERPC_RESPONSE_LENGTH > call->dce->cli_max_recv_frag) {
421                         length = call->dce->cli_max_recv_frag - DCERPC_RESPONSE_LENGTH;
422                 }
423
424                 /* form the dcerpc response packet */
425                 pkt.rpc_vers = 5;
426                 pkt.rpc_vers_minor = 0;
427                 pkt.drep[0] = 0x10; /* Little endian */
428                 pkt.drep[1] = 0;
429                 pkt.drep[2] = 0;
430                 pkt.drep[3] = 0;
431                 pkt.auth_length = 0;
432                 pkt.call_id = call->pkt.call_id;
433                 pkt.ptype = DCERPC_PKT_RESPONSE;
434                 pkt.pfc_flags = 0;
435                 if (!call->replies) {
436                         pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
437                 }
438                 if (length == stub.length) {
439                         pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
440                 }
441                 pkt.u.response.alloc_hint = stub.length;
442                 pkt.u.response.context_id = call->pkt.u.request.context_id;
443                 pkt.u.response.cancel_count = 0;
444                 pkt.u.response.stub_and_verifier.data = stub.data;
445                 pkt.u.response.stub_and_verifier.length = length;
446
447                 push = ndr_push_init_ctx(call->mem_ctx);
448                 if (!push) {
449                         return NT_STATUS_NO_MEMORY;
450                 }
451                 
452                 status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
453                 if (!NT_STATUS_IS_OK(status)) {
454                         return status;
455                 }
456                 
457                 rep->data = ndr_push_blob(push);
458                 SSVAL(rep->data.data,  DCERPC_FRAG_LEN_OFFSET, rep->data.length);
459
460                 DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
461                 
462                 stub.data += length;
463                 stub.length -= length;
464         } while (stub.length != 0);
465
466         return NT_STATUS_OK;
467 }
468
469
470 /*
471   provide some input to a dcerpc endpoint server. This passes data
472   from a dcerpc client into the server
473 */
474 NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
475 {
476         struct ndr_pull *ndr;
477         TALLOC_CTX *mem_ctx;
478         NTSTATUS status;
479         struct dcesrv_call_state *call;
480
481         mem_ctx = talloc_init("dcesrv_input");
482         if (!mem_ctx) {
483                 return NT_STATUS_NO_MEMORY;
484         }
485         call = talloc_p(mem_ctx, struct dcesrv_call_state);
486         if (!call) {
487                 talloc_destroy(mem_ctx);
488                 return NT_STATUS_NO_MEMORY;
489         }
490         call->mem_ctx = mem_ctx;
491         call->dce = dce;
492         call->replies = NULL;
493
494         ndr = ndr_pull_init_blob(data, mem_ctx);
495         if (!ndr) {
496                 talloc_destroy(mem_ctx);
497                 return NT_STATUS_NO_MEMORY;
498         }
499
500         status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
501         if (!NT_STATUS_IS_OK(status)) {
502                 talloc_destroy(mem_ctx);
503                 return status;
504         }
505
506         /* see if this is a continued packet */
507         if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
508                 struct dcesrv_call_state *call2 = call;
509                 uint32 alloc_size;
510
511                 /* we only allow fragmented requests, no other packet types */
512                 if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
513                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
514                 }
515
516                 /* this is a continuation of an existing call - find the call then
517                    tack it on the end */
518                 call = dcesrv_find_call(dce, call2->pkt.call_id);
519                 if (!call) {
520                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
521                 }
522
523                 if (call->pkt.ptype != call2->pkt.ptype) {
524                         /* trying to play silly buggers are we? */
525                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
526                 }
527
528                 alloc_size = call->pkt.u.request.stub_and_verifier.length +
529                         call2->pkt.u.request.stub_and_verifier.length;
530                 if (call->pkt.u.request.alloc_hint > alloc_size) {
531                         alloc_size = call->pkt.u.request.alloc_hint;
532                 }
533
534                 call->pkt.u.request.stub_and_verifier.data = 
535                         talloc_realloc(call->mem_ctx,
536                                        call->pkt.u.request.stub_and_verifier.data, alloc_size);
537                 if (!call->pkt.u.request.stub_and_verifier.data) {
538                         return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
539                 }
540                 memcpy(call->pkt.u.request.stub_and_verifier.data +
541                        call->pkt.u.request.stub_and_verifier.length,
542                        call2->pkt.u.request.stub_and_verifier.data,
543                        call2->pkt.u.request.stub_and_verifier.length);
544                 call->pkt.u.request.stub_and_verifier.length += 
545                         call2->pkt.u.request.stub_and_verifier.length;
546
547                 call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
548         }
549
550         /* this may not be the last pdu in the chain - if its isn't then
551            just put it on the call_list and wait for the rest */
552         if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
553                 DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
554                 return NT_STATUS_OK;
555         }
556
557         switch (call->pkt.ptype) {
558         case DCERPC_PKT_BIND:
559                 status = dcesrv_bind(call);
560                 break;
561         case DCERPC_PKT_REQUEST:
562                 status = dcesrv_request(call);
563                 break;
564         default:
565                 status = NT_STATUS_INVALID_PARAMETER;
566                 break;
567         }
568
569         /* if we are going to be sending a reply then add
570            it to the list of pending calls. We add it to the end to keep the call
571            list in the order we will answer */
572         if (NT_STATUS_IS_OK(status)) {
573                 DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
574         } else {
575                 talloc_destroy(mem_ctx);
576         }
577
578         return status;
579 }
580
581 /*
582   retrieve some output from a dcerpc server. The amount of data that
583   is wanted is in data->length and data->data is already allocated
584   to hold that much data.
585 */
586 NTSTATUS dcesrv_output(struct dcesrv_state *dce, DATA_BLOB *data)
587 {
588         struct dcesrv_call_state *call;
589         struct dcesrv_call_reply *rep;
590
591         call = dce->call_list;
592         if (!call || !call->replies) {
593                 return NT_STATUS_FOOBAR;
594         }
595         rep = call->replies;
596
597         if (data->length >= rep->data.length) {
598                 data->length = rep->data.length;
599         }
600
601         memcpy(data->data, rep->data.data, data->length);
602         rep->data.length -= data->length;
603         rep->data.data += data->length;
604
605         if (rep->data.length == 0) {
606                 /* we're done with this section of the call */
607                 DLIST_REMOVE(call->replies, rep);
608         }
609
610         if (call->replies == NULL) {
611                 /* we're done with the whole call */
612                 DLIST_REMOVE(dce->call_list, call);
613                 talloc_destroy(call->mem_ctx);
614         }
615
616         return NT_STATUS_OK;
617 }
618
619
620 /*
621   a useful function for implementing the query endpoint op
622  */
623 BOOL dcesrv_table_query(const struct dcerpc_interface_table *table,
624                         const struct dcesrv_endpoint *ep)
625 {
626         int i;
627         const struct dcerpc_endpoint_list *endpoints = table->endpoints;
628
629         if (ep->type != ENDPOINT_SMB) {
630                 return False;
631         }
632
633         for (i=0;i<endpoints->count;i++) {
634                 if (strcasecmp(ep->info.smb_pipe, endpoints->names[i]) == 0) {
635                         return True;
636                 }
637         }
638         return False;
639 }
640
641
642 /*
643   a useful function for implementing the lookup_endpoints op
644  */
645 int dcesrv_lookup_endpoints(const struct dcerpc_interface_table *table,
646                             TALLOC_CTX *mem_ctx,
647                             struct dcesrv_ep_iface **e)
648 {
649         *e = talloc_p(mem_ctx, struct dcesrv_ep_iface);
650         if (! *e) {
651                 return -1;
652         }
653
654         (*e)->uuid = table->uuid;
655         (*e)->if_version = table->if_version;
656         (*e)->endpoint.type = ENDPOINT_SMB;
657         (*e)->endpoint.info.smb_pipe = table->endpoints->names[0];
658
659         return 1;
660 }
661
662
663 /*
664   initialise the dcerpc server subsystem
665 */
666 BOOL dcesrv_init(struct server_context *smb)
667 {
668         rpc_echo_init(smb);
669         rpc_epmapper_init(smb);
670         return True;
671 }