+ switch (rpc->s.ss_family) {
+ case AF_INET:
+ rpc_port = *(uint32_t *)command_data;
+ break;
+ case AF_INET6:
+ /* ouch. portmapper and ipv6 are not great */
+ gar = command_data;
+ if (gar->addr == NULL) {
+ break;
+ }
+ ptr = strrchr(gar->addr, '.');
+ if (ptr == NULL) {
+ break;
+ }
+ rpc_port = atoi(ptr + 1);
+ *ptr = 0;
+ ptr = strrchr(gar->addr, '.');
+ if (ptr == NULL) {
+ break;
+ }
+ rpc_port += 256 * atoi(ptr + 1);
+ break;
+ }
+ if (rpc_port == 0) {
+ rpc_set_error(rpc, "RPC error. Program is not available on %s", data->server);
+ data->cb(rpc, RPC_STATUS_ERROR, rpc_get_error(rpc), data->private_data);
+ free_rpc_cb_data(data);
+ return;
+ }
+
+ rpc_disconnect(rpc, "normal disconnect");
+ if (rpc_connect_async(rpc, data->server, rpc_port, rpc_connect_program_4_cb, data) != 0) {
+ data->cb(rpc, status, command_data, data->private_data);
+ free_rpc_cb_data(data);
+ return;
+ }
+}
+
+static void rpc_connect_program_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+ struct rpc_cb_data *data = private_data;
+ struct pmap3_mapping map;
+
+ assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(rpc, status, command_data, data->private_data);
+ free_rpc_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(rpc, status, "Command was cancelled", data->private_data);
+ free_rpc_cb_data(data);
+ return;
+ }
+
+ switch (rpc->s.ss_family) {
+ case AF_INET:
+ if (rpc_pmap2_getport_async(rpc, data->program, data->version, IPPROTO_TCP, rpc_connect_program_3_cb, private_data) != 0) {
+ data->cb(rpc, status, command_data, data->private_data);
+ free_rpc_cb_data(data);
+ return;
+ }
+ break;
+ case AF_INET6:
+ map.prog=data->program;
+ map.vers=data->version;
+ map.netid="";
+ map.addr="";
+ map.owner="";
+ if (rpc_pmap3_getaddr_async(rpc, &map, rpc_connect_program_3_cb, private_data) != 0) {
+ data->cb(rpc, status, command_data, data->private_data);
+ free_rpc_cb_data(data);
+ return;
+ }
+ break;
+ }
+}
+
+static void rpc_connect_program_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+ struct rpc_cb_data *data = private_data;
+
+ assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+ /* Dont want any more callbacks even if the socket is closed */
+ rpc->connect_cb = NULL;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(rpc, status, command_data, data->private_data);
+ free_rpc_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(rpc, status, "Command was cancelled", data->private_data);
+ free_rpc_cb_data(data);
+ return;
+ }
+
+ switch (rpc->s.ss_family) {
+ case AF_INET:
+ if (rpc_pmap2_null_async(rpc, rpc_connect_program_2_cb, data) != 0) {
+ data->cb(rpc, status, command_data, data->private_data);
+ free_rpc_cb_data(data);
+ return;
+ }
+ break;
+ case AF_INET6:
+ if (rpc_pmap3_null_async(rpc, rpc_connect_program_2_cb, data) != 0) {
+ data->cb(rpc, status, command_data, data->private_data);
+ free_rpc_cb_data(data);
+ return;
+ }
+ break;
+ }
+}
+
+int rpc_connect_program_async(struct rpc_context *rpc, const char *server, int program, int version, rpc_cb cb, void *private_data)
+{
+ struct rpc_cb_data *data;
+
+ data = malloc(sizeof(struct rpc_cb_data));
+ if (data == NULL) {
+ return -1;
+ }
+ memset(data, 0, sizeof(struct rpc_cb_data));
+ data->server = strdup(server);
+ data->program = program;
+ data->version = version;
+
+ data->cb = cb;
+ data->private_data = private_data;
+
+ if (rpc_connect_async(rpc, server, 111, rpc_connect_program_1_cb, data) != 0) {
+ rpc_set_error(rpc, "Failed to start connection");
+ free_rpc_cb_data(data);
+ return -1;
+ }
+ return 0;