0e9eff147c59c2b2b01c39139ee84400d83c4de4
[samba.git] / source4 / lib / com / dcom / main.c
1 /*
2    Unix SMB/CIFS implementation.
3    Main DCOM functionality
4    Copyright (C) 2004 Jelmer Vernooij <jelmer@samba.org>
5    Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "librpc/gen_ndr/epmapper.h"
25 #include "librpc/gen_ndr/ndr_remact_c.h"
26 #include "librpc/gen_ndr/com_dcom.h"
27 #include "librpc/gen_ndr/dcom.h"
28 #include "librpc/rpc/dcerpc.h"
29 #include "lib/com/dcom/dcom.h"
30 #include "librpc/ndr/ndr_table.h"
31 #include "../lib/util/dlinklist.h"
32 #include "auth/credentials/credentials.h"
33 #include "libcli/composite/composite.h"
34
35 #undef strncasecmp
36
37 #define DCOM_NEGOTIATED_PROTOCOLS { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NCALRPC }
38
39 static NTSTATUS dcerpc_binding_from_STRINGBINDING(TALLOC_CTX *mem_ctx, struct dcerpc_binding **b_out, struct STRINGBINDING *bd)
40 {
41         char *tstr;
42         char *bstr;
43         enum dcerpc_transport_t transport;
44         struct dcerpc_binding *b;
45
46         transport = dcerpc_transport_by_endpoint_protocol(bd->wTowerId);
47         if (transport == NCA_UNKNOWN) {
48                 DEBUG(1, ("Can't find transport match endpoint protocol %d\n", bd->wTowerId));
49                 return NT_STATUS_NOT_SUPPORTED;
50         }
51
52         tstr = derpc_transport_string_by_transport(transport);
53         bstr = talloc_asprintf(mem_ctx, "%s:%s", tstr, bd->NetworkAddr);
54         if (bstr == NULL) {
55                 return NT_STATUS_NO_MEMORY;
56         }
57
58         status = dcerpc_parse_binding(mem_ctx, bstr, &b);
59         TALLOC_FREE(bstr);
60         if (!NT_STATUS_IS_OK(status)) {
61                 return status;
62         }
63
64         *b_out = b;
65         return NT_STATUS_OK;
66 }
67
68 struct cli_credentials *dcom_get_server_credentials(struct com_context *ctx, const char *server)
69 {
70         struct dcom_server_credentials *c;
71         struct cli_credentials *d;
72
73         d = NULL;
74         for (c = ctx->dcom->credentials; c; c = c->next) {
75                 if (c->server == NULL) {
76                         d = c->credentials;
77                         continue;
78                 }
79                 if (server && !strcmp(c->server, server)) return c->credentials;
80         }
81         return d;
82 }
83
84 /**
85  * Register credentials for a specific server.
86  *
87  * @param ctx COM context
88  * @param server Name of server, can be NULL
89  * @param credentials Credentials object
90  */
91 void dcom_add_server_credentials(struct com_context *ctx, const char *server, 
92                                                                  struct cli_credentials *credentials)
93 {
94         struct dcom_server_credentials *c;
95
96         /* FIXME: Don't use talloc_find_parent_bytype */
97         for (c = ctx->dcom->credentials; c; c = c->next) {
98                 if ((server == NULL && c->server == NULL) ||
99                         (server != NULL && c->server != NULL && 
100                          !strcmp(c->server, server))) {
101                         if (c->credentials && c->credentials != credentials) {
102                                 talloc_unlink(c, c->credentials);
103                                 c->credentials = credentials;
104                                 if (talloc_find_parent_bytype(c->credentials, struct dcom_server_credentials))
105                                         (void)talloc_reference(c, c->credentials);
106                                 else
107                                         talloc_steal(c, c->credentials);
108                         }
109
110                         return;
111                 }
112         }
113
114         c = talloc(ctx->event_ctx, struct dcom_server_credentials);
115         c->server = talloc_strdup(c, server);
116         c->credentials = credentials;
117         if (talloc_find_parent_bytype(c->credentials, struct dcom_server_credentials))
118                 (void)talloc_reference(c, c->credentials);
119         else
120                 talloc_steal(c, c->credentials);
121
122         DLIST_ADD(ctx->dcom->credentials, c);
123 }
124
125 void dcom_update_credentials_for_aliases(struct com_context *ctx, 
126                                                                                  const char *server, 
127                                                                                  struct DUALSTRINGARRAY *pds)
128 {
129         struct cli_credentials *cc;
130         struct dcerpc_binding *b;
131         uint32_t i;
132         NTSTATUS status;
133
134         cc = dcom_get_server_credentials(ctx, server);
135         for (i = 0; pds->stringbindings[i]; ++i) {
136                 if (pds->stringbindings[i]->wTowerId != EPM_PROTOCOL_TCP) 
137                                         continue;
138                 status = dcerpc_binding_from_STRINGBINDING(ctx, &b, pds->stringbindings[i]);
139                 if (!NT_STATUS_IS_OK(status)) 
140                         continue;
141                 dcom_add_server_credentials(ctx, b->host, cc);
142                 talloc_free(b);
143         }
144 }
145
146 struct dcom_client_context *dcom_client_init(struct com_context *ctx, struct cli_credentials *credentials)
147 {
148         ctx->dcom = talloc_zero(ctx, struct dcom_client_context);
149         if (!credentials) {
150                 credentials = cli_credentials_init(ctx);
151                 cli_credentials_set_conf(credentials, ctx->lp_ctx);
152                 cli_credentials_parse_string(credentials, "%", CRED_SPECIFIED);
153         }
154         dcom_add_server_credentials(ctx, NULL, credentials);
155         return ctx->dcom;
156 }
157
158 static NTSTATUS dcom_connect_host(struct com_context *ctx, 
159                                                                   struct dcerpc_pipe **p, const char *server)
160 {
161         struct dcerpc_binding *bd;
162         const char * available_transports[] = { "ncacn_ip_tcp", "ncacn_np" };
163         int i;
164         NTSTATUS status;
165         TALLOC_CTX *loc_ctx;
166
167         if (server == NULL) { 
168                 return dcerpc_pipe_connect(ctx->event_ctx, p, "ncalrpc", 
169                                                                    &ndr_table_IRemoteActivation,
170                                                                    dcom_get_server_credentials(ctx, NULL), ctx->event_ctx, ctx->lp_ctx);
171         }
172         loc_ctx = talloc_new(ctx);
173
174         /* Allow server name to contain a binding string */
175         if (strchr(server, ':') && 
176                 NT_STATUS_IS_OK(dcerpc_parse_binding(loc_ctx, server, &bd))) {
177                 if (DEBUGLVL(11))
178                         bd->flags |= DCERPC_DEBUG_PRINT_BOTH;
179                 status = dcerpc_pipe_connect_b(ctx->event_ctx, p, bd, 
180                                                                            &ndr_table_IRemoteActivation,
181                                                                    dcom_get_server_credentials(ctx, bd->host), ctx->event_ctx, ctx->lp_ctx);
182                 goto end;
183         }
184
185         for (i = 0; i < ARRAY_SIZE(available_transports); i++)
186         {
187                 char *binding = talloc_asprintf(loc_ctx, "%s:%s", available_transports[i], server);
188                 if (!binding) {
189                         status = NT_STATUS_NO_MEMORY;
190                         goto end;
191                 }
192                 status = dcerpc_pipe_connect(ctx->event_ctx, p, binding, 
193                                                                          &ndr_table_IRemoteActivation, 
194                                                                          dcom_get_server_credentials(ctx, server), 
195                                                                          ctx->event_ctx, ctx->lp_ctx);
196
197                 if (NT_STATUS_IS_OK(status)) {
198                         if (DEBUGLVL(11))
199                                 (*p)->conn->flags |= DCERPC_DEBUG_PRINT_BOTH;
200                         goto end;
201                 } else {
202                         DEBUG(1,(__location__": dcom_connect_host : %s\n", get_friendly_nt_error_msg(status)));
203                 }
204         }
205
206 end:
207         talloc_free(loc_ctx);
208         return status;
209 }
210
211 struct dcom_object_exporter *object_exporter_by_oxid(struct com_context *ctx, 
212                                                                                                          uint64_t oxid)
213 {
214         struct dcom_object_exporter *ox;
215         for (ox = ctx->dcom->object_exporters; ox; ox = ox->next) {
216                 if (ox->oxid == oxid) {
217                         return ox;
218                 }
219         }
220
221         return NULL; 
222 }
223
224 struct dcom_object_exporter *object_exporter_update_oxid(struct com_context *ctx, uint64_t oxid, struct DUALSTRINGARRAY *bindings)
225 {
226         struct dcom_object_exporter *ox;
227         ox = object_exporter_by_oxid(ctx, oxid);
228         if (!ox) {
229                 ox = talloc_zero(ctx, struct dcom_object_exporter);
230                 DLIST_ADD(ctx->dcom->object_exporters, ox);
231                 ox->oxid = oxid;
232         } else {
233                 talloc_free(ox->bindings);
234         }
235         ox->bindings = bindings;
236         talloc_steal(ox, bindings);
237         return ox;
238 }
239
240 struct dcom_object_exporter *object_exporter_by_ip(struct com_context *ctx, struct IUnknown *ip)
241 {
242         return object_exporter_by_oxid(ctx, ip->obj.u_objref.u_standard.std.oxid);
243 }
244
245 WERROR dcom_create_object(struct com_context *ctx, struct GUID *clsid, const char *server, int num_ifaces, struct GUID *iid, struct IUnknown ***ip, HRESULT *results)
246 {
247         uint16_t protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
248         struct dcerpc_pipe *p;
249         struct dcom_object_exporter *m;
250         NTSTATUS status;
251         struct RemoteActivation r;
252         struct DUALSTRINGARRAY *pds;
253         int i;
254         HRESULT hr;
255         uint64_t oxid;
256         struct GUID ipidRemUnknown;
257         struct IUnknown *ru_template;
258         struct ORPCTHAT that;
259         uint32_t AuthnHint;
260         struct COMVERSION ServerVersion;
261         struct MInterfacePointer **ifaces;
262         TALLOC_CTX *loc_ctx;
263
264         status = dcom_connect_host(ctx, &p, server);
265         if (NT_STATUS_IS_ERR(status)) {
266                 DEBUG(1, ("Unable to connect to %s - %s\n", server, get_friendly_nt_error_msg(status)));
267                 return ntstatus_to_werror(status);
268         }
269         loc_ctx = talloc_new(ctx);
270
271         ifaces = talloc_array(loc_ctx, struct MInterfacePointer *, num_ifaces);
272
273         ZERO_STRUCT(r.in);
274         r.in.this.version.MajorVersion = COM_MAJOR_VERSION;
275         r.in.this.version.MinorVersion = COM_MINOR_VERSION;
276         r.in.this.cid = GUID_random();
277         r.in.Clsid = *clsid;
278         r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
279         r.in.num_protseqs = ARRAY_SIZE(protseq);
280         r.in.protseq = protseq;
281         r.in.Interfaces = num_ifaces;
282         r.in.pIIDs = iid;
283         r.out.that = &that;
284         r.out.pOxid = &oxid;
285         r.out.pdsaOxidBindings = &pds;
286         r.out.ipidRemUnknown = &ipidRemUnknown;
287         r.out.AuthnHint = &AuthnHint;
288         r.out.ServerVersion = &ServerVersion;
289         r.out.hr = &hr;
290         r.out.ifaces = ifaces;
291         r.out.results = results;
292
293         status = dcerpc_RemoteActivation(p, loc_ctx, &r);
294         talloc_free(p);
295
296         if(NT_STATUS_IS_ERR(status)) {
297                 DEBUG(1, ("Error while running RemoteActivation %s\n", nt_errstr(status)));
298                 hr = ntstatus_to_werror(status);
299                 goto end;
300         }
301
302         if(!W_ERROR_IS_OK(r.out.result)) {
303                 hr = r.out.result;
304                 goto end;
305         }
306
307         if(!HRES_IS_OK(hr)) {
308                 goto end;
309         }
310
311         m = object_exporter_update_oxid(ctx, oxid, pds);
312
313         ru_template = NULL;
314         *ip = talloc_array(ctx, struct IUnknown *, num_ifaces);
315         for (i = 0; i < num_ifaces; i++) {
316                 (*ip)[i] = NULL;
317                 if (W_ERROR_IS_OK(results[i])) {
318                         status = dcom_IUnknown_from_OBJREF(ctx, &(*ip)[i], &r.out.ifaces[i]->obj);
319                         if (!NT_STATUS_IS_OK(status)) {
320                                 results[i] = ntstatus_to_werror(status);
321                         } else if (!ru_template)
322                                 ru_template = (*ip)[i];
323                 }
324         }
325
326         /* TODO:avg check when exactly oxid should be updated,its lifetime etc */
327         if (m->rem_unknown && memcmp(&m->rem_unknown->obj.u_objref.u_standard.std.ipid, &ipidRemUnknown, sizeof(ipidRemUnknown))) {
328                 talloc_free(m->rem_unknown);
329                 m->rem_unknown = NULL;
330         }
331         if (!m->rem_unknown) {
332                 if (!ru_template) {
333                         DEBUG(1,("dcom_create_object: Cannot Create IRemUnknown - template interface not available\n"));
334                         hr = WERR_GEN_FAILURE;
335                 }
336                 m->rem_unknown = talloc_zero(m, struct IRemUnknown);
337                 memcpy(m->rem_unknown, ru_template, sizeof(struct IUnknown));
338                 GUID_from_string(COM_IREMUNKNOWN_UUID, &m->rem_unknown->obj.iid);
339                 m->rem_unknown->obj.u_objref.u_standard.std.ipid = ipidRemUnknown;
340                 m->rem_unknown->vtable = (struct IRemUnknown_vtable *)dcom_proxy_vtable_by_iid(&m->rem_unknown->obj.iid);
341                 /* TODO:avg copy stringbindigs?? */
342         }
343
344         dcom_update_credentials_for_aliases(ctx, server, pds);
345         { 
346                 char *c; 
347                 c = strchr(server, '['); 
348                 if (m->host) talloc_free(m->host); 
349                 m->host = c ? talloc_strndup(m, server, c - server) : talloc_strdup(m, server); 
350         }
351         hr = WERR_OK;
352 end:
353         talloc_free(loc_ctx);
354         return hr;
355 }
356
357 int find_similar_binding(struct STRINGBINDING **sb, const char *host)
358
359         int i, l;
360         l = strlen(host);
361         for (i = 0; sb[i]; ++i) {
362                 if ((sb[i]->wTowerId == EPM_PROTOCOL_TCP) && !strncasecmp(host, sb[i]->NetworkAddr, l) && (sb[i]->NetworkAddr[l] == '['))
363                 break;
364         }
365         return i;
366 }
367
368 WERROR dcom_query_interface(struct IUnknown *d, uint32_t cRefs, uint16_t cIids, struct GUID *iids, struct IUnknown **ip, WERROR *results)
369 {
370         struct dcom_object_exporter *ox;
371         struct REMQIRESULT *rqir;
372         WERROR result;
373         NTSTATUS status;
374         int i;
375         TALLOC_CTX *loc_ctx;
376         struct IUnknown ru;
377
378         loc_ctx = talloc_new(d);
379         ox = object_exporter_by_ip(d->ctx, d);
380
381         result = IRemUnknown_RemQueryInterface(ox->rem_unknown, loc_ctx, &IUnknown_ipid(d), cRefs, cIids, iids, &rqir);
382         if (!W_ERROR_IS_OK(result)) {
383                 DEBUG(1, ("dcom_query_interface failed: %08X\n", W_ERROR_V(result)));
384                 talloc_free(loc_ctx);
385                 return result;
386         }
387         ru = *(struct IUnknown *)ox->rem_unknown;
388         for (i = 0; i < cIids; ++i) {
389                 ip[i] = NULL;
390                 results[i] = rqir[i].hResult;
391                 if (W_ERROR_IS_OK(results[i])) {
392                         ru.obj.iid = iids[i];
393                         ru.obj.u_objref.u_standard.std = rqir[i].std;
394                         status = dcom_IUnknown_from_OBJREF(d->ctx, &ip[i], &ru.obj);
395                         if (!NT_STATUS_IS_OK(status)) {
396                                 results[i] = ntstatus_to_werror(status);
397                         }
398                 }
399         }
400
401         talloc_free(loc_ctx);
402         return WERR_OK;
403 }
404
405 int is_ip_binding(const char* s)
406 {
407         while (*s && (*s != '[')) {
408                 if (((*s >= '0') && (*s <= '9')) || *s == '.')
409                         ++s;
410                 else
411                     return 0;
412         }
413         return 1;
414 }
415
416 NTSTATUS dcom_get_pipe(struct IUnknown *iface, struct dcerpc_pipe **pp)
417 {
418         struct dcerpc_binding *binding;
419         struct GUID iid;
420         uint64_t oxid;
421         NTSTATUS status;
422         int i, j, isimilar;
423         struct dcerpc_pipe *p;
424         struct dcom_object_exporter *ox;
425         const struct ndr_interface_table *table;
426
427         ox = object_exporter_by_oxid(iface->ctx, iface->obj.u_objref.u_standard.std.oxid);
428         if (!ox) {
429                 DEBUG(0, ("dcom_get_pipe: OXID not found\n"));
430                 return NT_STATUS_NOT_SUPPORTED;
431         }
432
433         p = ox->pipe;
434         
435         iid = iface->vtable->iid;
436         table = ndr_table_by_uuid(&iid);
437         if (table == NULL) {
438                 char *guid_str;
439                 guid_str = GUID_string(NULL, &iid);
440                 DEBUG(0,(__location__": dcom_get_pipe - unrecognized interface{%s}\n", guid_str));
441                 talloc_free(guid_str);
442                 return NT_STATUS_NOT_SUPPORTED;
443         }
444
445         if (p && p->last_fault_code) {
446                 talloc_free(p);
447                 ox->pipe = p = NULL;
448         }
449
450         if (p) {
451                 if (!GUID_equal(&p->syntax.uuid, &iid)) {
452                         ox->pipe->syntax.uuid = iid;
453
454                         /* interface will always be present, so 
455                          * idl_iface_by_uuid can't return NULL */
456                         /* status = dcerpc_secondary_context(p, &p2, idl_iface_by_uuid(&iid)); */
457                         status = dcerpc_alter_context(p, p, &ndr_table_by_uuid(&iid)->syntax_id, &p->transfer_syntax);
458                 } else
459                         status = NT_STATUS_OK;
460                 *pp = p;
461                 return status;
462         }
463
464         status = NT_STATUS_NO_MORE_ENTRIES;
465
466         /* To avoid delays whe connecting nonroutable bindings we 1st check binding starting with hostname */
467         /* FIX:low create concurrent connections to all bindings, fastest wins - Win2k and newer does this way???? */
468         isimilar = find_similar_binding(ox->bindings->stringbindings, ox->host);
469         DEBUG(1, (__location__": dcom_get_pipe: host=%s, similar=%s\n", ox->host, ox->bindings->stringbindings[isimilar] ? ox->bindings->stringbindings[isimilar]->NetworkAddr : "None"));
470         j = isimilar - 1;
471         for (i = 0; ox->bindings->stringbindings[i]; ++i) {
472                 if (!ox->bindings->stringbindings[++j]) j = 0;
473                 /* FIXME:LOW Use also other transports if possible */
474                 if ((j != isimilar) && (ox->bindings->stringbindings[j]->wTowerId != EPM_PROTOCOL_TCP || !is_ip_binding(ox->bindings->stringbindings[j]->NetworkAddr))) {
475                         DEBUG(9, ("dcom_get_pipe: Skipping stringbinding %24.24s\n", ox->bindings->stringbindings[j]->NetworkAddr));
476                         continue;
477                 }
478                 DEBUG(9, ("dcom_get_pipe: Trying stringbinding %s\n", ox->bindings->stringbindings[j]->NetworkAddr));
479                 status = dcerpc_binding_from_STRINGBINDING(iface->ctx, &binding, 
480                                                            ox->bindings->stringbindings[j]);
481                 if (!NT_STATUS_IS_OK(status)) {
482                         DEBUG(1, ("Error parsing string binding"));
483                 } else {
484                         /* FIXME:LOW Make flags more flexible */
485                         binding->flags |= DCERPC_AUTH_NTLM | DCERPC_SIGN;
486                         if (DEBUGLVL(11))
487                                 binding->flags |= DCERPC_DEBUG_PRINT_BOTH;
488                         status = dcerpc_pipe_connect_b(iface->ctx->event_ctx, &p, binding, 
489                                                        ndr_table_by_uuid(&iid),
490                                                        dcom_get_server_credentials(iface->ctx, binding->host),
491                                                            iface->ctx->event_ctx, iface->ctx->lp_ctx);
492                         talloc_unlink(iface->ctx, binding);
493                 }
494                 if (NT_STATUS_IS_OK(status)) break;
495         }
496
497         if (NT_STATUS_IS_ERR(status)) {
498                 DEBUG(0, ("Unable to connect to remote host - %s\n", nt_errstr(status)));
499                 return status;
500         }
501
502         DEBUG(2, ("Successfully connected to OXID %llx\n", (long long)oxid));
503         
504         ox->pipe = *pp = p;
505
506         return NT_STATUS_OK;
507 }
508
509 NTSTATUS dcom_OBJREF_from_IUnknown(TALLLOC_CTX *mem_ctx, struct OBJREF *o, struct IUnknown *p)
510 {
511         /* FIXME: Cache generated objref objects? */
512         ZERO_STRUCTP(o);
513         
514         if (!p) {
515                 o->signature = OBJREF_SIGNATURE;
516                 o->flags = OBJREF_NULL;
517         } else {
518                 *o = p->obj;
519                 switch(o->flags) {
520                 case OBJREF_CUSTOM: {
521                         marshal_fn marshal;
522
523                         marshal = dcom_marshal_by_clsid(&o->u_objref.u_custom.clsid);
524                         if (marshal) {
525                                 return marshal(mem_ctx, p, o);
526                         } else {
527                                 return NT_STATUS_NOT_SUPPORTED;
528                         }
529                 }
530                 }
531         }
532
533         return NT_STATUS_OK;
534 }
535
536 enum ndr_err_code dcom_IUnknown_from_OBJREF(struct com_context *ctx, struct IUnknown **_p, struct OBJREF *o)
537 {
538         struct IUnknown *p;
539         struct dcom_object_exporter *ox;
540         unmarshal_fn unmarshal;
541
542         switch(o->flags) {
543         case OBJREF_NULL: 
544                 *_p = NULL;
545                 return NDR_ERR_SUCCESS;
546
547         case OBJREF_STANDARD:
548                 p = talloc_zero(ctx, struct IUnknown);
549                 p->ctx = ctx;
550                 p->obj = *o;
551                 p->vtable = dcom_proxy_vtable_by_iid(&o->iid);
552
553                 if (!p->vtable) {
554                         DEBUG(0, ("Unable to find proxy class for interface with IID %s\n", GUID_string(ctx, &o->iid)));
555                         return NDR_ERR_INVALID_POINTER;
556                 }
557
558                 p->vtable->Release_send = dcom_release_send;
559
560                 ox = object_exporter_by_oxid(ctx, o->u_objref.u_standard.std.oxid);
561                 /* FIXME: Add object to list of objects to ping */
562                 *_p = p;
563                 return NDR_ERR_SUCCESS;
564                 
565         case OBJREF_HANDLER:
566                 p = talloc_zero(ctx, struct IUnknown);
567                 p->ctx = ctx;   
568                 p->obj = *o;
569                 ox = object_exporter_by_oxid(ctx, o->u_objref.u_handler.std.oxid );
570                 /* FIXME: Add object to list of objects to ping */
571 /*FIXME         p->vtable = dcom_vtable_by_clsid(&o->u_objref.u_handler.clsid);*/
572                 /* FIXME: Do the custom unmarshaling call */
573         
574                 *_p = p;
575                 return NDR_ERR_BAD_SWITCH;
576                 
577         case OBJREF_CUSTOM:
578                 p = talloc_zero(ctx, struct IUnknown);
579                 p->ctx = ctx;   
580                 p->vtable = NULL;
581                 p->obj = *o;
582                 unmarshal = dcom_unmarshal_by_clsid(&o->u_objref.u_custom.clsid);
583                 *_p = p;
584                 if (unmarshal) {
585                     return unmarshal(ctx, o, _p);
586                 } else {
587                     return NDR_ERR_BAD_SWITCH;
588                 }
589         }
590
591         return NDR_ERR_BAD_SWITCH;
592 }
593
594 uint64_t dcom_get_current_oxid(void)
595 {
596         return getpid();
597 }
598
599 /* FIXME:Fake async dcom_get_pipe_* */
600 struct composite_context *dcom_get_pipe_send(struct IUnknown *d, TALLOC_CTX *mem_ctx)
601 {
602         struct composite_context *c;
603
604         c = composite_create(0, d->ctx->event_ctx);
605         if (c == NULL) return NULL;
606         c->private_data = d;
607         /* composite_done(c); bugged - callback is triggered twice by composite_continue and composite_done */
608         c->state = COMPOSITE_STATE_DONE; /* this is workaround */
609
610         return c;
611 }
612
613 NTSTATUS dcom_get_pipe_recv(struct composite_context *c, struct dcerpc_pipe **pp)
614 {
615         NTSTATUS status;
616
617         status = dcom_get_pipe((struct IUnknown *)c->private_data, pp);
618         talloc_free(c);
619
620         return status;
621 }
622
623 /* FIXME:avg put IUnknown_Release_out into header */
624 struct IUnknown_Release_out {
625         uint32_t result;
626 };
627
628 void dcom_release_continue(struct composite_context *cr)
629 {
630         struct composite_context *c;
631         struct IUnknown *d;
632         struct IUnknown_Release_out *out;
633         WERROR r;
634
635         c = talloc_get_type(cr->async.private_data, struct composite_context);
636         d = c->private_data;
637         r = IRemUnknown_RemRelease_recv(cr);
638         talloc_free(d);
639         out = talloc_zero(c, struct IUnknown_Release_out);
640         out->result = W_ERROR_V(r);
641         c->private_data = out;
642         composite_done(c);
643 }
644
645 struct composite_context *dcom_release_send(struct IUnknown *d, TALLOC_CTX *mem_ctx)
646 {
647         struct composite_context *c, *cr;
648         struct REMINTERFACEREF iref;
649         struct dcom_object_exporter *ox;
650
651         c = composite_create(d->ctx, d->ctx->event_ctx);
652         if (c == NULL) return NULL;
653         c->private_data = d;
654
655         ox = object_exporter_by_ip(d->ctx, d);
656         iref.ipid = IUnknown_ipid(d);
657         iref.cPublicRefs = 5;
658         iref.cPrivateRefs = 0;
659         cr = IRemUnknown_RemRelease_send(ox->rem_unknown, mem_ctx, 1, &iref);
660
661         composite_continue(c, cr, dcom_release_continue, c);
662         return c;
663 }
664
665 uint32_t dcom_release_recv(struct composite_context *c)
666 {
667         NTSTATUS status;
668         WERROR r;
669
670         status = composite_wait(c);
671         if (!NT_STATUS_IS_OK(status))
672                 r = ntstatus_to_werror(status);
673         else
674                 W_ERROR_V(r) = ((struct IUnknown_Release_out *)c->private_data)->result;
675         talloc_free(c);
676         return W_ERROR_IS_OK(r) ? 0 : W_ERROR_V(r);
677 }
678
679 uint32_t dcom_release(void *interface, TALLOC_CTX *mem_ctx)
680 {
681         struct composite_context *c;
682
683         c = dcom_release_send(interface, mem_ctx);
684         return dcom_release_recv(c);
685 }
686
687 void dcom_proxy_async_call_recv_pipe_send_rpc(struct composite_context *c_pipe)
688 {
689         struct composite_context *c;
690         struct dcom_proxy_async_call_state *s;
691         struct dcerpc_pipe *p;
692         struct rpc_request *req;
693         NTSTATUS status;
694
695         c = c_pipe->async.private_data;
696         s = talloc_get_type(c->private_data, struct dcom_proxy_async_call_state);
697
698         status = dcom_get_pipe_recv(c_pipe, &p);
699         if (!NT_STATUS_IS_OK(status)) {
700                 composite_error(c, NT_STATUS_RPC_NT_CALL_FAILED);
701                 return;
702         }
703 /*TODO: FIXME - for now this unused anyway */
704         req = dcerpc_ndr_request_send(p, &s->d->obj.u_objref.u_standard.std.ipid, s->table, s->opnum, s, s->r);
705         composite_continue_rpc(c, req, s->continuation, c);
706 }