Add unique IP address binding for client connections (EPM and ncacn_ip_tcp levels)
authorJulien Kerihuel <j.kerihuel@openchange.org>
Fri, 27 Aug 2010 12:04:07 +0000 (14:04 +0200)
committerAndrew Tridgell <tridge@samba.org>
Mon, 6 Sep 2010 23:55:14 +0000 (09:55 +1000)
This allows for binding strings like this:

  ncacn_ip_tcp:host[localaddress=192.168.2.1,seal]

which will force the connection to be locally bound to the specified
IP address

Signed-off-by: Andrew Tridgell <tridge@samba.org>
librpc/rpc/binding.c
source3/librpc/rpc/dcerpc.h
source4/librpc/rpc/dcerpc.h
source4/librpc/rpc/dcerpc_connect.c
source4/librpc/rpc/dcerpc_secondary.c
source4/librpc/rpc/dcerpc_sock.c
source4/librpc/rpc/dcerpc_util.c
source4/librpc/tests/binding_string.c

index 5e9bef8e7c370d464aa75ff82f4342cc8415104a..feff5128ac77f3e1181052cbf73617c95931e419 100644 (file)
@@ -86,7 +86,8 @@ static const struct {
        {"bigendian", DCERPC_PUSH_BIGENDIAN},
        {"smb2", DCERPC_SMB2},
        {"hdrsign", DCERPC_HEADER_SIGNING},
-       {"ndr64", DCERPC_NDR64}
+       {"ndr64", DCERPC_NDR64},
+       {"localaddress", DCERPC_LOCALADDRESS}
 };
 
 const char *epm_floor_string(TALLOC_CTX *mem_ctx, struct epm_floor *epm_floor)
