Wine also supports the ncacn_np protocol sequence.
[wine] / dlls / rpcrt4 / rpc_binding.c
1 /*
2  * RPC binding API
3  *
4  * Copyright 2001 Ove Kåven, TransGaming Technologies
5  * Copyright 2003 Mike Hearn
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * TODO:
22  *  - a whole lot
23  */
24
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <assert.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winnls.h"
33 #include "winerror.h"
34 #include "winreg.h"
35 #include "winternl.h"
36 #include "wine/unicode.h"
37
38 #include "rpc.h"
39
40 #include "wine/debug.h"
41
42 #include "rpc_binding.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(ole);
45
46 static RpcConnection* conn_cache;
47
48 static CRITICAL_SECTION conn_cache_cs;
49 static CRITICAL_SECTION_DEBUG critsect_debug =
50 {
51     0, 0, &conn_cache_cs,
52     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
53       0, 0, { 0, (DWORD)(__FILE__ ": conn_cache_cs") }
54 };
55 static CRITICAL_SECTION conn_cache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
56
57 LPSTR RPCRT4_strndupA(LPCSTR src, INT slen)
58 {
59   DWORD len;
60   LPSTR s;
61   if (!src) return NULL;
62   if (slen == -1) slen = strlen(src);
63   len = slen;
64   s = HeapAlloc(GetProcessHeap(), 0, len+1);
65   memcpy(s, src, len);
66   s[len] = 0;
67   return s;
68 }
69
70 LPSTR RPCRT4_strdupWtoA(LPWSTR src)
71 {
72   DWORD len;
73   LPSTR s;
74   if (!src) return NULL;
75   len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
76   s = HeapAlloc(GetProcessHeap(), 0, len);
77   WideCharToMultiByte(CP_ACP, 0, src, -1, s, len, NULL, NULL);
78   return s;
79 }
80
81 LPWSTR RPCRT4_strdupAtoW(LPSTR src)
82 {
83   DWORD len;
84   LPWSTR s;
85   if (!src) return NULL;
86   len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
87   s = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
88   MultiByteToWideChar(CP_ACP, 0, src, -1, s, len);
89   return s;
90 }
91
92 LPWSTR RPCRT4_strndupW(LPWSTR src, INT slen)
93 {
94   DWORD len;
95   LPWSTR s;
96   if (!src) return NULL;
97   if (slen == -1) slen = strlenW(src);
98   len = slen;
99   s = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
100   memcpy(s, src, len*sizeof(WCHAR));
101   s[len] = 0;
102   return s;
103 }
104
105 void RPCRT4_strfree(LPSTR src)
106 {
107   if (src) HeapFree(GetProcessHeap(), 0, src);
108 }
109
110 RPC_STATUS RPCRT4_CreateConnection(RpcConnection** Connection, BOOL server, LPSTR Protseq, LPSTR NetworkAddr, LPSTR Endpoint, LPSTR NetworkOptions, RpcBinding* Binding)
111 {
112   RpcConnection* NewConnection;
113
114   NewConnection = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RpcConnection));
115   NewConnection->server = server;
116   NewConnection->Protseq = RPCRT4_strdupA(Protseq);
117   NewConnection->NetworkAddr = RPCRT4_strdupA(NetworkAddr);
118   NewConnection->Endpoint = RPCRT4_strdupA(Endpoint);
119   NewConnection->Used = Binding;
120
121   EnterCriticalSection(&conn_cache_cs);
122   NewConnection->Next = conn_cache;
123   conn_cache = NewConnection;
124   LeaveCriticalSection(&conn_cache_cs);
125
126   TRACE("connection: %p\n", NewConnection);
127   *Connection = NewConnection;
128
129   return RPC_S_OK;
130 }
131
132 RPC_STATUS RPCRT4_DestroyConnection(RpcConnection* Connection)
133 {
134   RpcConnection* PrevConnection;
135
136   TRACE("connection: %p\n", Connection);
137   if (Connection->Used) ERR("connection is still in use\n");
138
139   EnterCriticalSection(&conn_cache_cs);
140   PrevConnection = conn_cache;
141   if (PrevConnection == Connection) {
142     conn_cache = Connection->Next;
143   } else {
144     while (PrevConnection && PrevConnection->Next != Connection)
145       PrevConnection = PrevConnection->Next;
146     if (PrevConnection) PrevConnection->Next = Connection->Next;
147   }
148   LeaveCriticalSection(&conn_cache_cs);
149
150   RPCRT4_CloseConnection(Connection);
151   RPCRT4_strfree(Connection->Endpoint);
152   RPCRT4_strfree(Connection->NetworkAddr);
153   RPCRT4_strfree(Connection->Protseq);
154   HeapFree(GetProcessHeap(), 0, Connection);
155   return RPC_S_OK;
156 }
157
158 RPC_STATUS RPCRT4_GetConnection(RpcConnection** Connection, BOOL server, LPSTR Protseq, LPSTR NetworkAddr, LPSTR Endpoint, LPSTR NetworkOptions, RpcBinding* Binding)
159 {
160   RpcConnection* NewConnection;
161
162   if (!server) {
163     EnterCriticalSection(&conn_cache_cs);
164     for (NewConnection = conn_cache; NewConnection; NewConnection = NewConnection->Next) {
165       if (NewConnection->Used) continue;
166       if (NewConnection->server != server) continue;
167       if (Protseq && strcmp(NewConnection->Protseq, Protseq)) continue;
168       if (NetworkAddr && strcmp(NewConnection->NetworkAddr, NetworkAddr)) continue;
169       if (Endpoint && strcmp(NewConnection->Endpoint, Endpoint)) continue;
170       /* this connection fits the bill */
171       NewConnection->Used = Binding;
172       break;
173     }
174     LeaveCriticalSection(&conn_cache_cs);
175     if (NewConnection) {
176       TRACE("cached connection: %p\n", NewConnection);
177       *Connection = NewConnection;
178       return RPC_S_OK;
179     }
180   }
181   return RPCRT4_CreateConnection(Connection, server, Protseq, NetworkAddr, Endpoint, NetworkOptions, Binding);
182 }
183
184 RPC_STATUS RPCRT4_ReleaseConnection(RpcConnection* Connection)
185 {
186   TRACE("connection: %p\n", Connection);
187   Connection->Used = NULL;
188   if (!Connection->server) {
189     /* cache the open connection for reuse later */
190     /* FIXME: we should probably clean the cache someday */
191     return RPC_S_OK;
192   }
193   return RPCRT4_DestroyConnection(Connection);
194 }
195
196 RPC_STATUS RPCRT4_OpenConnection(RpcConnection* Connection)
197 {
198   TRACE("(Connection == ^%p)\n", Connection);
199   if (!Connection->conn) {
200     if (Connection->server) { /* server */
201       /* protseq=ncalrpc: supposed to use NT LPC ports,
202        * but we'll implement it with named pipes for now */
203       if (strcmp(Connection->Protseq, "ncalrpc") == 0) {
204         static LPCSTR prefix = "\\\\.\\pipe\\lrpc\\";
205         LPSTR pname;
206         pname = HeapAlloc(GetProcessHeap(), 0, strlen(prefix) + strlen(Connection->Endpoint) + 1);
207         strcat(strcpy(pname, prefix), Connection->Endpoint);
208         TRACE("listening on %s\n", pname);
209         Connection->conn = CreateNamedPipeA(pname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
210                                          0, PIPE_UNLIMITED_INSTANCES, 0, 0, 5000, NULL);
211         HeapFree(GetProcessHeap(), 0, pname);
212         memset(&Connection->ovl, 0, sizeof(Connection->ovl));
213         Connection->ovl.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
214         if (!ConnectNamedPipe(Connection->conn, &Connection->ovl)) {
215           DWORD err = GetLastError();
216           if (err == ERROR_PIPE_CONNECTED) {
217             SetEvent(Connection->ovl.hEvent);
218             return RPC_S_OK;
219           }
220           return err;
221         }
222       }
223       /* protseq=ncacn_np: named pipes */
224       else if (strcmp(Connection->Protseq, "ncacn_np") == 0) {
225         static LPCSTR prefix = "\\\\.";
226         LPSTR pname;
227         pname = HeapAlloc(GetProcessHeap(), 0, strlen(prefix) + strlen(Connection->Endpoint) + 1);
228         strcat(strcpy(pname, prefix), Connection->Endpoint);
229         TRACE("listening on %s\n", pname);
230         Connection->conn = CreateNamedPipeA(pname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
231                                          0, PIPE_UNLIMITED_INSTANCES, 0, 0, 5000, NULL);
232         HeapFree(GetProcessHeap(), 0, pname);
233         memset(&Connection->ovl, 0, sizeof(Connection->ovl));
234         Connection->ovl.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
235         if (!ConnectNamedPipe(Connection->conn, &Connection->ovl)) {
236           DWORD err = GetLastError();
237           if (err == ERROR_PIPE_CONNECTED) {
238             SetEvent(Connection->ovl.hEvent);
239             return RPC_S_OK;
240           }
241           return err;
242         }
243       }
244       else {
245         ERR("protseq %s not supported\n", Connection->Protseq);
246         return RPC_S_PROTSEQ_NOT_SUPPORTED;
247       }
248     }
249     else { /* client */
250       /* protseq=ncalrpc: supposed to use NT LPC ports,
251        * but we'll implement it with named pipes for now */
252       if (strcmp(Connection->Protseq, "ncalrpc") == 0) {
253         static LPCSTR prefix = "\\\\.\\pipe\\lrpc\\";
254         LPSTR pname;
255         HANDLE conn;
256         DWORD err;
257
258         pname = HeapAlloc(GetProcessHeap(), 0, strlen(prefix) + strlen(Connection->Endpoint) + 1);
259         strcat(strcpy(pname, prefix), Connection->Endpoint);
260         TRACE("connecting to %s\n", pname);
261         while (TRUE) {
262           if (WaitNamedPipeA(pname, NMPWAIT_WAIT_FOREVER)) {
263             conn = CreateFileA(pname, GENERIC_READ|GENERIC_WRITE, 0, NULL,
264                                OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
265             if (conn != INVALID_HANDLE_VALUE) break;
266             err = GetLastError();
267             if (err == ERROR_PIPE_BUSY) continue;
268             TRACE("connection failed, error=%lx\n", err);
269             HeapFree(GetProcessHeap(), 0, pname);
270             return err;
271           } else {
272             err = GetLastError();
273             TRACE("connection failed, error=%lx\n", err);
274             HeapFree(GetProcessHeap(), 0, pname);
275             return err;
276           }
277         }
278
279         /* success */
280         HeapFree(GetProcessHeap(), 0, pname);
281         memset(&Connection->ovl, 0, sizeof(Connection->ovl));
282         Connection->ovl.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
283         Connection->conn = conn;
284       }
285       /* protseq=ncacn_np: named pipes */
286       else if (strcmp(Connection->Protseq, "ncacn_np") == 0) {
287         static LPCSTR prefix = "\\\\.";
288         LPSTR pname;
289         HANDLE conn;
290         DWORD err;
291
292         pname = HeapAlloc(GetProcessHeap(), 0, strlen(prefix) + strlen(Connection->Endpoint) + 1);
293         strcat(strcpy(pname, prefix), Connection->Endpoint);
294         TRACE("connecting to %s\n", pname);
295         conn = CreateFileA(pname, GENERIC_READ|GENERIC_WRITE, 0, NULL,
296                            OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
297         if (conn == INVALID_HANDLE_VALUE) {
298           err = GetLastError();
299           /* we don't need to handle ERROR_PIPE_BUSY here,
300            * the doc says that it is returned to the app */
301           TRACE("connection failed, error=%lx\n", err);
302           HeapFree(GetProcessHeap(), 0, pname);
303           return err;
304         }
305
306         /* success */
307         HeapFree(GetProcessHeap(), 0, pname);
308         memset(&Connection->ovl, 0, sizeof(Connection->ovl));
309         Connection->ovl.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
310         Connection->conn = conn;
311       } else {
312         ERR("protseq %s not supported\n", Connection->Protseq);
313         return RPC_S_PROTSEQ_NOT_SUPPORTED;
314       }
315     }
316   }
317   return RPC_S_OK;
318 }
319
320 RPC_STATUS RPCRT4_CloseConnection(RpcConnection* Connection)
321 {
322   TRACE("(Connection == ^%p)\n", Connection);
323   if (Connection->conn) {
324     CancelIo(Connection->conn);
325     CloseHandle(Connection->conn);
326     Connection->conn = 0;
327   }
328   if (Connection->ovl.hEvent) {
329     CloseHandle(Connection->ovl.hEvent);
330     Connection->ovl.hEvent = 0;
331   }
332   return RPC_S_OK;
333 }
334
335 RPC_STATUS RPCRT4_SpawnConnection(RpcConnection** Connection, RpcConnection* OldConnection)
336 {
337   RpcConnection* NewConnection;
338   RPC_STATUS err = RPCRT4_CreateConnection(&NewConnection, OldConnection->server, OldConnection->Protseq,
339                                            OldConnection->NetworkAddr, OldConnection->Endpoint, NULL, NULL);
340   if (err == RPC_S_OK) {
341     /* because of the way named pipes work, we'll transfer the connected pipe
342      * to the child, then reopen the server binding to continue listening */
343     NewConnection->conn = OldConnection->conn;
344     NewConnection->ovl = OldConnection->ovl;
345     OldConnection->conn = 0;
346     memset(&OldConnection->ovl, 0, sizeof(OldConnection->ovl));
347     *Connection = NewConnection;
348     RPCRT4_OpenConnection(OldConnection);
349   }
350   return err;
351 }
352
353 RPC_STATUS RPCRT4_AllocBinding(RpcBinding** Binding, BOOL server)
354 {
355   RpcBinding* NewBinding;
356
357   NewBinding = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RpcBinding));
358   NewBinding->refs = 1;
359   NewBinding->server = server;
360
361   *Binding = NewBinding;
362
363   return RPC_S_OK;
364 }
365
366 RPC_STATUS RPCRT4_CreateBindingA(RpcBinding** Binding, BOOL server, LPSTR Protseq)
367 {
368   RpcBinding* NewBinding;
369
370   RPCRT4_AllocBinding(&NewBinding, server);
371   NewBinding->Protseq = RPCRT4_strdupA(Protseq);
372
373   TRACE("binding: %p\n", NewBinding);
374   *Binding = NewBinding;
375
376   return RPC_S_OK;
377 }
378
379 RPC_STATUS RPCRT4_CreateBindingW(RpcBinding** Binding, BOOL server, LPWSTR Protseq)
380 {
381   RpcBinding* NewBinding;
382
383   RPCRT4_AllocBinding(&NewBinding, server);
384   NewBinding->Protseq = RPCRT4_strdupWtoA(Protseq);
385
386   TRACE("binding: %p\n", NewBinding);
387   *Binding = NewBinding;
388
389   return RPC_S_OK;
390 }
391
392 RPC_STATUS RPCRT4_CompleteBindingA(RpcBinding* Binding, LPSTR NetworkAddr,  LPSTR Endpoint,  LPSTR NetworkOptions)
393 {
394   TRACE("(RpcBinding == ^%p, NetworkAddr == \"%s\", EndPoint == \"%s\", NetworkOptions == \"%s\")\n", Binding,
395    debugstr_a(NetworkAddr), debugstr_a(Endpoint), debugstr_a(NetworkOptions));
396
397   RPCRT4_strfree(Binding->NetworkAddr);
398   Binding->NetworkAddr = RPCRT4_strdupA(NetworkAddr);
399   RPCRT4_strfree(Binding->Endpoint);
400   if (Endpoint) {
401     Binding->Endpoint = RPCRT4_strdupA(Endpoint);
402   } else {
403     Binding->Endpoint = RPCRT4_strdupA("");
404   }
405   if (!Binding->Endpoint) ERR("out of memory?\n");
406
407   return RPC_S_OK;
408 }
409
410 RPC_STATUS RPCRT4_CompleteBindingW(RpcBinding* Binding, LPWSTR NetworkAddr, LPWSTR Endpoint, LPWSTR NetworkOptions)
411 {
412   TRACE("(RpcBinding == ^%p, NetworkAddr == \"%s\", EndPoint == \"%s\", NetworkOptions == \"%s\")\n", Binding, 
413    debugstr_w(NetworkAddr), debugstr_w(Endpoint), debugstr_w(NetworkOptions));
414
415   RPCRT4_strfree(Binding->NetworkAddr);
416   Binding->NetworkAddr = RPCRT4_strdupWtoA(NetworkAddr);
417   RPCRT4_strfree(Binding->Endpoint);
418   if (Endpoint) {
419     Binding->Endpoint = RPCRT4_strdupWtoA(Endpoint);
420   } else {
421     Binding->Endpoint = RPCRT4_strdupA("");
422   }
423   if (!Binding->Endpoint) ERR("out of memory?\n");
424
425   return RPC_S_OK;
426 }
427
428 RPC_STATUS RPCRT4_ResolveBinding(RpcBinding* Binding, LPSTR Endpoint)
429 {
430   TRACE("(RpcBinding == ^%p, EndPoint == \"%s\"\n", Binding, Endpoint);
431
432   RPCRT4_strfree(Binding->Endpoint);
433   Binding->Endpoint = RPCRT4_strdupA(Endpoint);
434
435   return RPC_S_OK;
436 }
437
438 RPC_STATUS RPCRT4_SetBindingObject(RpcBinding* Binding, UUID* ObjectUuid)
439 {
440   TRACE("(*RpcBinding == ^%p, UUID == %s)\n", Binding, debugstr_guid(ObjectUuid)); 
441   if (ObjectUuid) memcpy(&Binding->ObjectUuid, ObjectUuid, sizeof(UUID));
442   else UuidCreateNil(&Binding->ObjectUuid);
443   return RPC_S_OK;
444 }
445
446 RPC_STATUS RPCRT4_MakeBinding(RpcBinding** Binding, RpcConnection* Connection)
447 {
448   RpcBinding* NewBinding;
449   TRACE("(*RpcBinding == ^%p, Connection == ^%p)\n", *Binding, Connection);
450
451   RPCRT4_AllocBinding(&NewBinding, Connection->server);
452   NewBinding->Protseq = RPCRT4_strdupA(Connection->Protseq);
453   NewBinding->NetworkAddr = RPCRT4_strdupA(Connection->NetworkAddr);
454   NewBinding->Endpoint = RPCRT4_strdupA(Connection->Endpoint);
455   NewBinding->FromConn = Connection;
456
457   TRACE("binding: %p\n", NewBinding);
458   *Binding = NewBinding;
459
460   return RPC_S_OK;
461 }
462
463 RPC_STATUS RPCRT4_ExportBinding(RpcBinding** Binding, RpcBinding* OldBinding)
464 {
465   InterlockedIncrement(&OldBinding->refs);
466   *Binding = OldBinding;
467   return RPC_S_OK;
468 }
469
470 RPC_STATUS RPCRT4_DestroyBinding(RpcBinding* Binding)
471 {
472   if (InterlockedDecrement(&Binding->refs))
473     return RPC_S_OK;
474
475   TRACE("binding: %p\n", Binding);
476   /* FIXME: release connections */
477   RPCRT4_strfree(Binding->Endpoint);
478   RPCRT4_strfree(Binding->NetworkAddr);
479   RPCRT4_strfree(Binding->Protseq);
480   HeapFree(GetProcessHeap(), 0, Binding);
481   return RPC_S_OK;
482 }
483
484 RPC_STATUS RPCRT4_OpenBinding(RpcBinding* Binding, RpcConnection** Connection)
485 {
486   RpcConnection* NewConnection;
487   TRACE("(Binding == ^%p)\n", Binding);
488   if (Binding->FromConn) {
489     *Connection = Binding->FromConn;
490     return RPC_S_OK;
491   }
492
493   RPCRT4_GetConnection(&NewConnection, Binding->server, Binding->Protseq, Binding->NetworkAddr, Binding->Endpoint, NULL, Binding);
494   *Connection = NewConnection;
495   return RPCRT4_OpenConnection(NewConnection);
496 }
497
498 RPC_STATUS RPCRT4_CloseBinding(RpcBinding* Binding, RpcConnection* Connection)
499 {
500   TRACE("(Binding == ^%p)\n", Binding);
501   if (!Connection) return RPC_S_OK;
502   if (Binding->FromConn == Connection) return RPC_S_OK;
503   return RPCRT4_ReleaseConnection(Connection);
504 }
505
506 /* utility functions for string composing and parsing */
507 static unsigned RPCRT4_strcopyA(LPSTR data, LPCSTR src)
508 {
509   unsigned len = strlen(src);
510   memcpy(data, src, len*sizeof(CHAR));
511   return len;
512 }
513
514 static unsigned RPCRT4_strcopyW(LPWSTR data, LPCWSTR src)
515 {
516   unsigned len = strlenW(src);
517   memcpy(data, src, len*sizeof(WCHAR));
518   return len;
519 }
520
521 static LPSTR RPCRT4_strconcatA(LPSTR dst, LPCSTR src)
522 {
523   DWORD len = strlen(dst), slen = strlen(src);
524   LPSTR ndst = HeapReAlloc(GetProcessHeap(), 0, dst, (len+slen+2)*sizeof(CHAR));
525   if (!ndst)
526   {
527     HeapFree(GetProcessHeap(), 0, dst);
528     return NULL;
529   }
530   ndst[len] = ',';
531   memcpy(ndst+len+1, src, slen+1);
532   return ndst;
533 }
534
535 static LPWSTR RPCRT4_strconcatW(LPWSTR dst, LPCWSTR src)
536 {
537   DWORD len = strlenW(dst), slen = strlenW(src);
538   LPWSTR ndst = HeapReAlloc(GetProcessHeap(), 0, dst, (len+slen+2)*sizeof(WCHAR));
539   if (!ndst) 
540   {
541     HeapFree(GetProcessHeap(), 0, dst);
542     return NULL;
543   }
544   ndst[len] = ',';
545   memcpy(ndst+len+1, src, (slen+1)*sizeof(WCHAR));
546   return ndst;
547 }
548
549
550 /***********************************************************************
551  *             RpcStringBindingComposeA (RPCRT4.@)
552  */
553 RPC_STATUS WINAPI RpcStringBindingComposeA( LPSTR ObjUuid, LPSTR Protseq,
554                                            LPSTR NetworkAddr, LPSTR Endpoint,
555                                            LPSTR Options, LPSTR* StringBinding )
556 {
557   DWORD len = 1;
558   LPSTR data;
559
560   TRACE( "(%s,%s,%s,%s,%s,%p)\n",
561         debugstr_a( ObjUuid ), debugstr_a( Protseq ),
562         debugstr_a( NetworkAddr ), debugstr_a( Endpoint ),
563         debugstr_a( Options ), StringBinding );
564
565   if (ObjUuid && *ObjUuid) len += strlen(ObjUuid) + 1;
566   if (Protseq && *Protseq) len += strlen(Protseq) + 1;
567   if (NetworkAddr && *NetworkAddr) len += strlen(NetworkAddr);
568   if (Endpoint && *Endpoint) len += strlen(Endpoint) + 2;
569   if (Options && *Options) len += strlen(Options) + 2;
570
571   data = HeapAlloc(GetProcessHeap(), 0, len);
572   *StringBinding = data;
573
574   if (ObjUuid && *ObjUuid) {
575     data += RPCRT4_strcopyA(data, ObjUuid);
576     *data++ = '@';
577   }
578   if (Protseq && *Protseq) {
579     data += RPCRT4_strcopyA(data, Protseq);
580     *data++ = ':';
581   }
582   if (NetworkAddr && *NetworkAddr)
583     data += RPCRT4_strcopyA(data, NetworkAddr);
584
585   if ((Endpoint && *Endpoint) ||
586       (Options && *Options)) {
587     *data++ = '[';
588     if (Endpoint && *Endpoint) {
589       data += RPCRT4_strcopyA(data, Endpoint);
590       if (Options && *Options) *data++ = ',';
591     }
592     if (Options && *Options) {
593       data += RPCRT4_strcopyA(data, Options);
594     }
595     *data++ = ']';
596   }
597   *data = 0;
598
599   return RPC_S_OK;
600 }
601
602 /***********************************************************************
603  *             RpcStringBindingComposeW (RPCRT4.@)
604  */
605 RPC_STATUS WINAPI RpcStringBindingComposeW( LPWSTR ObjUuid, LPWSTR Protseq,
606                                             LPWSTR NetworkAddr, LPWSTR Endpoint,
607                                             LPWSTR Options, LPWSTR* StringBinding )
608 {
609   DWORD len = 1;
610   LPWSTR data;
611
612   TRACE("(%s,%s,%s,%s,%s,%p)\n",
613        debugstr_w( ObjUuid ), debugstr_w( Protseq ),
614        debugstr_w( NetworkAddr ), debugstr_w( Endpoint ),
615        debugstr_w( Options ), StringBinding);
616
617   if (ObjUuid && *ObjUuid) len += strlenW(ObjUuid) + 1;
618   if (Protseq && *Protseq) len += strlenW(Protseq) + 1;
619   if (NetworkAddr && *NetworkAddr) len += strlenW(NetworkAddr);
620   if (Endpoint && *Endpoint) len += strlenW(Endpoint) + 2;
621   if (Options && *Options) len += strlenW(Options) + 2;
622
623   data = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
624   *StringBinding = data;
625
626   if (ObjUuid && *ObjUuid) {
627     data += RPCRT4_strcopyW(data, ObjUuid);
628     *data++ = '@';
629   }
630   if (Protseq && *Protseq) {
631     data += RPCRT4_strcopyW(data, Protseq);
632     *data++ = ':';
633   }
634   if (NetworkAddr && *NetworkAddr) {
635     data += RPCRT4_strcopyW(data, NetworkAddr);
636   }
637   if ((Endpoint && *Endpoint) ||
638       (Options && *Options)) {
639     *data++ = '[';
640     if (Endpoint && *Endpoint) {
641       data += RPCRT4_strcopyW(data, Endpoint);
642       if (Options && *Options) *data++ = ',';
643     }
644     if (Options && *Options) {
645       data += RPCRT4_strcopyW(data, Options);
646     }
647     *data++ = ']';
648   }
649   *data = 0;
650
651   return RPC_S_OK;
652 }
653
654
655 /***********************************************************************
656  *             RpcStringBindingParseA (RPCRT4.@)
657  */
658 RPC_STATUS WINAPI RpcStringBindingParseA( LPSTR StringBinding, LPSTR *ObjUuid,
659                                           LPSTR *Protseq, LPSTR *NetworkAddr,
660                                           LPSTR *Endpoint, LPSTR *Options)
661 {
662   CHAR *data, *next;
663   static const char ep_opt[] = "endpoint=";
664
665   TRACE("(%s,%p,%p,%p,%p,%p)\n", debugstr_a(StringBinding),
666        ObjUuid, Protseq, NetworkAddr, Endpoint, Options);
667
668   if (ObjUuid) *ObjUuid = NULL;
669   if (Protseq) *Protseq = NULL;
670   if (NetworkAddr) *NetworkAddr = NULL;
671   if (Endpoint) *Endpoint = NULL;
672   if (Options) *Options = NULL;
673
674   data = StringBinding;
675
676   next = strchr(data, '@');
677   if (next) {
678     if (ObjUuid) *ObjUuid = RPCRT4_strndupA(data, next - data);
679     data = next+1;
680   }
681
682   next = strchr(data, ':');
683   if (next) {
684     if (Protseq) *Protseq = RPCRT4_strndupA(data, next - data);
685     data = next+1;
686   }
687
688   next = strchr(data, '[');
689   if (next) {
690     CHAR *close, *opt;
691
692     if (NetworkAddr) *NetworkAddr = RPCRT4_strndupA(data, next - data);
693     data = next+1;
694     close = strchr(data, ']');
695     if (!close) goto fail;
696
697     /* tokenize options */
698     while (data < close) {
699       next = strchr(data, ',');
700       if (!next || next > close) next = close;
701       /* FIXME: this is kind of inefficient */
702       opt = RPCRT4_strndupA(data, next - data);
703       data = next+1;
704
705       /* parse option */
706       next = strchr(opt, '=');
707       if (!next) {
708         /* not an option, must be an endpoint */
709         if (*Endpoint) goto fail;
710         *Endpoint = opt;
711       } else {
712         if (strncmp(opt, ep_opt, strlen(ep_opt)) == 0) {
713           /* endpoint option */
714           if (*Endpoint) goto fail;
715           *Endpoint = RPCRT4_strdupA(next+1);
716           HeapFree(GetProcessHeap(), 0, opt);
717         } else {
718           /* network option */
719           if (*Options) {
720             /* FIXME: this is kind of inefficient */
721             *Options = RPCRT4_strconcatA(*Options, opt);
722             HeapFree(GetProcessHeap(), 0, opt);
723           } else 
724             *Options = opt;
725         }
726       }
727     }
728
729     data = close+1;
730     if (*data) goto fail;
731   }
732   else if (NetworkAddr) 
733     *NetworkAddr = RPCRT4_strdupA(data);
734
735   return RPC_S_OK;
736
737 fail:
738   if (ObjUuid) RpcStringFreeA((unsigned char**)ObjUuid);
739   if (Protseq) RpcStringFreeA((unsigned char**)Protseq);
740   if (NetworkAddr) RpcStringFreeA((unsigned char**)NetworkAddr);
741   if (Endpoint) RpcStringFreeA((unsigned char**)Endpoint);
742   if (Options) RpcStringFreeA((unsigned char**)Options);
743   return RPC_S_INVALID_STRING_BINDING;
744 }
745
746 /***********************************************************************
747  *             RpcStringBindingParseW (RPCRT4.@)
748  */
749 RPC_STATUS WINAPI RpcStringBindingParseW( LPWSTR StringBinding, LPWSTR *ObjUuid,
750                                           LPWSTR *Protseq, LPWSTR *NetworkAddr,
751                                           LPWSTR *Endpoint, LPWSTR *Options)
752 {
753   WCHAR *data, *next;
754   static const WCHAR ep_opt[] = {'e','n','d','p','o','i','n','t','=',0};
755
756   TRACE("(%s,%p,%p,%p,%p,%p)\n", debugstr_w(StringBinding),
757        ObjUuid, Protseq, NetworkAddr, Endpoint, Options);
758
759   if (ObjUuid) *ObjUuid = NULL;
760   if (Protseq) *Protseq = NULL;
761   if (NetworkAddr) *NetworkAddr = NULL;
762   if (Endpoint) *Endpoint = NULL;
763   if (Options) *Options = NULL;
764
765   data = StringBinding;
766
767   next = strchrW(data, '@');
768   if (next) {
769     if (ObjUuid) *ObjUuid = RPCRT4_strndupW(data, next - data);
770     data = next+1;
771   }
772
773   next = strchrW(data, ':');
774   if (next) {
775     if (Protseq) *Protseq = RPCRT4_strndupW(data, next - data);
776     data = next+1;
777   }
778
779   next = strchrW(data, '[');
780   if (next) {
781     WCHAR *close, *opt;
782
783     if (NetworkAddr) *NetworkAddr = RPCRT4_strndupW(data, next - data);
784     data = next+1;
785     close = strchrW(data, ']');
786     if (!close) goto fail;
787
788     /* tokenize options */
789     while (data < close) {
790       next = strchrW(data, ',');
791       if (!next || next > close) next = close;
792       /* FIXME: this is kind of inefficient */
793       opt = RPCRT4_strndupW(data, next - data);
794       data = next+1;
795
796       /* parse option */
797       next = strchrW(opt, '=');
798       if (!next) {
799         /* not an option, must be an endpoint */
800         if (*Endpoint) goto fail;
801         *Endpoint = opt;
802       } else {
803         if (strncmpW(opt, ep_opt, strlenW(ep_opt)) == 0) {
804           /* endpoint option */
805           if (*Endpoint) goto fail;
806           *Endpoint = RPCRT4_strdupW(next+1);
807           HeapFree(GetProcessHeap(), 0, opt);
808         } else {
809           /* network option */
810           if (*Options) {
811             /* FIXME: this is kind of inefficient */
812             *Options = RPCRT4_strconcatW(*Options, opt);
813             HeapFree(GetProcessHeap(), 0, opt);
814           } else 
815             *Options = opt;
816         }
817       }
818     }
819
820     data = close+1;
821     if (*data) goto fail;
822   } else if (NetworkAddr) 
823     *NetworkAddr = RPCRT4_strdupW(data);
824
825   return RPC_S_OK;
826
827 fail:
828   if (ObjUuid) RpcStringFreeW(ObjUuid);
829   if (Protseq) RpcStringFreeW(Protseq);
830   if (NetworkAddr) RpcStringFreeW(NetworkAddr);
831   if (Endpoint) RpcStringFreeW(Endpoint);
832   if (Options) RpcStringFreeW(Options);
833   return RPC_S_INVALID_STRING_BINDING;
834 }
835
836 /***********************************************************************
837  *             RpcBindingFree (RPCRT4.@)
838  */
839 RPC_STATUS WINAPI RpcBindingFree( RPC_BINDING_HANDLE* Binding )
840 {
841   RPC_STATUS status;
842   TRACE("(%p) = %p\n", Binding, *Binding);
843   status = RPCRT4_DestroyBinding(*Binding);
844   if (status == RPC_S_OK) *Binding = 0;
845   return status;
846 }
847   
848 /***********************************************************************
849  *             RpcBindingVectorFree (RPCRT4.@)
850  */
851 RPC_STATUS WINAPI RpcBindingVectorFree( RPC_BINDING_VECTOR** BindingVector )
852 {
853   RPC_STATUS status;
854   unsigned long c;
855
856   TRACE("(%p)\n", BindingVector);
857   for (c=0; c<(*BindingVector)->Count; c++) {
858     status = RpcBindingFree(&(*BindingVector)->BindingH[c]);
859   }
860   HeapFree(GetProcessHeap(), 0, *BindingVector);
861   *BindingVector = NULL;
862   return RPC_S_OK;
863 }
864   
865 /***********************************************************************
866  *             RpcBindingInqObject (RPCRT4.@)
867  */
868 RPC_STATUS WINAPI RpcBindingInqObject( RPC_BINDING_HANDLE Binding, UUID* ObjectUuid )
869 {
870   RpcBinding* bind = (RpcBinding*)Binding;
871
872   TRACE("(%p,%p) = %s\n", Binding, ObjectUuid, debugstr_guid(&bind->ObjectUuid));
873   memcpy(ObjectUuid, &bind->ObjectUuid, sizeof(UUID));
874   return RPC_S_OK;
875 }
876   
877 /***********************************************************************
878  *             RpcBindingSetObject (RPCRT4.@)
879  */
880 RPC_STATUS WINAPI RpcBindingSetObject( RPC_BINDING_HANDLE Binding, UUID* ObjectUuid )
881 {
882   RpcBinding* bind = (RpcBinding*)Binding;
883
884   TRACE("(%p,%s)\n", Binding, debugstr_guid(ObjectUuid));
885   if (bind->server) return RPC_S_WRONG_KIND_OF_BINDING;
886   return RPCRT4_SetBindingObject(Binding, ObjectUuid);
887 }
888
889 /***********************************************************************
890  *             RpcBindingFromStringBindingA (RPCRT4.@)
891  */
892 RPC_STATUS WINAPI RpcBindingFromStringBindingA( LPSTR StringBinding, RPC_BINDING_HANDLE* Binding )
893 {
894   RPC_STATUS ret;
895   RpcBinding* bind = NULL;
896   LPSTR ObjectUuid, Protseq, NetworkAddr, Endpoint, Options;
897   UUID Uuid;
898
899   TRACE("(%s,%p)\n", debugstr_a(StringBinding), Binding);
900
901   ret = RpcStringBindingParseA(StringBinding, &ObjectUuid, &Protseq,
902                               &NetworkAddr, &Endpoint, &Options);
903   if (ret != RPC_S_OK) return ret;
904
905   ret = UuidFromStringA(ObjectUuid, &Uuid);
906
907   if (ret == RPC_S_OK)
908     ret = RPCRT4_CreateBindingA(&bind, FALSE, Protseq);
909   if (ret == RPC_S_OK)
910     ret = RPCRT4_SetBindingObject(bind, &Uuid);
911   if (ret == RPC_S_OK)
912     ret = RPCRT4_CompleteBindingA(bind, NetworkAddr, Endpoint, Options);
913
914   RpcStringFreeA((unsigned char**)&Options);
915   RpcStringFreeA((unsigned char**)&Endpoint);
916   RpcStringFreeA((unsigned char**)&NetworkAddr);
917   RpcStringFreeA((unsigned char**)&Protseq);
918   RpcStringFreeA((unsigned char**)&ObjectUuid);
919
920   if (ret == RPC_S_OK) 
921     *Binding = (RPC_BINDING_HANDLE)bind;
922   else 
923     RPCRT4_DestroyBinding(bind);
924
925   return ret;
926 }
927
928 /***********************************************************************
929  *             RpcBindingFromStringBindingW (RPCRT4.@)
930  */
931 RPC_STATUS WINAPI RpcBindingFromStringBindingW( LPWSTR StringBinding, RPC_BINDING_HANDLE* Binding )
932 {
933   RPC_STATUS ret;
934   RpcBinding* bind = NULL;
935   LPWSTR ObjectUuid, Protseq, NetworkAddr, Endpoint, Options;
936   UUID Uuid;
937
938   TRACE("(%s,%p)\n", debugstr_w(StringBinding), Binding);
939
940   ret = RpcStringBindingParseW(StringBinding, &ObjectUuid, &Protseq,
941                               &NetworkAddr, &Endpoint, &Options);
942   if (ret != RPC_S_OK) return ret;
943
944   ret = UuidFromStringW(ObjectUuid, &Uuid);
945
946   if (ret == RPC_S_OK)
947     ret = RPCRT4_CreateBindingW(&bind, FALSE, Protseq);
948   if (ret == RPC_S_OK)
949     ret = RPCRT4_SetBindingObject(bind, &Uuid);
950   if (ret == RPC_S_OK)
951     ret = RPCRT4_CompleteBindingW(bind, NetworkAddr, Endpoint, Options);
952
953   RpcStringFreeW(&Options);
954   RpcStringFreeW(&Endpoint);
955   RpcStringFreeW(&NetworkAddr);
956   RpcStringFreeW(&Protseq);
957   RpcStringFreeW(&ObjectUuid);
958
959   if (ret == RPC_S_OK)
960     *Binding = (RPC_BINDING_HANDLE)bind;
961   else
962     RPCRT4_DestroyBinding(bind);
963
964   return ret;
965 }
966   
967 /***********************************************************************
968  *             RpcBindingToStringBindingA (RPCRT4.@)
969  */
970 RPC_STATUS WINAPI RpcBindingToStringBindingA( RPC_BINDING_HANDLE Binding, LPSTR* StringBinding )
971 {
972   RPC_STATUS ret;
973   RpcBinding* bind = (RpcBinding*)Binding;
974   LPSTR ObjectUuid;
975
976   TRACE("(%p,%p)\n", Binding, StringBinding);
977
978   ret = UuidToStringA(&bind->ObjectUuid, (unsigned char**)&ObjectUuid);
979   if (ret != RPC_S_OK) return ret;
980
981   ret = RpcStringBindingComposeA(ObjectUuid, bind->Protseq, bind->NetworkAddr,
982                                  bind->Endpoint, NULL, StringBinding);
983
984   RpcStringFreeA((unsigned char**)&ObjectUuid);
985
986   return ret;
987 }
988   
989 /***********************************************************************
990  *             RpcBindingToStringBindingW (RPCRT4.@)
991  */
992 RPC_STATUS WINAPI RpcBindingToStringBindingW( RPC_BINDING_HANDLE Binding, LPWSTR* StringBinding )
993 {
994   RPC_STATUS ret;
995   LPSTR str = NULL;
996   TRACE("(%p,%p)\n", Binding, StringBinding);
997   ret = RpcBindingToStringBindingA(Binding, &str);
998   *StringBinding = RPCRT4_strdupAtoW(str);
999   RpcStringFreeA((unsigned char**)&str);
1000   return ret;
1001 }
1002
1003 /***********************************************************************
1004  *             I_RpcBindingSetAsync (RPCRT4.@)
1005  * NOTES
1006  *  Exists in win9x and winNT, but with different number of arguments
1007  *  (9x version has 3 arguments, NT has 2).
1008  */
1009 RPC_STATUS WINAPI I_RpcBindingSetAsync( RPC_BINDING_HANDLE Binding, RPC_BLOCKING_FN BlockingFn)
1010 {
1011   RpcBinding* bind = (RpcBinding*)Binding;
1012
1013   TRACE( "(%p,%p): stub\n", Binding, BlockingFn );
1014
1015   bind->BlockingFn = BlockingFn;
1016
1017   return RPC_S_OK;
1018 }
1019
1020 /***********************************************************************
1021  *             RpcNetworkIsProtSeqValidA (RPCRT4.@)
1022  */
1023 RPC_STATUS RPC_ENTRY RpcNetworkIsProtSeqValidA(unsigned char *protseq) {
1024   UNICODE_STRING protseqW;
1025
1026   if (!protseq) return RPC_S_INVALID_RPC_PROTSEQ; /* ? */
1027   
1028   if (RtlCreateUnicodeStringFromAsciiz(&protseqW, protseq)) {
1029     RPC_STATUS ret = RpcNetworkIsProtSeqValidW(protseqW.Buffer);
1030     RtlFreeUnicodeString(&protseqW);
1031     return ret;
1032   } else return RPC_S_OUT_OF_MEMORY;
1033 }
1034
1035 /***********************************************************************
1036  *             RpcNetworkIsProtSeqValidW (RPCRT4.@)
1037  * 
1038  * Checks if the given protocol sequence is known by the RPC system.
1039  * If it is, returns RPC_S_OK, otherwise RPC_S_PROTSEQ_NOT_SUPPORTED.
1040  *
1041  * We currently support:
1042  *   ncalrpc   local-only rpc over LPC (LPC is not really used)
1043  *   ncacn_np  rpc over named pipes
1044  */
1045 RPC_STATUS RPC_ENTRY RpcNetworkIsProtSeqValidW(LPWSTR protseq) {
1046   static const WCHAR protseqsW[][15] = { 
1047     {'n','c','a','l','r','p','c',0},
1048     {'n','c','a','c','n','_','n','p',0}
1049   };
1050   static const int count = sizeof(protseqsW) / sizeof(protseqsW[0]);
1051   int i;
1052
1053   if (!protseq) return RPC_S_INVALID_RPC_PROTSEQ; /* ? */
1054
1055   for (i = 0; i < count; i++) {
1056     if (!strcmpW(protseq, protseqsW[i])) return RPC_S_OK;
1057   }
1058   
1059   FIXME("Unknown protseq %s - we probably need to implement it one day", debugstr_w(protseq));
1060   return RPC_S_PROTSEQ_NOT_SUPPORTED;
1061 }