Finish removal of iconv_convenience in public API's.
[bbaumbach/samba-autobuild/.git] / librpc / rpc / binding.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    dcerpc utility functions
5
6    Copyright (C) Andrew Tridgell 2003
7    Copyright (C) Jelmer Vernooij 2004
8    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
9    Copyright (C) Rafal Szczesniak 2006
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "../../lib/util/util_net.h"
27 #include "librpc/gen_ndr/ndr_epmapper.h"
28 #include "librpc/gen_ndr/ndr_misc.h"
29 #include "librpc/rpc/dcerpc.h"
30 #undef strcasecmp
31
32 #define MAX_PROTSEQ             10
33
34 static const struct {
35         const char *name;
36         enum dcerpc_transport_t transport;
37         int num_protocols;
38         enum epm_protocol protseq[MAX_PROTSEQ];
39 } transports[] = {
40         { "ncacn_np",     NCACN_NP, 3, 
41                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NETBIOS }},
42         { "ncacn_ip_tcp", NCACN_IP_TCP, 3, 
43                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_TCP, EPM_PROTOCOL_IP } }, 
44         { "ncacn_http", NCACN_HTTP, 3, 
45                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_HTTP, EPM_PROTOCOL_IP } }, 
46         { "ncadg_ip_udp", NCACN_IP_UDP, 3, 
47                 { EPM_PROTOCOL_NCADG, EPM_PROTOCOL_UDP, EPM_PROTOCOL_IP } },
48         { "ncalrpc", NCALRPC, 2, 
49                 { EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_NAMED_PIPE } },
50         { "ncacn_unix_stream", NCACN_UNIX_STREAM, 2, 
51                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_UNIX_DS } },
52         { "ncadg_unix_dgram", NCADG_UNIX_DGRAM, 2, 
53                 { EPM_PROTOCOL_NCADG, EPM_PROTOCOL_UNIX_DS } },
54         { "ncacn_at_dsp", NCACN_AT_DSP, 3, 
55                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_APPLETALK, EPM_PROTOCOL_DSP } },
56         { "ncadg_at_ddp", NCADG_AT_DDP, 3, 
57                 { EPM_PROTOCOL_NCADG, EPM_PROTOCOL_APPLETALK, EPM_PROTOCOL_DDP } },
58         { "ncacn_vns_ssp", NCACN_VNS_SPP, 3, 
59                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_STREETTALK, EPM_PROTOCOL_VINES_SPP } },
60         { "ncacn_vns_ipc", NCACN_VNS_IPC, 3, 
61                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_STREETTALK, EPM_PROTOCOL_VINES_IPC }, },
62         { "ncadg_ipx", NCADG_IPX, 2,
63                 { EPM_PROTOCOL_NCADG, EPM_PROTOCOL_IPX },
64         },
65         { "ncacn_spx", NCACN_SPX, 3,
66                 /* I guess some MS programmer confused the identifier for 
67                  * EPM_PROTOCOL_UUID (0x0D or 13) with the one for 
68                  * EPM_PROTOCOL_SPX (0x13) here. -- jelmer*/
69                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_UUID },
70         },
71 };
72
73 static const struct {
74         const char *name;
75         uint32_t flag;
76 } ncacn_options[] = {
77         {"sign", DCERPC_SIGN},
78         {"seal", DCERPC_SEAL},
79         {"connect", DCERPC_CONNECT},
80         {"spnego", DCERPC_AUTH_SPNEGO},
81         {"ntlm", DCERPC_AUTH_NTLM},
82         {"krb5", DCERPC_AUTH_KRB5},
83         {"validate", DCERPC_DEBUG_VALIDATE_BOTH},
84         {"print", DCERPC_DEBUG_PRINT_BOTH},
85         {"padcheck", DCERPC_DEBUG_PAD_CHECK},
86         {"bigendian", DCERPC_PUSH_BIGENDIAN},
87         {"smb2", DCERPC_SMB2},
88         {"hdrsign", DCERPC_HEADER_SIGNING},
89         {"ndr64", DCERPC_NDR64}
90 };
91
92 const char *epm_floor_string(TALLOC_CTX *mem_ctx, struct epm_floor *epm_floor)
93 {
94         struct ndr_syntax_id syntax;
95         NTSTATUS status;
96
97         switch(epm_floor->lhs.protocol) {
98                 case EPM_PROTOCOL_UUID:
99                         status = dcerpc_floor_get_lhs_data(epm_floor, &syntax);
100                         if (NT_STATUS_IS_OK(status)) {
101                                 /* lhs is used: UUID */
102                                 char *uuidstr;
103
104                                 if (GUID_equal(&syntax.uuid, &ndr_transfer_syntax.uuid)) {
105                                         return "NDR";
106                                 } 
107
108                                 if (GUID_equal(&syntax.uuid, &ndr64_transfer_syntax.uuid)) {
109                                         return "NDR64";
110                                 } 
111
112                                 uuidstr = GUID_string(mem_ctx, &syntax.uuid);
113
114                                 return talloc_asprintf(mem_ctx, " uuid %s/0x%02x", uuidstr, syntax.if_version);
115                         } else { /* IPX */
116                                 return talloc_asprintf(mem_ctx, "IPX:%s", 
117                                                 data_blob_hex_string_upper(mem_ctx, &epm_floor->rhs.uuid.unknown));
118                         }
119
120                 case EPM_PROTOCOL_NCACN:
121                         return "RPC-C";
122
123                 case EPM_PROTOCOL_NCADG:
124                         return "RPC";
125
126                 case EPM_PROTOCOL_NCALRPC:
127                         return "NCALRPC";
128
129                 case EPM_PROTOCOL_DNET_NSP:
130                         return "DNET/NSP";
131
132                 case EPM_PROTOCOL_IP:
133                         return talloc_asprintf(mem_ctx, "IP:%s", epm_floor->rhs.ip.ipaddr);
134
135                 case EPM_PROTOCOL_NAMED_PIPE:
136                         return talloc_asprintf(mem_ctx, "NAMED-PIPE:%s", epm_floor->rhs.named_pipe.path);
137
138                 case EPM_PROTOCOL_SMB:
139                         return talloc_asprintf(mem_ctx, "SMB:%s", epm_floor->rhs.smb.unc);
140
141                 case EPM_PROTOCOL_UNIX_DS:
142                         return talloc_asprintf(mem_ctx, "Unix:%s", epm_floor->rhs.unix_ds.path);
143
144                 case EPM_PROTOCOL_NETBIOS:
145                         return talloc_asprintf(mem_ctx, "NetBIOS:%s", epm_floor->rhs.netbios.name);
146
147                 case EPM_PROTOCOL_NETBEUI:
148                         return "NETBeui";
149
150                 case EPM_PROTOCOL_SPX:
151                         return "SPX";
152
153                 case EPM_PROTOCOL_NB_IPX:
154                         return "NB_IPX";
155
156                 case EPM_PROTOCOL_HTTP:
157                         return talloc_asprintf(mem_ctx, "HTTP:%d", epm_floor->rhs.http.port);
158
159                 case EPM_PROTOCOL_TCP:
160                         return talloc_asprintf(mem_ctx, "TCP:%d", epm_floor->rhs.tcp.port);
161
162                 case EPM_PROTOCOL_UDP:
163                         return talloc_asprintf(mem_ctx, "UDP:%d", epm_floor->rhs.udp.port);
164
165                 default:
166                         return talloc_asprintf(mem_ctx, "UNK(%02x):", epm_floor->lhs.protocol);
167         }
168 }
169
170
171 /*
172   form a binding string from a binding structure
173 */
174 _PUBLIC_ char *dcerpc_binding_string(TALLOC_CTX *mem_ctx, const struct dcerpc_binding *b)
175 {
176         char *s = talloc_strdup(mem_ctx, "");
177         int i;
178         const char *t_name = NULL;
179
180         if (b->transport != NCA_UNKNOWN) {
181                 t_name = derpc_transport_string_by_transport(b->transport);
182                 if (!t_name) {
183                         return NULL;
184                 }
185         }
186
187         if (!GUID_all_zero(&b->object.uuid)) { 
188                 s = talloc_asprintf(s, "%s@",
189                                     GUID_string(mem_ctx, &b->object.uuid));
190         }
191
192         if (t_name != NULL) {
193                 s = talloc_asprintf_append_buffer(s, "%s:", t_name);
194                 if (s == NULL) {
195                         return NULL;
196                 }
197         }
198
199         if (b->host) {
200                 s = talloc_asprintf_append_buffer(s, "%s", b->host);
201         }
202
203         if (!b->endpoint && !b->options && !b->flags) {
204                 return s;
205         }
206
207         s = talloc_asprintf_append_buffer(s, "[");
208
209         if (b->endpoint) {
210                 s = talloc_asprintf_append_buffer(s, "%s", b->endpoint);
211         }
212
213         /* this is a *really* inefficent way of dealing with strings,
214            but this is rarely called and the strings are always short,
215            so I don't care */
216         for (i=0;b->options && b->options[i];i++) {
217                 s = talloc_asprintf_append_buffer(s, ",%s", b->options[i]);
218                 if (!s) return NULL;
219         }
220
221         for (i=0;i<ARRAY_SIZE(ncacn_options);i++) {
222                 if (b->flags & ncacn_options[i].flag) {
223                         s = talloc_asprintf_append_buffer(s, ",%s", ncacn_options[i].name);
224                         if (!s) return NULL;
225                 }
226         }
227
228         s = talloc_asprintf_append_buffer(s, "]");
229
230         return s;
231 }
232
233 /*
234   parse a binding string into a dcerpc_binding structure
235 */
236 _PUBLIC_ NTSTATUS dcerpc_parse_binding(TALLOC_CTX *mem_ctx, const char *s, struct dcerpc_binding **b_out)
237 {
238         struct dcerpc_binding *b;
239         char *options;
240         char *p;
241         int i, j, comma_count;
242
243         b = talloc(mem_ctx, struct dcerpc_binding);
244         if (!b) {
245                 return NT_STATUS_NO_MEMORY;
246         }
247
248         p = strchr(s, '@');
249
250         if (p && PTR_DIFF(p, s) == 36) { /* 36 is the length of a UUID */
251                 NTSTATUS status;
252                 DATA_BLOB blob = data_blob(s, 36);
253                 status = GUID_from_data_blob(&blob, &b->object.uuid);
254
255                 if (NT_STATUS_IS_ERR(status)) {
256                         DEBUG(0, ("Failed parsing UUID\n"));
257                         return status;
258                 }
259
260                 s = p + 1;
261         } else {
262                 ZERO_STRUCT(b->object);
263         }
264
265         b->object.if_version = 0;
266
267         p = strchr(s, ':');
268
269         if (p == NULL) {
270                 b->transport = NCA_UNKNOWN;
271         } else {
272                 char *type = talloc_strndup(mem_ctx, s, PTR_DIFF(p, s));
273                 if (!type) {
274                         return NT_STATUS_NO_MEMORY;
275                 }
276
277                 for (i=0;i<ARRAY_SIZE(transports);i++) {
278                         if (strcasecmp(type, transports[i].name) == 0) {
279                                 b->transport = transports[i].transport;
280                                 break;
281                         }
282                 }
283
284                 if (i==ARRAY_SIZE(transports)) {
285                         DEBUG(0,("Unknown dcerpc transport '%s'\n", type));
286                         return NT_STATUS_INVALID_PARAMETER;
287                 }
288
289                 talloc_free(type);
290
291                 s = p+1;
292         }
293
294         p = strchr(s, '[');
295         if (p) {
296                 b->host = talloc_strndup(b, s, PTR_DIFF(p, s));
297                 options = talloc_strdup(mem_ctx, p+1);
298                 if (options[strlen(options)-1] != ']') {
299                         return NT_STATUS_INVALID_PARAMETER;
300                 }
301                 options[strlen(options)-1] = 0;
302         } else {
303                 b->host = talloc_strdup(b, s);
304                 options = NULL;
305         }
306         if (!b->host) {
307                 return NT_STATUS_NO_MEMORY;
308         }
309
310         b->target_hostname = b->host;
311
312         b->options = NULL;
313         b->flags = 0;
314         b->assoc_group_id = 0;
315         b->endpoint = NULL;
316
317         if (!options) {
318                 *b_out = b;
319                 return NT_STATUS_OK;
320         }
321
322         comma_count = count_chars(options, ',');
323
324         b->options = talloc_array(b, const char *, comma_count+2);
325         if (!b->options) {
326                 return NT_STATUS_NO_MEMORY;
327         }
328
329         for (i=0; (p = strchr(options, ',')); i++) {
330                 b->options[i] = talloc_strndup(b, options, PTR_DIFF(p, options));
331                 if (!b->options[i]) {
332                         return NT_STATUS_NO_MEMORY;
333                 }
334                 options = p+1;
335         }
336         b->options[i] = options;
337         b->options[i+1] = NULL;
338
339         /* some options are pre-parsed for convenience */
340         for (i=0;b->options[i];i++) {
341                 for (j=0;j<ARRAY_SIZE(ncacn_options);j++) {
342                         if (strcasecmp(ncacn_options[j].name, b->options[i]) == 0) {
343                                 int k;
344                                 b->flags |= ncacn_options[j].flag;
345                                 for (k=i;b->options[k];k++) {
346                                         b->options[k] = b->options[k+1];
347                                 }
348                                 i--;
349                                 break;
350                         }
351                 }
352         }
353
354         if (b->options[0]) {
355                 /* Endpoint is first option */
356                 b->endpoint = b->options[0];
357                 if (strlen(b->endpoint) == 0) b->endpoint = NULL;
358
359                 for (i=0;b->options[i];i++) {
360                         b->options[i] = b->options[i+1];
361                 }
362         }
363
364         if (b->options[0] == NULL)
365                 b->options = NULL;
366
367         *b_out = b;
368         return NT_STATUS_OK;
369 }
370
371 _PUBLIC_ NTSTATUS dcerpc_floor_get_lhs_data(const struct epm_floor *epm_floor,
372                                             struct ndr_syntax_id *syntax)
373 {
374         TALLOC_CTX *mem_ctx = talloc_init("floor_get_lhs_data");
375         struct ndr_pull *ndr;
376         enum ndr_err_code ndr_err;
377         uint16_t if_version=0;
378
379         ndr = ndr_pull_init_blob(&epm_floor->lhs.lhs_data, mem_ctx);
380         if (ndr == NULL) {
381                 talloc_free(mem_ctx);
382                 return NT_STATUS_NO_MEMORY;
383         }
384         ndr->flags |= LIBNDR_FLAG_NOALIGN;
385
386         ndr_err = ndr_pull_GUID(ndr, NDR_SCALARS | NDR_BUFFERS, &syntax->uuid);
387         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
388                 talloc_free(mem_ctx);
389                 return ndr_map_error2ntstatus(ndr_err);
390         }
391
392         ndr_err = ndr_pull_uint16(ndr, NDR_SCALARS, &if_version);
393         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
394                 talloc_free(mem_ctx);
395                 return ndr_map_error2ntstatus(ndr_err);
396         }
397
398         syntax->if_version = if_version;
399
400         talloc_free(mem_ctx);
401
402         return NT_STATUS_OK;
403 }
404
405 static DATA_BLOB dcerpc_floor_pack_lhs_data(TALLOC_CTX *mem_ctx, const struct ndr_syntax_id *syntax)
406 {
407         DATA_BLOB blob;
408         struct ndr_push *ndr = ndr_push_init_ctx(mem_ctx);
409
410         ndr->flags |= LIBNDR_FLAG_NOALIGN;
411
412         ndr_push_GUID(ndr, NDR_SCALARS | NDR_BUFFERS, &syntax->uuid);
413         ndr_push_uint16(ndr, NDR_SCALARS, syntax->if_version);
414
415         blob = ndr_push_blob(ndr);
416         talloc_steal(mem_ctx, blob.data);
417         talloc_free(ndr);
418         return blob;
419 }
420
421 static DATA_BLOB dcerpc_floor_pack_rhs_if_version_data(TALLOC_CTX *mem_ctx, const struct ndr_syntax_id *syntax)
422 {
423         DATA_BLOB blob;
424         struct ndr_push *ndr = ndr_push_init_ctx(mem_ctx);
425
426         ndr->flags |= LIBNDR_FLAG_NOALIGN;
427
428         ndr_push_uint16(ndr, NDR_SCALARS, syntax->if_version >> 16);
429
430         blob = ndr_push_blob(ndr);
431         talloc_steal(mem_ctx, blob.data);
432         talloc_free(ndr);
433         return blob;
434 }
435
436 const char *dcerpc_floor_get_rhs_data(TALLOC_CTX *mem_ctx, struct epm_floor *epm_floor)
437 {
438         switch (epm_floor->lhs.protocol) {
439         case EPM_PROTOCOL_TCP:
440                 if (epm_floor->rhs.tcp.port == 0) return NULL;
441                 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.tcp.port);
442
443         case EPM_PROTOCOL_UDP:
444                 if (epm_floor->rhs.udp.port == 0) return NULL;
445                 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.udp.port);
446
447         case EPM_PROTOCOL_HTTP:
448                 if (epm_floor->rhs.http.port == 0) return NULL;
449                 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.http.port);
450
451         case EPM_PROTOCOL_IP:
452                 return talloc_strdup(mem_ctx, epm_floor->rhs.ip.ipaddr);
453
454         case EPM_PROTOCOL_NCACN:
455                 return NULL;
456
457         case EPM_PROTOCOL_NCADG:
458                 return NULL;
459
460         case EPM_PROTOCOL_SMB:
461                 if (strlen(epm_floor->rhs.smb.unc) == 0) return NULL;
462                 return talloc_strdup(mem_ctx, epm_floor->rhs.smb.unc);
463
464         case EPM_PROTOCOL_NAMED_PIPE:
465                 if (strlen(epm_floor->rhs.named_pipe.path) == 0) return NULL;
466                 return talloc_strdup(mem_ctx, epm_floor->rhs.named_pipe.path);
467
468         case EPM_PROTOCOL_NETBIOS:
469                 if (strlen(epm_floor->rhs.netbios.name) == 0) return NULL;
470                 return talloc_strdup(mem_ctx, epm_floor->rhs.netbios.name);
471
472         case EPM_PROTOCOL_NCALRPC:
473                 return NULL;
474
475         case EPM_PROTOCOL_VINES_SPP:
476                 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.vines_spp.port);
477
478         case EPM_PROTOCOL_VINES_IPC:
479                 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.vines_ipc.port);
480
481         case EPM_PROTOCOL_STREETTALK:
482                 return talloc_strdup(mem_ctx, epm_floor->rhs.streettalk.streettalk);
483
484         case EPM_PROTOCOL_UNIX_DS:
485                 if (strlen(epm_floor->rhs.unix_ds.path) == 0) return NULL;
486                 return talloc_strdup(mem_ctx, epm_floor->rhs.unix_ds.path);
487
488         case EPM_PROTOCOL_NULL:
489                 return NULL;
490
491         default:
492                 DEBUG(0,("Unsupported lhs protocol %d\n", epm_floor->lhs.protocol));
493                 break;
494         }
495
496         return NULL;
497 }
498
499 static NTSTATUS dcerpc_floor_set_rhs_data(TALLOC_CTX *mem_ctx, 
500                                           struct epm_floor *epm_floor,  
501                                           const char *data)
502 {
503         switch (epm_floor->lhs.protocol) {
504         case EPM_PROTOCOL_TCP:
505                 epm_floor->rhs.tcp.port = atoi(data);
506                 return NT_STATUS_OK;
507
508         case EPM_PROTOCOL_UDP:
509                 epm_floor->rhs.udp.port = atoi(data);
510                 return NT_STATUS_OK;
511
512         case EPM_PROTOCOL_HTTP:
513                 epm_floor->rhs.http.port = atoi(data);
514                 return NT_STATUS_OK;
515
516         case EPM_PROTOCOL_IP:
517                 epm_floor->rhs.ip.ipaddr = talloc_strdup(mem_ctx, data);
518                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.ip.ipaddr);
519                 return NT_STATUS_OK;
520
521         case EPM_PROTOCOL_NCACN:
522                 epm_floor->rhs.ncacn.minor_version = 0;
523                 return NT_STATUS_OK;
524
525         case EPM_PROTOCOL_NCADG:
526                 epm_floor->rhs.ncadg.minor_version = 0;
527                 return NT_STATUS_OK;
528
529         case EPM_PROTOCOL_SMB:
530                 epm_floor->rhs.smb.unc = talloc_strdup(mem_ctx, data);
531                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.smb.unc);
532                 return NT_STATUS_OK;
533
534         case EPM_PROTOCOL_NAMED_PIPE:
535                 epm_floor->rhs.named_pipe.path = talloc_strdup(mem_ctx, data);
536                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.named_pipe.path);
537                 return NT_STATUS_OK;
538
539         case EPM_PROTOCOL_NETBIOS:
540                 epm_floor->rhs.netbios.name = talloc_strdup(mem_ctx, data);
541                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.netbios.name);
542                 return NT_STATUS_OK;
543
544         case EPM_PROTOCOL_NCALRPC:
545                 return NT_STATUS_OK;
546
547         case EPM_PROTOCOL_VINES_SPP:
548                 epm_floor->rhs.vines_spp.port = atoi(data);
549                 return NT_STATUS_OK;
550
551         case EPM_PROTOCOL_VINES_IPC:
552                 epm_floor->rhs.vines_ipc.port = atoi(data);
553                 return NT_STATUS_OK;
554
555         case EPM_PROTOCOL_STREETTALK:
556                 epm_floor->rhs.streettalk.streettalk = talloc_strdup(mem_ctx, data);
557                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.streettalk.streettalk);
558                 return NT_STATUS_OK;
559
560         case EPM_PROTOCOL_UNIX_DS:
561                 epm_floor->rhs.unix_ds.path = talloc_strdup(mem_ctx, data);
562                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.unix_ds.path);
563                 return NT_STATUS_OK;
564
565         case EPM_PROTOCOL_NULL:
566                 return NT_STATUS_OK;
567
568         default:
569                 DEBUG(0,("Unsupported lhs protocol %d\n", epm_floor->lhs.protocol));
570                 break;
571         }
572
573         return NT_STATUS_NOT_SUPPORTED;
574 }
575
576 enum dcerpc_transport_t dcerpc_transport_by_endpoint_protocol(int prot)
577 {
578         int i;
579
580         /* Find a transport that has 'prot' as 4th protocol */
581         for (i=0;i<ARRAY_SIZE(transports);i++) {
582                 if (transports[i].num_protocols >= 2 && 
583                         transports[i].protseq[1] == prot) {
584                         return transports[i].transport;
585                 }
586         }
587
588         /* Unknown transport */
589         return (unsigned int)-1;
590 }
591
592 _PUBLIC_ enum dcerpc_transport_t dcerpc_transport_by_tower(const struct epm_tower *tower)
593 {
594         int i;
595
596         /* Find a transport that matches this tower */
597         for (i=0;i<ARRAY_SIZE(transports);i++) {
598                 int j;
599                 if (transports[i].num_protocols != tower->num_floors - 2) {
600                         continue; 
601                 }
602
603                 for (j = 0; j < transports[i].num_protocols; j++) {
604                         if (transports[i].protseq[j] != tower->floors[j+2].lhs.protocol) {
605                                 break;
606                         }
607                 }
608
609                 if (j == transports[i].num_protocols) {
610                         return transports[i].transport;
611                 }
612         }
613
614         /* Unknown transport */
615         return (unsigned int)-1;
616 }
617
618 _PUBLIC_ const char *derpc_transport_string_by_transport(enum dcerpc_transport_t t)
619 {
620         int i;
621
622         for (i=0; i<ARRAY_SIZE(transports); i++) {
623                 if (t == transports[i].transport) {
624                         return transports[i].name;
625                 }
626         }
627         return NULL;
628 }
629
630 _PUBLIC_ NTSTATUS dcerpc_binding_from_tower(TALLOC_CTX *mem_ctx, 
631                                    struct epm_tower *tower, 
632                                    struct dcerpc_binding **b_out)
633 {
634         NTSTATUS status;
635         struct dcerpc_binding *binding;
636
637         binding = talloc(mem_ctx, struct dcerpc_binding);
638         NT_STATUS_HAVE_NO_MEMORY(binding);
639
640         ZERO_STRUCT(binding->object);
641         binding->options = NULL;
642         binding->host = NULL;
643         binding->target_hostname = NULL;
644         binding->flags = 0;
645         binding->assoc_group_id = 0;
646
647         binding->transport = dcerpc_transport_by_tower(tower);
648
649         if (binding->transport == (unsigned int)-1) {
650                 return NT_STATUS_NOT_SUPPORTED;
651         }
652
653         if (tower->num_floors < 1) {
654                 return NT_STATUS_OK;
655         }
656
657         /* Set object uuid */
658         status = dcerpc_floor_get_lhs_data(&tower->floors[0], &binding->object);
659
660         if (!NT_STATUS_IS_OK(status)) {
661                 DEBUG(1, ("Error pulling object uuid and version: %s", nt_errstr(status)));     
662                 return status;
663         }
664
665         /* Ignore floor 1, it contains the NDR version info */
666
667         binding->options = NULL;
668
669         /* Set endpoint */
670         if (tower->num_floors >= 4) {
671                 binding->endpoint = dcerpc_floor_get_rhs_data(mem_ctx, &tower->floors[3]);
672         } else {
673                 binding->endpoint = NULL;
674         }
675
676         /* Set network address */
677         if (tower->num_floors >= 5) {
678                 binding->host = dcerpc_floor_get_rhs_data(mem_ctx, &tower->floors[4]);
679                 NT_STATUS_HAVE_NO_MEMORY(binding->host);
680                 binding->target_hostname = binding->host;
681         }
682         *b_out = binding;
683         return NT_STATUS_OK;
684 }
685
686 _PUBLIC_ NTSTATUS dcerpc_binding_build_tower(TALLOC_CTX *mem_ctx,
687                                              const struct dcerpc_binding *binding,
688                                              struct epm_tower *tower)
689 {
690         const enum epm_protocol *protseq = NULL;
691         int num_protocols = -1, i;
692         NTSTATUS status;
693
694         /* Find transport */
695         for (i=0;i<ARRAY_SIZE(transports);i++) {
696                 if (transports[i].transport == binding->transport) {
697                         protseq = transports[i].protseq;
698                         num_protocols = transports[i].num_protocols;
699                         break;
700                 }
701         }
702
703         if (num_protocols == -1) {
704                 DEBUG(0, ("Unable to find transport with id '%d'\n", binding->transport));
705                 return NT_STATUS_UNSUCCESSFUL;
706         }
707
708         tower->num_floors = 2 + num_protocols;
709         tower->floors = talloc_array(mem_ctx, struct epm_floor, tower->num_floors);
710
711         /* Floor 0 */
712         tower->floors[0].lhs.protocol = EPM_PROTOCOL_UUID;
713
714         tower->floors[0].lhs.lhs_data = dcerpc_floor_pack_lhs_data(tower->floors, &binding->object);
715
716         tower->floors[0].rhs.uuid.unknown = dcerpc_floor_pack_rhs_if_version_data(tower->floors, &binding->object);
717
718         /* Floor 1 */
719         tower->floors[1].lhs.protocol = EPM_PROTOCOL_UUID;
720
721         tower->floors[1].lhs.lhs_data = dcerpc_floor_pack_lhs_data(tower->floors, 
722                                                                 &ndr_transfer_syntax);
723
724         tower->floors[1].rhs.uuid.unknown = data_blob_talloc_zero(tower->floors, 2);
725
726         /* Floor 2 to num_protocols */
727         for (i = 0; i < num_protocols; i++) {
728                 tower->floors[2 + i].lhs.protocol = protseq[i];
729                 tower->floors[2 + i].lhs.lhs_data = data_blob_talloc(tower->floors, NULL, 0);
730                 ZERO_STRUCT(tower->floors[2 + i].rhs);
731                 dcerpc_floor_set_rhs_data(tower->floors, &tower->floors[2 + i], "");
732         }
733
734         /* The 4th floor contains the endpoint */
735         if (num_protocols >= 2 && binding->endpoint) {
736                 status = dcerpc_floor_set_rhs_data(tower->floors, &tower->floors[3], binding->endpoint);
737                 if (NT_STATUS_IS_ERR(status)) {
738                         return status;
739                 }
740         }
741
742         /* The 5th contains the network address */
743         if (num_protocols >= 3 && binding->host) {
744                 if (is_ipaddress(binding->host)) {
745                         status = dcerpc_floor_set_rhs_data(tower->floors, &tower->floors[4], 
746                                                            binding->host);
747                 } else {
748                         /* note that we don't attempt to resolve the
749                            name here - when we get a hostname here we
750                            are in the client code, and want to put in
751                            a wildcard all-zeros IP for the server to
752                            fill in */
753                         status = dcerpc_floor_set_rhs_data(tower->floors, &tower->floors[4], 
754                                                            "0.0.0.0");
755                 }
756                 if (NT_STATUS_IS_ERR(status)) {
757                         return status;
758                 }
759         }
760
761         return NT_STATUS_OK;
762 }