shlwapi: Implement stub for ZoneCheckUrlExW.
[wine] / dlls / rpcrt4 / rpc_epmap.c
1 /*
2  * RPC endpoint mapper
3  *
4  * Copyright 2002 Greg Turner
5  * Copyright 2001 Ove Kåven, TransGaming Technologies
6  * Copyright 2008 Robert Shearman (for CodeWeavers)
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library 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 GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include <stdarg.h>
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28
29 #include "rpc.h"
30
31 #include "wine/debug.h"
32 #include "wine/exception.h"
33
34 #include "rpc_binding.h"
35 #include "epm.h"
36 #include "epm_towers.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(ole);
39
40 /* The "real" RPC portmapper endpoints that I know of are:
41  *
42  *  ncadg_ip_udp: 135
43  *  ncacn_ip_tcp: 135
44  *  ncacn_np: \\pipe\epmapper
45  *  ncalrpc: epmapper
46  *  ncacn_http: 593
47  *
48  * If the user's machine ran a DCE RPC daemon, it would
49  * probably be possible to connect to it, but there are many
50  * reasons not to, like:
51  *  - the user probably does *not* run one, and probably
52  *    shouldn't be forced to run one just for local COM
53  *  - very few Unix systems use DCE RPC... if they run a RPC
54  *    daemon at all, it's usually Sun RPC
55  *  - DCE RPC registrations are persistent and saved on disk,
56  *    while MS-RPC registrations are documented as non-persistent
57  *    and stored only in RAM, and auto-destroyed when the process
58  *    dies (something DCE RPC can't do)
59  *
60  * Of course, if the user *did* want to run a DCE RPC daemon anyway,
61  * there would be interoperability advantages, like the possibility
62  * of running a fully functional DCOM server using Wine...
63  */
64
65 static const struct epm_endpoints
66 {
67     const char *protseq;
68     const char *endpoint;
69 } epm_endpoints[] =
70 {
71     { "ncacn_np", "\\pipe\\epmapper" },
72     { "ncacn_ip_tcp", "135" },
73     { "ncacn_ip_udp", "135" },
74     { "ncalrpc", "epmapper" },
75     { "ncacn_http", "593" },
76 };
77
78 static BOOL start_rpcss(void)
79 {
80     PROCESS_INFORMATION pi;
81     STARTUPINFOW si;
82     static WCHAR cmd[6];
83     static const WCHAR rpcss[] = {'r','p','c','s','s',0};
84     BOOL rslt;
85
86     TRACE("\n");
87
88     ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
89     ZeroMemory(&si, sizeof(STARTUPINFOA));
90     si.cb = sizeof(STARTUPINFOA);
91
92     memcpy(cmd, rpcss, sizeof(rpcss));
93
94     rslt = CreateProcessW(
95                           NULL,           /* executable */
96                           cmd,            /* command line */
97                           NULL,           /* process security attributes */
98                           NULL,           /* primary thread security attributes */
99                           FALSE,          /* inherit handles */
100                           0,              /* creation flags */
101                           NULL,           /* use parent's environment */
102                           NULL,           /* use parent's current directory */
103                           &si,            /* STARTUPINFO pointer */
104                           &pi             /* PROCESS_INFORMATION */
105                           );
106
107     if (rslt)
108     {
109         CloseHandle(pi.hProcess);
110         CloseHandle(pi.hThread);
111         Sleep(100);
112     }
113
114     return rslt;
115 }
116
117 static inline BOOL is_epm_destination_local(RPC_BINDING_HANDLE handle)
118 {
119     RpcBinding *bind = handle;
120     const char *protseq = bind->Protseq;
121     const char *network_addr = bind->NetworkAddr;
122
123     return ((!strcmp(protseq, "ncalrpc") && !network_addr) ||
124             (!strcmp(protseq, "ncacn_np") &&
125                 (!network_addr || !strcmp(network_addr, "."))));
126 }
127
128 static RPC_STATUS get_epm_handle_client(RPC_BINDING_HANDLE handle, RPC_BINDING_HANDLE *epm_handle)
129 {
130     RpcBinding *bind = handle;
131     const char * pszEndpoint = NULL;
132     RPC_STATUS status;
133     RpcBinding* epm_bind;
134     unsigned int i;
135
136     if (bind->server)
137         return RPC_S_INVALID_BINDING;
138
139     for (i = 0; i < sizeof(epm_endpoints)/sizeof(epm_endpoints[0]); i++)
140         if (!strcmp(bind->Protseq, epm_endpoints[i].protseq))
141             pszEndpoint = epm_endpoints[i].endpoint;
142
143     if (!pszEndpoint)
144     {
145         FIXME("no endpoint for the endpoint-mapper found for protseq %s\n", debugstr_a(bind->Protseq));
146         return RPC_S_PROTSEQ_NOT_SUPPORTED;
147     }
148
149     status = RpcBindingCopy(handle, epm_handle);
150     if (status != RPC_S_OK) return status;
151
152     epm_bind = *epm_handle;
153     if (epm_bind->AuthInfo)
154     {
155         /* don't bother with authenticating against the EPM by default
156         * (see EnableAuthEpResolution registry value) */
157         RpcAuthInfo_Release(epm_bind->AuthInfo);
158         epm_bind->AuthInfo = NULL;
159     }
160     RPCRT4_ResolveBinding(epm_bind, pszEndpoint);
161     TRACE("RPC_S_OK\n");
162     return RPC_S_OK;
163 }
164
165 static RPC_STATUS get_epm_handle_server(RPC_BINDING_HANDLE *epm_handle)
166 {
167     unsigned char string_binding[] = "ncacn_np:.[\\\\pipe\\\\epmapper]";
168
169     return RpcBindingFromStringBindingA(string_binding, epm_handle);
170 }
171
172 static LONG WINAPI rpc_filter(EXCEPTION_POINTERS *__eptr)
173 {
174     switch (GetExceptionCode())
175     {
176         case EXCEPTION_ACCESS_VIOLATION:
177         case EXCEPTION_ILLEGAL_INSTRUCTION:
178             return EXCEPTION_CONTINUE_SEARCH;
179         default:
180             return EXCEPTION_EXECUTE_HANDLER;
181     }
182 }
183
184 /***********************************************************************
185  *             RpcEpRegisterA (RPCRT4.@)
186  */
187 RPC_STATUS WINAPI RpcEpRegisterA( RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR *BindingVector,
188                                   UUID_VECTOR *UuidVector, RPC_CSTR Annotation )
189 {
190   PRPC_SERVER_INTERFACE If = IfSpec;
191   ULONG i;
192   RPC_STATUS status = RPC_S_OK;
193   error_status_t status2;
194   ept_entry_t *entries;
195   handle_t handle;
196
197   TRACE("(%p,%p,%p,%s)\n", IfSpec, BindingVector, UuidVector, debugstr_a((char*)Annotation));
198   TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID));
199   for (i=0; i<BindingVector->Count; i++) {
200     RpcBinding* bind = BindingVector->BindingH[i];
201     TRACE(" protseq[%d]=%s\n", i, debugstr_a(bind->Protseq));
202     TRACE(" endpoint[%d]=%s\n", i, debugstr_a(bind->Endpoint));
203   }
204   if (UuidVector) {
205     for (i=0; i<UuidVector->Count; i++)
206       TRACE(" obj[%d]=%s\n", i, debugstr_guid(UuidVector->Uuid[i]));
207   }
208
209   if (!BindingVector->Count) return RPC_S_OK;
210
211   entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entries) * BindingVector->Count * (UuidVector ? UuidVector->Count : 1));
212   if (!entries)
213       return RPC_S_OUT_OF_MEMORY;
214
215   status = get_epm_handle_server(&handle);
216   if (status != RPC_S_OK)
217   {
218     HeapFree(GetProcessHeap(), 0, entries);
219     return status;
220   }
221
222   for (i = 0; i < BindingVector->Count; i++)
223   {
224       unsigned j;
225       RpcBinding* bind = BindingVector->BindingH[i];
226       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
227       {
228           status = TowerConstruct(&If->InterfaceId, &If->TransferSyntax,
229                                   bind->Protseq, bind->Endpoint,
230                                   bind->NetworkAddr,
231                                   &entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
232           if (status != RPC_S_OK) break;
233
234           if (UuidVector)
235               memcpy(&entries[i * UuidVector->Count].object, &UuidVector->Uuid[j], sizeof(GUID));
236           else
237               memset(&entries[i].object, 0, sizeof(entries[i].object));
238           if (Annotation)
239               memcpy(entries[i].annotation, Annotation,
240                      min(strlen((char *)Annotation) + 1, ept_max_annotation_size));
241       }
242   }
243
244   if (status == RPC_S_OK)
245   {
246       while (TRUE)
247       {
248           __TRY
249           {
250               ept_insert(handle, BindingVector->Count * (UuidVector ? UuidVector->Count : 1),
251                          entries, TRUE, &status2);
252           }
253           __EXCEPT(rpc_filter)
254           {
255               status2 = GetExceptionCode();
256           }
257           __ENDTRY
258           if (status2 == RPC_S_SERVER_UNAVAILABLE &&
259               is_epm_destination_local(handle))
260           {
261               if (start_rpcss())
262                   continue;
263           }
264           if (status2 != RPC_S_OK)
265               ERR("ept_insert failed with error %d\n", status2);
266           status = status2; /* FIXME: convert status? */
267           break;
268       }
269   }
270   RpcBindingFree(&handle);
271
272   for (i = 0; i < BindingVector->Count; i++)
273   {
274       unsigned j;
275       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
276           I_RpcFree(entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
277   }
278
279   HeapFree(GetProcessHeap(), 0, entries);
280
281   return status;
282 }
283
284 /***********************************************************************
285  *             RpcEpRegisterW (RPCRT4.@)
286  */
287 RPC_STATUS WINAPI RpcEpRegisterW( RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR *BindingVector,
288                                   UUID_VECTOR *UuidVector, RPC_WSTR Annotation )
289 {
290   LPSTR annA = RPCRT4_strdupWtoA(Annotation);
291   RPC_STATUS status;
292
293   status = RpcEpRegisterA(IfSpec, BindingVector, UuidVector, (RPC_CSTR)annA);
294
295   HeapFree(GetProcessHeap(), 0, annA);
296   return status;
297 }
298
299 /***********************************************************************
300  *             RpcEpUnregister (RPCRT4.@)
301  */
302 RPC_STATUS WINAPI RpcEpUnregister( RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR *BindingVector,
303                                    UUID_VECTOR *UuidVector )
304 {
305   PRPC_SERVER_INTERFACE If = IfSpec;
306   ULONG i;
307   RPC_STATUS status = RPC_S_OK;
308   error_status_t status2;
309   ept_entry_t *entries;
310   handle_t handle;
311
312   TRACE("(%p,%p,%p)\n", IfSpec, BindingVector, UuidVector);
313   TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID));
314   for (i=0; i<BindingVector->Count; i++) {
315     RpcBinding* bind = BindingVector->BindingH[i];
316     TRACE(" protseq[%d]=%s\n", i, debugstr_a(bind->Protseq));
317     TRACE(" endpoint[%d]=%s\n", i, debugstr_a(bind->Endpoint));
318   }
319   if (UuidVector) {
320     for (i=0; i<UuidVector->Count; i++)
321       TRACE(" obj[%d]=%s\n", i, debugstr_guid(UuidVector->Uuid[i]));
322   }
323
324   entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entries) * BindingVector->Count * (UuidVector ? UuidVector->Count : 1));
325   if (!entries)
326       return RPC_S_OUT_OF_MEMORY;
327
328   status = get_epm_handle_server(&handle);
329   if (status != RPC_S_OK)
330   {
331     HeapFree(GetProcessHeap(), 0, entries);
332     return status;
333   }
334
335   for (i = 0; i < BindingVector->Count; i++)
336   {
337       unsigned j;
338       RpcBinding* bind = BindingVector->BindingH[i];
339       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
340       {
341           status = TowerConstruct(&If->InterfaceId, &If->TransferSyntax,
342                                   bind->Protseq, bind->Endpoint,
343                                   bind->NetworkAddr,
344                                   &entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
345           if (status != RPC_S_OK) break;
346
347           if (UuidVector)
348               memcpy(&entries[i * UuidVector->Count + j].object, &UuidVector->Uuid[j], sizeof(GUID));
349           else
350               memset(&entries[i].object, 0, sizeof(entries[i].object));
351       }
352   }
353
354   if (status == RPC_S_OK)
355   {
356       __TRY
357       {
358           ept_insert(handle, BindingVector->Count * (UuidVector ? UuidVector->Count : 1),
359                      entries, TRUE, &status2);
360       }
361       __EXCEPT(rpc_filter)
362       {
363           status2 = GetExceptionCode();
364       }
365       __ENDTRY
366       if (status2 == RPC_S_SERVER_UNAVAILABLE)
367           status2 = EPT_S_NOT_REGISTERED;
368       if (status2 != RPC_S_OK)
369           ERR("ept_insert failed with error %d\n", status2);
370       status = status2; /* FIXME: convert status? */
371   }
372   RpcBindingFree(&handle);
373
374   for (i = 0; i < BindingVector->Count; i++)
375   {
376       unsigned j;
377       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
378           I_RpcFree(entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
379   }
380
381   HeapFree(GetProcessHeap(), 0, entries);
382
383   return status;
384 }
385
386 /***********************************************************************
387  *             RpcEpResolveBinding (RPCRT4.@)
388  */
389 RPC_STATUS WINAPI RpcEpResolveBinding( RPC_BINDING_HANDLE Binding, RPC_IF_HANDLE IfSpec )
390 {
391   PRPC_CLIENT_INTERFACE If = IfSpec;
392   RpcBinding* bind = Binding;
393   RPC_STATUS status;
394   error_status_t status2;
395   handle_t handle;
396   ept_lookup_handle_t entry_handle = NULL;
397   twr_t *tower;
398   twr_t *towers[4] = { NULL };
399   unsigned32 num_towers, i;
400   GUID uuid = GUID_NULL;
401   char *resolved_endpoint = NULL;
402
403   TRACE("(%p,%p)\n", Binding, IfSpec);
404   TRACE(" protseq=%s\n", debugstr_a(bind->Protseq));
405   TRACE(" obj=%s\n", debugstr_guid(&bind->ObjectUuid));
406   TRACE(" networkaddr=%s\n", debugstr_a(bind->NetworkAddr));
407   TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID));
408
409   /* just return for fully bound handles */
410   if (bind->Endpoint && (bind->Endpoint[0] != '\0'))
411     return RPC_S_OK;
412
413   status = get_epm_handle_client(Binding, &handle);
414   if (status != RPC_S_OK) return status;
415   
416   status = TowerConstruct(&If->InterfaceId, &If->TransferSyntax, bind->Protseq,
417                           ((RpcBinding *)handle)->Endpoint,
418                           bind->NetworkAddr, &tower);
419   if (status != RPC_S_OK)
420   {
421       WARN("couldn't get tower\n");
422       RpcBindingFree(&handle);
423       return status;
424   }
425
426   while (TRUE)
427   {
428     __TRY
429     {
430       ept_map(handle, &uuid, tower, &entry_handle, sizeof(towers)/sizeof(towers[0]), &num_towers, towers, &status2);
431       /* FIXME: translate status2? */
432     }
433     __EXCEPT(rpc_filter)
434     {
435       status2 = GetExceptionCode();
436     }
437     __ENDTRY
438     if (status2 == RPC_S_SERVER_UNAVAILABLE &&
439         is_epm_destination_local(handle))
440     {
441       if (start_rpcss())
442         continue;
443     }
444     break;
445   };
446
447   RpcBindingFree(&handle);
448   I_RpcFree(tower);
449
450   if (status2 != RPC_S_OK)
451   {
452     ERR("ept_map failed for ifid %s, protseq %s, networkaddr %s\n", debugstr_guid(&If->TransferSyntax.SyntaxGUID), bind->Protseq, bind->NetworkAddr);
453     return status2;
454   }
455
456   for (i = 0; i < num_towers; i++)
457   {
458     /* only parse the tower if we haven't already found a suitable
459     * endpoint, otherwise just free the tower */
460     if (!resolved_endpoint)
461     {
462       status = TowerExplode(towers[i], NULL, NULL, NULL, &resolved_endpoint, NULL);
463       TRACE("status = %d\n", status);
464     }
465     I_RpcFree(towers[i]);
466   }
467
468   if (resolved_endpoint)
469   {
470     RPCRT4_ResolveBinding(Binding, resolved_endpoint);
471     I_RpcFree(resolved_endpoint);
472     return RPC_S_OK;
473   }
474
475   WARN("couldn't find an endpoint\n");
476   return EPT_S_NOT_REGISTERED;
477 }
478
479 /*****************************************************************************
480  * TowerExplode (RPCRT4.@)
481  */
482 RPC_STATUS WINAPI TowerExplode(
483     const twr_t *tower, PRPC_SYNTAX_IDENTIFIER object, PRPC_SYNTAX_IDENTIFIER syntax,
484     char **protseq, char **endpoint, char **address)
485 {
486     size_t tower_size;
487     RPC_STATUS status;
488     const unsigned char *p;
489     u_int16 floor_count;
490     const twr_uuid_floor_t *object_floor;
491     const twr_uuid_floor_t *syntax_floor;
492
493     TRACE("(%p, %p, %p, %p, %p, %p)\n", tower, object, syntax, protseq,
494           endpoint, address);
495
496     if (protseq)
497         *protseq = NULL;
498     if (endpoint)
499         *endpoint = NULL;
500     if (address)
501         *address = NULL;
502
503     tower_size = tower->tower_length;
504
505     if (tower_size < sizeof(u_int16))
506         return EPT_S_NOT_REGISTERED;
507
508     p = &tower->tower_octet_string[0];
509
510     floor_count = *(const u_int16 *)p;
511     p += sizeof(u_int16);
512     tower_size -= sizeof(u_int16);
513     TRACE("floor_count: %d\n", floor_count);
514     /* FIXME: should we do something with the floor count? at the moment we don't */
515
516     if (tower_size < sizeof(*object_floor) + sizeof(*syntax_floor))
517         return EPT_S_NOT_REGISTERED;
518
519     object_floor = (const twr_uuid_floor_t *)p;
520     p += sizeof(*object_floor);
521     tower_size -= sizeof(*object_floor);
522     syntax_floor = (const twr_uuid_floor_t *)p;
523     p += sizeof(*syntax_floor);
524     tower_size -= sizeof(*syntax_floor);
525
526     if ((object_floor->count_lhs != sizeof(object_floor->protid) +
527         sizeof(object_floor->uuid) + sizeof(object_floor->major_version)) ||
528         (object_floor->protid != EPM_PROTOCOL_UUID) ||
529         (object_floor->count_rhs != sizeof(object_floor->minor_version)))
530         return EPT_S_NOT_REGISTERED;
531
532     if ((syntax_floor->count_lhs != sizeof(syntax_floor->protid) +
533         sizeof(syntax_floor->uuid) + sizeof(syntax_floor->major_version)) ||
534         (syntax_floor->protid != EPM_PROTOCOL_UUID) ||
535         (syntax_floor->count_rhs != sizeof(syntax_floor->minor_version)))
536         return EPT_S_NOT_REGISTERED;
537
538     status = RpcTransport_ParseTopOfTower(p, tower_size, protseq, address, endpoint);
539     if ((status == RPC_S_OK) && syntax && object)
540     {
541         syntax->SyntaxGUID = syntax_floor->uuid;
542         syntax->SyntaxVersion.MajorVersion = syntax_floor->major_version;
543         syntax->SyntaxVersion.MinorVersion = syntax_floor->minor_version;
544         object->SyntaxGUID = object_floor->uuid;
545         object->SyntaxVersion.MajorVersion = object_floor->major_version;
546         object->SyntaxVersion.MinorVersion = object_floor->minor_version;
547     }
548     return status;
549 }
550
551 /***********************************************************************
552  *             TowerConstruct (RPCRT4.@)
553  */
554 RPC_STATUS WINAPI TowerConstruct(
555     const RPC_SYNTAX_IDENTIFIER *object, const RPC_SYNTAX_IDENTIFIER *syntax,
556     const char *protseq, const char *endpoint, const char *address,
557     twr_t **tower)
558 {
559     size_t tower_size;
560     RPC_STATUS status;
561     unsigned char *p;
562     twr_uuid_floor_t *object_floor;
563     twr_uuid_floor_t *syntax_floor;
564
565     TRACE("(%p, %p, %s, %s, %s, %p)\n", object, syntax, debugstr_a(protseq),
566           debugstr_a(endpoint), debugstr_a(address), tower);
567
568     *tower = NULL;
569
570     status = RpcTransport_GetTopOfTower(NULL, &tower_size, protseq, address, endpoint);
571
572     if (status != RPC_S_OK)
573         return status;
574
575     tower_size += sizeof(u_int16) + sizeof(*object_floor) + sizeof(*syntax_floor);
576     *tower = I_RpcAllocate(FIELD_OFFSET(twr_t, tower_octet_string[tower_size]));
577     if (!*tower)
578         return RPC_S_OUT_OF_RESOURCES;
579
580     (*tower)->tower_length = tower_size;
581     p = &(*tower)->tower_octet_string[0];
582     *(u_int16 *)p = 5; /* number of floors */
583     p += sizeof(u_int16);
584     object_floor = (twr_uuid_floor_t *)p;
585     p += sizeof(*object_floor);
586     syntax_floor = (twr_uuid_floor_t *)p;
587     p += sizeof(*syntax_floor);
588
589     object_floor->count_lhs = sizeof(object_floor->protid) + sizeof(object_floor->uuid) +
590                               sizeof(object_floor->major_version);
591     object_floor->protid = EPM_PROTOCOL_UUID;
592     object_floor->count_rhs = sizeof(object_floor->minor_version);
593     object_floor->uuid = object->SyntaxGUID;
594     object_floor->major_version = object->SyntaxVersion.MajorVersion;
595     object_floor->minor_version = object->SyntaxVersion.MinorVersion;
596
597     syntax_floor->count_lhs = sizeof(syntax_floor->protid) + sizeof(syntax_floor->uuid) +
598                               sizeof(syntax_floor->major_version);
599     syntax_floor->protid = EPM_PROTOCOL_UUID;
600     syntax_floor->count_rhs = sizeof(syntax_floor->minor_version);
601     syntax_floor->uuid = syntax->SyntaxGUID;
602     syntax_floor->major_version = syntax->SyntaxVersion.MajorVersion;
603     syntax_floor->minor_version = syntax->SyntaxVersion.MinorVersion;
604
605     status = RpcTransport_GetTopOfTower(p, &tower_size, protseq, address, endpoint);
606     if (status != RPC_S_OK)
607     {
608         I_RpcFree(*tower);
609         *tower = NULL;
610         return status;
611     }
612     return RPC_S_OK;
613 }
614
615 void __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len)
616 {
617     return HeapAlloc(GetProcessHeap(), 0, len);
618 }
619
620 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
621 {
622     HeapFree(GetProcessHeap(), 0, ptr);
623 }