rpcrt4: Factorise conformant string functions into array_* functions.
[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     { "ncalprc", "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 RPC_STATUS get_epm_handle_client(RPC_BINDING_HANDLE handle, RPC_BINDING_HANDLE *epm_handle)
118 {
119     RpcBinding *bind = (RpcBinding *)handle;
120     const char * pszEndpoint = NULL;
121     RPC_STATUS status;
122     RpcBinding* epm_bind;
123     unsigned int i;
124
125     if (bind->server)
126         return RPC_S_INVALID_BINDING;
127
128     for (i = 0; i < sizeof(epm_endpoints)/sizeof(epm_endpoints[0]); i++)
129         if (!strcmp(bind->Protseq, epm_endpoints[i].protseq))
130             pszEndpoint = epm_endpoints[i].endpoint;
131
132     if (!pszEndpoint)
133     {
134         FIXME("no endpoint for the endpoint-mapper found for protseq %s\n", debugstr_a(bind->Protseq));
135         return RPC_S_PROTSEQ_NOT_SUPPORTED;
136     }
137
138     status = RpcBindingCopy(handle, epm_handle);
139     if (status != RPC_S_OK) return status;
140
141     epm_bind = (RpcBinding*)*epm_handle;
142     if (epm_bind->AuthInfo)
143     {
144         /* don't bother with authenticating against the EPM by default
145         * (see EnableAuthEpResolution registry value) */
146         RpcAuthInfo_Release(epm_bind->AuthInfo);
147         epm_bind->AuthInfo = NULL;
148     }
149     RPCRT4_ResolveBinding(epm_bind, pszEndpoint);
150     TRACE("RPC_S_OK\n");
151     return RPC_S_OK;
152 }
153
154 static RPC_STATUS get_epm_handle_server(RPC_BINDING_HANDLE *epm_handle)
155 {
156     unsigned char string_binding[] = "ncacn_np:.[\\pipe\\epmapper]";
157
158     return RpcBindingFromStringBindingA(string_binding, epm_handle);
159 }
160
161 static LONG WINAPI rpc_filter(EXCEPTION_POINTERS *__eptr)
162 {
163     switch (GetExceptionCode())
164     {
165         case EXCEPTION_ACCESS_VIOLATION:
166         case EXCEPTION_ILLEGAL_INSTRUCTION:
167             return EXCEPTION_CONTINUE_SEARCH;
168         default:
169             return EXCEPTION_EXECUTE_HANDLER;
170     }
171 }
172
173 /***********************************************************************
174  *             RpcEpRegisterA (RPCRT4.@)
175  */
176 RPC_STATUS WINAPI RpcEpRegisterA( RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR *BindingVector,
177                                   UUID_VECTOR *UuidVector, RPC_CSTR Annotation )
178 {
179   PRPC_SERVER_INTERFACE If = (PRPC_SERVER_INTERFACE)IfSpec;
180   unsigned long i;
181   RPC_STATUS status = RPC_S_OK;
182   error_status_t status2;
183   ept_entry_t *entries;
184   handle_t handle;
185
186   TRACE("(%p,%p,%p,%s)\n", IfSpec, BindingVector, UuidVector, debugstr_a((char*)Annotation));
187   TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID));
188   for (i=0; i<BindingVector->Count; i++) {
189     RpcBinding* bind = (RpcBinding*)(BindingVector->BindingH[i]);
190     TRACE(" protseq[%ld]=%s\n", i, debugstr_a(bind->Protseq));
191     TRACE(" endpoint[%ld]=%s\n", i, debugstr_a(bind->Endpoint));
192   }
193   if (UuidVector) {
194     for (i=0; i<UuidVector->Count; i++)
195       TRACE(" obj[%ld]=%s\n", i, debugstr_guid(UuidVector->Uuid[i]));
196   }
197
198   if (!BindingVector->Count) return RPC_S_OK;
199
200   entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entries) * BindingVector->Count * (UuidVector ? UuidVector->Count : 1));
201   if (!entries)
202       return RPC_S_OUT_OF_MEMORY;
203
204   status = get_epm_handle_server(&handle);
205   if (status != RPC_S_OK)
206   {
207     HeapFree(GetProcessHeap(), 0, entries);
208     return status;
209   }
210
211   for (i = 0; i < BindingVector->Count; i++)
212   {
213       unsigned j;
214       RpcBinding* bind = (RpcBinding*)(BindingVector->BindingH[i]);
215       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
216       {
217           int len = strlen((char *)Annotation);
218           status = TowerConstruct(&If->InterfaceId, &If->TransferSyntax,
219                                   bind->Protseq, bind->Endpoint,
220                                   bind->NetworkAddr,
221                                   &entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
222           if (status != RPC_S_OK) break;
223
224           if (UuidVector)
225               memcpy(&entries[i * UuidVector->Count].object, &UuidVector->Uuid[j], sizeof(GUID));
226           else
227               memset(&entries[i].object, 0, sizeof(entries[i].object));
228           memcpy(entries[i].annotation, Annotation, min(len + 1, ept_max_annotation_size));
229       }
230   }
231
232   if (status == RPC_S_OK)
233   {
234       while (TRUE)
235       {
236           __TRY
237           {
238               ept_insert(handle, BindingVector->Count * (UuidVector ? UuidVector->Count : 1),
239                          entries, TRUE, &status2);
240           }
241           __EXCEPT(rpc_filter)
242           {
243               status2 = GetExceptionCode();
244           }
245           __ENDTRY
246           if (status2 == RPC_S_SERVER_UNAVAILABLE)
247           {
248               if (start_rpcss())
249                   continue;
250           }
251           if (status2 != RPC_S_OK)
252               ERR("ept_insert failed with error %d\n", status2);
253           status = status2; /* FIXME: convert status? */
254           break;
255       }
256   }
257   RpcBindingFree(&handle);
258
259   for (i = 0; i < BindingVector->Count; i++)
260   {
261       unsigned j;
262       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
263           I_RpcFree(entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
264   }
265
266   HeapFree(GetProcessHeap(), 0, entries);
267
268   return status;
269 }
270
271 /***********************************************************************
272  *             RpcEpUnregister (RPCRT4.@)
273  */
274 RPC_STATUS WINAPI RpcEpUnregister( RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR *BindingVector,
275                                    UUID_VECTOR *UuidVector )
276 {
277   PRPC_SERVER_INTERFACE If = (PRPC_SERVER_INTERFACE)IfSpec;
278   unsigned long i;
279   RPC_STATUS status = RPC_S_OK;
280   error_status_t status2;
281   ept_entry_t *entries;
282   handle_t handle;
283
284   TRACE("(%p,%p,%p)\n", IfSpec, BindingVector, UuidVector);
285   TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID));
286   for (i=0; i<BindingVector->Count; i++) {
287     RpcBinding* bind = (RpcBinding*)(BindingVector->BindingH[i]);
288     TRACE(" protseq[%ld]=%s\n", i, debugstr_a(bind->Protseq));
289     TRACE(" endpoint[%ld]=%s\n", i, debugstr_a(bind->Endpoint));
290   }
291   if (UuidVector) {
292     for (i=0; i<UuidVector->Count; i++)
293       TRACE(" obj[%ld]=%s\n", i, debugstr_guid(UuidVector->Uuid[i]));
294   }
295
296   entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entries) * BindingVector->Count * (UuidVector ? UuidVector->Count : 1));
297   if (!entries)
298       return RPC_S_OUT_OF_MEMORY;
299
300   status = get_epm_handle_server(&handle);
301   if (status != RPC_S_OK)
302   {
303     HeapFree(GetProcessHeap(), 0, entries);
304     return status;
305   }
306
307   for (i = 0; i < BindingVector->Count; i++)
308   {
309       unsigned j;
310       RpcBinding* bind = (RpcBinding*)(BindingVector->BindingH[i]);
311       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
312       {
313           status = TowerConstruct(&If->InterfaceId, &If->TransferSyntax,
314                                   bind->Protseq, bind->Endpoint,
315                                   bind->NetworkAddr,
316                                   &entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
317           if (status != RPC_S_OK) break;
318
319           if (UuidVector)
320               memcpy(&entries[i * UuidVector->Count + j].object, &UuidVector->Uuid[j], sizeof(GUID));
321           else
322               memset(&entries[i].object, 0, sizeof(entries[i].object));
323       }
324   }
325
326   if (status == RPC_S_OK)
327   {
328       __TRY
329       {
330           ept_insert(handle, BindingVector->Count * (UuidVector ? UuidVector->Count : 1),
331                      entries, TRUE, &status2);
332       }
333       __EXCEPT(rpc_filter)
334       {
335           status2 = GetExceptionCode();
336       }
337       __ENDTRY
338       if (status2 == RPC_S_SERVER_UNAVAILABLE)
339           status2 = EPT_S_NOT_REGISTERED;
340       if (status2 != RPC_S_OK)
341           ERR("ept_insert failed with error %d\n", status2);
342       status = status2; /* FIXME: convert status? */
343   }
344   RpcBindingFree(&handle);
345
346   for (i = 0; i < BindingVector->Count; i++)
347   {
348       unsigned j;
349       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
350           I_RpcFree(entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
351   }
352
353   HeapFree(GetProcessHeap(), 0, entries);
354
355   return status;
356 }
357
358 /***********************************************************************
359  *             RpcEpResolveBinding (RPCRT4.@)
360  */
361 RPC_STATUS WINAPI RpcEpResolveBinding( RPC_BINDING_HANDLE Binding, RPC_IF_HANDLE IfSpec )
362 {
363   PRPC_CLIENT_INTERFACE If = (PRPC_CLIENT_INTERFACE)IfSpec;
364   RpcBinding* bind = (RpcBinding*)Binding;
365   RPC_STATUS status;
366   error_status_t status2;
367   handle_t handle;
368   ept_lookup_handle_t entry_handle = NULL;
369   twr_t *tower;
370   twr_t *towers[4] = { NULL };
371   unsigned32 num_towers, i;
372   GUID uuid = GUID_NULL;
373   char *resolved_endpoint = NULL;
374
375   TRACE("(%p,%p)\n", Binding, IfSpec);
376   TRACE(" protseq=%s\n", debugstr_a(bind->Protseq));
377   TRACE(" obj=%s\n", debugstr_guid(&bind->ObjectUuid));
378   TRACE(" networkaddr=%s\n", debugstr_a(bind->NetworkAddr));
379   TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID));
380
381   /* just return for fully bound handles */
382   if (bind->Endpoint && (bind->Endpoint[0] != '\0'))
383     return RPC_S_OK;
384
385   status = get_epm_handle_client(Binding, &handle);
386   if (status != RPC_S_OK) return status;
387   
388   status = TowerConstruct(&If->InterfaceId, &If->TransferSyntax, bind->Protseq,
389                           ((RpcBinding *)handle)->Endpoint,
390                           bind->NetworkAddr, &tower);
391   if (status != RPC_S_OK)
392   {
393       WARN("couldn't get tower\n");
394       RpcBindingFree(&handle);
395       return status;
396   }
397
398   while (TRUE)
399   {
400     __TRY
401     {
402       ept_map(handle, &uuid, tower, &entry_handle, sizeof(towers)/sizeof(towers[0]), &num_towers, towers, &status2);
403       /* FIXME: translate status2? */
404     }
405     __EXCEPT(rpc_filter)
406     {
407       status2 = GetExceptionCode();
408     }
409     __ENDTRY
410     if (status2 == RPC_S_SERVER_UNAVAILABLE)
411     {
412       if (start_rpcss())
413         continue;
414     }
415     break;
416   };
417
418   RpcBindingFree(&handle);
419   I_RpcFree(tower);
420
421   if (status2 != RPC_S_OK)
422   {
423     ERR("ept_map failed for ifid %s, protseq %s, networkaddr %s\n", debugstr_guid(&If->TransferSyntax.SyntaxGUID), bind->Protseq, bind->NetworkAddr);
424     return status2;
425   }
426
427   for (i = 0; i < num_towers; i++)
428   {
429     /* only parse the tower if we haven't already found a suitable
430     * endpoint, otherwise just free the tower */
431     if (!resolved_endpoint)
432     {
433       status = TowerExplode(towers[i], NULL, NULL, NULL, &resolved_endpoint, NULL);
434       TRACE("status = %ld\n", status);
435     }
436     I_RpcFree(towers[i]);
437   }
438
439   if (resolved_endpoint)
440   {
441     RPCRT4_ResolveBinding(Binding, resolved_endpoint);
442     I_RpcFree(resolved_endpoint);
443     return RPC_S_OK;
444   }
445
446   WARN("couldn't find an endpoint\n");
447   return EPT_S_NOT_REGISTERED;
448 }
449
450 /*****************************************************************************
451  * TowerExplode (RPCRT4.@)
452  */
453 RPC_STATUS WINAPI TowerExplode(
454     const twr_t *tower, PRPC_SYNTAX_IDENTIFIER object, PRPC_SYNTAX_IDENTIFIER syntax,
455     char **protseq, char **endpoint, char **address)
456 {
457     size_t tower_size;
458     RPC_STATUS status;
459     const unsigned char *p;
460     u_int16 floor_count;
461     const twr_uuid_floor_t *object_floor;
462     const twr_uuid_floor_t *syntax_floor;
463
464     TRACE("(%p, %p, %p, %p, %p, %p)\n", tower, object, syntax, protseq,
465           endpoint, address);
466
467     if (protseq)
468         *protseq = NULL;
469     if (endpoint)
470         *endpoint = NULL;
471     if (address)
472         *address = NULL;
473
474     tower_size = tower->tower_length;
475
476     if (tower_size < sizeof(u_int16))
477         return EPT_S_NOT_REGISTERED;
478
479     p = &tower->tower_octet_string[0];
480
481     floor_count = *(const u_int16 *)p;
482     p += sizeof(u_int16);
483     tower_size -= sizeof(u_int16);
484     TRACE("floor_count: %d\n", floor_count);
485     /* FIXME: should we do something with the floor count? at the moment we don't */
486
487     if (tower_size < sizeof(*object_floor) + sizeof(*syntax_floor))
488         return EPT_S_NOT_REGISTERED;
489
490     object_floor = (const twr_uuid_floor_t *)p;
491     p += sizeof(*object_floor);
492     tower_size -= sizeof(*object_floor);
493     syntax_floor = (const twr_uuid_floor_t *)p;
494     p += sizeof(*syntax_floor);
495     tower_size -= sizeof(*syntax_floor);
496
497     if ((object_floor->count_lhs != sizeof(object_floor->protid) +
498         sizeof(object_floor->uuid) + sizeof(object_floor->major_version)) ||
499         (object_floor->protid != EPM_PROTOCOL_UUID) ||
500         (object_floor->count_rhs != sizeof(object_floor->minor_version)))
501         return EPT_S_NOT_REGISTERED;
502
503     if ((syntax_floor->count_lhs != sizeof(syntax_floor->protid) +
504         sizeof(syntax_floor->uuid) + sizeof(syntax_floor->major_version)) ||
505         (syntax_floor->protid != EPM_PROTOCOL_UUID) ||
506         (syntax_floor->count_rhs != sizeof(syntax_floor->minor_version)))
507         return EPT_S_NOT_REGISTERED;
508
509     status = RpcTransport_ParseTopOfTower(p, tower_size, protseq, address, endpoint);
510     if ((status == RPC_S_OK) && syntax && object)
511     {
512         syntax->SyntaxGUID = syntax_floor->uuid;
513         syntax->SyntaxVersion.MajorVersion = syntax_floor->major_version;
514         syntax->SyntaxVersion.MinorVersion = syntax_floor->minor_version;
515         object->SyntaxGUID = object_floor->uuid;
516         object->SyntaxVersion.MajorVersion = object_floor->major_version;
517         object->SyntaxVersion.MinorVersion = object_floor->minor_version;
518     }
519     return status;
520 }
521
522 /***********************************************************************
523  *             TowerConstruct (RPCRT4.@)
524  */
525 RPC_STATUS WINAPI TowerConstruct(
526     const RPC_SYNTAX_IDENTIFIER *object, const RPC_SYNTAX_IDENTIFIER *syntax,
527     const char *protseq, const char *endpoint, const char *address,
528     twr_t **tower)
529 {
530     size_t tower_size;
531     RPC_STATUS status;
532     unsigned char *p;
533     twr_uuid_floor_t *object_floor;
534     twr_uuid_floor_t *syntax_floor;
535
536     TRACE("(%p, %p, %s, %s, %s, %p)\n", object, syntax, debugstr_a(protseq),
537           debugstr_a(endpoint), debugstr_a(address), tower);
538
539     *tower = NULL;
540
541     status = RpcTransport_GetTopOfTower(NULL, &tower_size, protseq, address, endpoint);
542
543     if (status != RPC_S_OK)
544         return status;
545
546     tower_size += sizeof(u_int16) + sizeof(*object_floor) + sizeof(*syntax_floor);
547     *tower = I_RpcAllocate(FIELD_OFFSET(twr_t, tower_octet_string[tower_size]));
548     if (!*tower)
549         return RPC_S_OUT_OF_RESOURCES;
550
551     (*tower)->tower_length = tower_size;
552     p = &(*tower)->tower_octet_string[0];
553     *(u_int16 *)p = 5; /* number of floors */
554     p += sizeof(u_int16);
555     object_floor = (twr_uuid_floor_t *)p;
556     p += sizeof(*object_floor);
557     syntax_floor = (twr_uuid_floor_t *)p;
558     p += sizeof(*syntax_floor);
559
560     object_floor->count_lhs = sizeof(object_floor->protid) + sizeof(object_floor->uuid) +
561                               sizeof(object_floor->major_version);
562     object_floor->protid = EPM_PROTOCOL_UUID;
563     object_floor->count_rhs = sizeof(object_floor->minor_version);
564     object_floor->uuid = object->SyntaxGUID;
565     object_floor->major_version = object->SyntaxVersion.MajorVersion;
566     object_floor->minor_version = object->SyntaxVersion.MinorVersion;
567
568     syntax_floor->count_lhs = sizeof(syntax_floor->protid) + sizeof(syntax_floor->uuid) +
569                               sizeof(syntax_floor->major_version);
570     syntax_floor->protid = EPM_PROTOCOL_UUID;
571     syntax_floor->count_rhs = sizeof(syntax_floor->minor_version);
572     syntax_floor->uuid = syntax->SyntaxGUID;
573     syntax_floor->major_version = syntax->SyntaxVersion.MajorVersion;
574     syntax_floor->minor_version = syntax->SyntaxVersion.MinorVersion;
575
576     status = RpcTransport_GetTopOfTower(p, &tower_size, protseq, address, endpoint);
577     if (status != RPC_S_OK)
578     {
579         I_RpcFree(*tower);
580         *tower = NULL;
581         return status;
582     }
583     return RPC_S_OK;
584 }
585
586 void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t len)
587 {
588     return HeapAlloc(GetProcessHeap(), 0, len);
589 }
590
591 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
592 {
593     HeapFree(GetProcessHeap(), 0, ptr);
594 }