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