@@ -220,7 +221,12 @@ _PUBLIC_ char *dcerpc_binding_string(TALLOC_CTX *mem_ctx, const struct dcerpc_bi
 
        for (i=0;i<ARRAY_SIZE(ncacn_options);i++) {
                if (b->flags & ncacn_options[i].flag) {
-                       s = talloc_asprintf_append_buffer(s, ",%s", ncacn_options[i].name);
+                       if (ncacn_options[i].flag == DCERPC_LOCALADDRESS && b->localaddress) {
+                               s = talloc_asprintf_append_buffer(s, ",%s=%s", ncacn_options[i].name,
+                                                                 b->localaddress);
+                       } else {
+                               s = talloc_asprintf_append_buffer(s, ",%s", ncacn_options[i].name);
+                       }
                        if (!s) return NULL;
                }
        }
@@ -313,6 +319,7 @@ _PUBLIC_ NTSTATUS dcerpc_parse_binding(TALLOC_CTX *mem_ctx, const char *s, struc
        b->flags = 0;
        b->assoc_group_id = 0;
        b->endpoint = NULL;
+       b->localaddress = NULL;
 
        if (!options) {
                *b_out = b;
@@ -339,8 +346,17 @@ _PUBLIC_ NTSTATUS dcerpc_parse_binding(TALLOC_CTX *mem_ctx, const char *s, struc
        /* some options are pre-parsed for convenience */
        for (i=0;b->options[i];i++) {
                for (j=0;j<ARRAY_SIZE(ncacn_options);j++) {
-                       if (strcasecmp(ncacn_options[j].name, b->options[i]) == 0) {
+                       size_t opt_len = strlen(ncacn_options[j].name);
+                       if (strncasecmp(ncacn_options[j].name, b->options[i], opt_len) == 0) {
                                int k;
+                               char c = b->options[i][opt_len];
+
+                               if (ncacn_options[j].flag == DCERPC_LOCALADDRESS && c == '=') {
+                                       b->localaddress = talloc_strdup(b, &b->options[i][opt_len+1]);
+                               } else if (c != 0) {
+                                       continue;
+                               }
+
                                b->flags |= ncacn_options[j].flag;
                                for (k=i;b->options[k];k++) {
                                        b->options[k] = b->options[k+1];
index af20889b1e0a498678cef28ba79a83045b26f9f3..56d6d320fdeb7fa64a4d7544bee67ae52106e375 100644 (file)
@@ -41,6 +41,7 @@ struct dcerpc_binding {
        const char *target_hostname;
        const char *endpoint;
        const char **options;
+       const char *localaddress;
        uint32_t flags;
        uint32_t assoc_group_id;
 };
@@ -95,6 +96,9 @@ struct dcerpc_binding {
 /* use NDR64 transport */
 #define DCERPC_NDR64                   (1<<21)
 
+/* specify binding interface */
+#define        DCERPC_LOCALADDRESS            (1<<22)
+
 /* The following definitions come from librpc/rpc/binding.c  */
 
 const char *epm_floor_string(TALLOC_CTX *mem_ctx, struct epm_floor *epm_floor);
index b77628d8d7c3e2b2ede6bae234e6c7c3304c28d4..b5062a55e3e0fb0aa5181bfcc16aa99ef56906af 100644 (file)
@@ -181,6 +181,9 @@ struct dcerpc_pipe {
 /* use NDR64 transport */
 #define DCERPC_NDR64                   (1<<21)
 
+/* specify binding interface */
+#define        DCERPC_LOCALADDRESS            (1<<22)
+
 /* this describes a binding to a particular transport/pipe */
 struct dcerpc_binding {
        enum dcerpc_transport_t transport;
@@ -189,6 +192,7 @@ struct dcerpc_binding {
        const char *target_hostname;
        const char *endpoint;
        const char **options;
+       const char *localaddress;
        uint32_t flags;
        uint32_t assoc_group_id;
 };
index 7779d31f9924c7a6e5acf5aee80f1f75eec52645..0f3a86256c8dbb1893f629f55cf8e5aa79081fd0 100644 (file)
@@ -276,6 +276,7 @@ static NTSTATUS dcerpc_pipe_connect_ncacn_np_smb2_recv(struct composite_context
 
 struct pipe_ip_tcp_state {
        struct dcerpc_pipe_connect io;
+       const char *localaddr;
        const char *host;
        const char *target_hostname;
        uint32_t port;
@@ -319,13 +320,14 @@ static struct composite_context* dcerpc_pipe_connect_ncacn_ip_tcp_send(TALLOC_CT
 
        /* store input parameters in state structure */
        s->io               = *io;
+       s->localaddr        = talloc_reference(c, io->binding->localaddress);
        s->host             = talloc_reference(c, io->binding->host);
        s->target_hostname  = talloc_reference(c, io->binding->target_hostname);
                              /* port number is a binding endpoint here */
        s->port             = atoi(io->binding->endpoint);   
 
        /* send pipe open request on tcp/ip */
-       pipe_req = dcerpc_pipe_open_tcp_send(s->io.pipe->conn, s->host, s->target_hostname, 
+       pipe_req = dcerpc_pipe_open_tcp_send(s->io.pipe->conn, s->localaddr, s->host, s->target_hostname,
                                             s->port, io->resolve_ctx);
        composite_continue(c, pipe_req, continue_pipe_open_ncacn_ip_tcp, c);
        return c;
index 5f355a59379c7a164f99b47570e5f39cfeed4f73..65466e45e03f944258a5f9a9895bb3295e9b733b 100644 (file)
@@ -102,6 +102,7 @@ _PUBLIC_ struct composite_context* dcerpc_secondary_connection_send(struct dcerp
                }
 
                pipe_tcp_req = dcerpc_pipe_open_tcp_send(s->pipe2->conn,
+                                                        s->binding->localaddress,
                                                         s->peer_addr->addr,
                                                         s->binding->target_hostname,
                                                         atoi(s->binding->endpoint),
index d8bd6d29380d04d89843b557e242c1a0d81a06bf..4ab8c350914774c591b277e02a7fb242864847e8 100644 (file)
@@ -228,6 +228,7 @@ struct pipe_open_socket_state {
        struct dcerpc_connection *conn;
        struct socket_context *socket_ctx;
        struct sock_private *sock;
+       struct socket_address *localaddr;
        struct socket_address *server;
        const char *target_hostname;
        enum dcerpc_transport_t transport;
@@ -305,6 +306,7 @@ static void continue_socket_connect(struct composite_context *ctx)
 
 static struct composite_context *dcerpc_pipe_open_socket_send(TALLOC_CTX *mem_ctx,
                                                       struct dcerpc_connection *cn,
+                                                      struct socket_address *localaddr,
                                                       struct socket_address *server,
                                                       const char *target_hostname,
                                                       const char *full_path,
@@ -323,6 +325,10 @@ static struct composite_context *dcerpc_pipe_open_socket_send(TALLOC_CTX *mem_ct
 
        s->conn      = cn;
        s->transport = transport;
+       if (localaddr) {
+               s->localaddr = talloc_reference(c, localaddr);
+               if (composite_nomem(s->localaddr, c)) return c;
+       }
        s->server    = talloc_reference(c, server);
        if (composite_nomem(s->server, c)) return c;
        s->target_hostname = talloc_reference(s, target_hostname);
@@ -337,7 +343,7 @@ static struct composite_context *dcerpc_pipe_open_socket_send(TALLOC_CTX *mem_ct
 
        s->sock->path = talloc_reference(s->sock, full_path);
 
-       conn_req = socket_connect_send(s->socket_ctx, NULL, s->server, 0, 
+       conn_req = socket_connect_send(s->socket_ctx, s->localaddr, s->server, 0,
                                       c->event_ctx);
        composite_continue(c, conn_req, continue_socket_connect, c);
        return c;
@@ -357,6 +363,7 @@ struct pipe_tcp_state {
        const char *target_hostname;
        const char *address;
        uint32_t port;
+       struct socket_address *localaddr;
        struct socket_address *srvaddr;
        struct resolve_context *resolve_ctx;
        struct dcerpc_connection *conn;
@@ -385,7 +392,7 @@ static void continue_ip_resolve_name(struct composite_context *ctx)
        if (composite_nomem(s->srvaddr, c)) return;
 
        /* resolve_nbt_name gives only ipv4 ... - send socket open request */
-       sock_ipv4_req = dcerpc_pipe_open_socket_send(c, s->conn,
+       sock_ipv4_req = dcerpc_pipe_open_socket_send(c, s->conn, s->localaddr,
                                                     s->srvaddr, s->target_hostname,
                                                     NULL,
                                                     NCACN_IP_TCP);
@@ -419,7 +426,7 @@ static void continue_ipv6_open_socket(struct composite_context *ctx)
        if (composite_nomem(s->srvaddr, c)) return;
 
        /* try IPv4 if IPv6 fails */
-       sock_ipv4_req = dcerpc_pipe_open_socket_send(c, s->conn, 
+       sock_ipv4_req = dcerpc_pipe_open_socket_send(c, s->conn, s->localaddr,
                                                     s->srvaddr, s->target_hostname, 
                                                     NCACN_IP_TCP);
        composite_continue(c, sock_ipv4_req, continue_ipv4_open_socket, c);
@@ -452,12 +459,12 @@ static void continue_ipv4_open_socket(struct composite_context *ctx)
        composite_done(c);
 }
 
-
 /*
   Send rpc pipe open request to given host:port using
   tcp/ip transport
 */
 struct composite_context* dcerpc_pipe_open_tcp_send(struct dcerpc_connection *conn,
+                                                   const char *localaddr,
                                                    const char *server,
                                                    const char *target_hostname,
                                                    uint32_t port,
@@ -486,6 +493,12 @@ struct composite_context* dcerpc_pipe_open_tcp_send(struct dcerpc_connection *co
        s->port            = port;
        s->conn            = conn;
        s->resolve_ctx     = resolve_ctx;
+       if (localaddr) {
+               s->localaddr = socket_address_from_strings(s, "ip", localaddr, 0);
+               /* if there is no localaddr, we pass NULL for
+                  s->localaddr, which is handled by the socket libraries as
+                  meaning no local binding address specified */
+       }
 
        make_nbt_name_server(&name, server);
        resolve_req = resolve_name_send(resolve_ctx, s, &name, c->event_ctx);
@@ -560,7 +573,7 @@ struct composite_context *dcerpc_pipe_open_unix_stream_send(struct dcerpc_connec
        if (composite_nomem(s->srvaddr, c)) return c;
 
        /* send socket open request */
-       sock_unix_req = dcerpc_pipe_open_socket_send(c, s->conn, 
+       sock_unix_req = dcerpc_pipe_open_socket_send(c, s->conn, NULL,
                                                     s->srvaddr, NULL,
                                                     s->path,
                                                     NCALRPC);
@@ -631,7 +644,7 @@ struct composite_context* dcerpc_pipe_open_pipe_send(struct dcerpc_connection *c
        if (composite_nomem(s->srvaddr, c)) return c;
 
        /* send socket open request */
-       sock_np_req = dcerpc_pipe_open_socket_send(c, s->conn, s->srvaddr, NULL, s->path, NCALRPC);
+       sock_np_req = dcerpc_pipe_open_socket_send(c, s->conn, NULL, s->srvaddr, NULL, s->path, NCALRPC);
        composite_continue(c, sock_np_req, continue_np_open_socket, c);
        return c;
 }
index ffe8506d2112f42dc5e171a8ef401aeec5c21182..d27b0f3f5bc7798958decc76a6423482d999863f 100644 (file)
@@ -285,6 +285,7 @@ struct composite_context *dcerpc_epm_map_binding_send(TALLOC_CTX *mem_ctx,
        epmapper_binding->host                  = talloc_reference(epmapper_binding, binding->host);
        epmapper_binding->target_hostname       = epmapper_binding->host;
        epmapper_binding->options               = NULL;
+       epmapper_binding->localaddress          = talloc_reference(epmapper_binding, binding->localaddress);
        epmapper_binding->flags                 = 0;
        epmapper_binding->assoc_group_id        = 0;
        epmapper_binding->endpoint              = NULL;
index 6de94eb58b401fe336ed4f47fc7084e7fe86e2a8..dfb6d2967aa07da937e9a68baf875b4be8ab42c9 100644 (file)
@@ -128,6 +128,12 @@ static bool test_parse_check_results(struct torture_context *tctx)
        torture_assert_int_equal(tctx, b->object.if_version, 0, "object version");
        torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx, 
                "308FB580-1EB2-11CA-923B-08002B1075A7@ncacn_ip_tcp:$SERVER", &b), "parse");
+       torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx, "ncacn_ip_tcp:$SERVER[,sign,localaddress=192.168.1.1]", &b), "parse");
+       torture_assert(tctx, b->transport == NCACN_IP_TCP, "ncacn_ip_tcp expected");
+       torture_assert(tctx, b->flags == (DCERPC_SIGN | DCERPC_LOCALADDRESS), "sign flag");
+       torture_assert_str_equal(tctx, b->localaddress, "192.168.1.1", "localaddress");
+       torture_assert_str_equal(tctx, "ncacn_ip_tcp:$SERVER[,sign,localaddress=192.168.1.1]",
+                                dcerpc_binding_string(tctx, b), "back to string");
 
        return true;
 }