diff options
author | Russ Allbery <rra@stanford.edu> | 2011-10-31 15:18:46 -0700 |
---|---|---|
committer | Russ Allbery <rra@stanford.edu> | 2011-10-31 15:34:57 -0700 |
commit | 360c60dd29ef9ece3bb2d8f20015e5063b6f95d3 (patch) | |
tree | cfe9b00bd2b9352f5b16733f877bfb3d3e1f322d /client | |
parent | ab300c2b6be6ab5492dd6bf938c9d35f61f936a9 (diff) |
Add remctl_noop to the remctl client library API
Diffstat (limited to 'client')
-rw-r--r-- | client/api.c | 57 | ||||
-rw-r--r-- | client/client-v2.c | 116 | ||||
-rw-r--r-- | client/internal.h | 3 | ||||
-rw-r--r-- | client/libremctl.map | 1 | ||||
-rw-r--r-- | client/libremctl.sym | 1 | ||||
-rw-r--r-- | client/remctl.h | 12 |
6 files changed, 150 insertions, 40 deletions
diff --git a/client/api.c b/client/api.c index c4e6c06..ba5cb13 100644 --- a/client/api.c +++ b/client/api.c @@ -344,6 +344,31 @@ remctl_close(struct remctl *r) /* + * Internal function to reopen the connection if it was closed and verify that + * we have an open connection, and reset the error message. Used by + * remctl_commandv and remctl_noop. Returns true on success and false on + * failure. + */ +static bool +internal_reopen(struct remctl *r) +{ + if (r->fd < 0) { + if (r->host == NULL) { + internal_set_error(r, "no connection open"); + return false; + } + if (!remctl_open(r, r->host, r->port, r->principal)) + return false; + } + if (r->error != NULL) { + free(r->error); + r->error = NULL; + } + return true; +} + + +/* * Send a complete remote command. Returns true on success, false on failure. * On failure, use remctl_error to get the error. command is a * NULL-terminated array of nul-terminated strings. finished is a boolean @@ -383,18 +408,8 @@ remctl_command(struct remctl *r, const char **command) int remctl_commandv(struct remctl *r, const struct iovec *command, size_t count) { - if (r->fd < 0) { - if (r->host == NULL) { - internal_set_error(r, "no connection open"); - return 0; - } - if (!remctl_open(r, r->host, r->port, r->principal)) - return 0; - } - if (r->error != NULL) { - free(r->error); - r->error = NULL; - } + if (!internal_reopen(r)) + return 0; if (r->protocol == 1) return internal_v1_commandv(r, command, count); else @@ -403,6 +418,24 @@ remctl_commandv(struct remctl *r, const struct iovec *command, size_t count) /* + * Send a NOOP command, or return an error if we're using too old of a + * protocol version. Returns true on success, false on failure. On failure, + * use remctl_error to get the error. + */ +int +remctl_noop(struct remctl *r) +{ + if (!internal_reopen(r)) + return 0; + if (r->protocol == 1) { + internal_set_error(r, "NOOP message not supported"); + return 0; + } + return internal_noop(r); +} + + +/* * Helper function for remctl_output implementations. Free and reset the * elements of the output struct, but don't free the output struct itself. */ diff --git a/client/client-v2.c b/client/client-v2.c index 9aa59dc..aff163d 100644 --- a/client/client-v2.c +++ b/client/client-v2.c @@ -180,7 +180,7 @@ internal_v2_quit(struct remctl *r) status = token_send_priv(r->fd, r->context, TOKEN_DATA | TOKEN_PROTOCOL, &token, &major, &minor); if (status != TOKEN_OK) { - internal_token_error(r, "sending token", status, major, minor); + internal_token_error(r, "sending QUIT token", status, major, minor); return false; } return true; @@ -188,6 +188,48 @@ internal_v2_quit(struct remctl *r) /* + * Read a token from the server connection and store it in the provided + * buffer. Return true on success and false on any failure. + */ +static bool +internal_v2_read_token(struct remctl *r, gss_buffer_t token) +{ + int status, flags; + OM_uint32 major, minor; + char *p; + + status = token_recv_priv(r->fd, r->context, &flags, token, + TOKEN_MAX_LENGTH, &major, &minor); + if (status != TOKEN_OK) { + internal_token_error(r, "receiving token", status, major, minor); + if (status == TOKEN_FAIL_EOF) { + socket_close(r->fd); + r->fd = INVALID_SOCKET; + } + return false; + } + if (flags != (TOKEN_DATA | TOKEN_PROTOCOL)) { + internal_set_error(r, "unexpected token from server"); + goto fail; + } + if (token->length < 2) { + internal_set_error(r, "malformed result token from server"); + goto fail; + } + p = token->value; + if (p[0] != 2 && p[0] != 3) { + internal_set_error(r, "unexpected protocol %d from server", p[0]); + goto fail; + } + return true; + +fail: + gss_release_buffer(&minor, token); + return false; +} + + +/* * Read a string from a server token, with its length starting at the given * offset, and store it in newly allocated memory in the remctl struct. * Returns true on success and false on any failure (also setting the error). @@ -228,9 +270,8 @@ internal_v2_read_string(struct remctl *r, gss_buffer_t token, size_t offset) struct remctl_output * internal_v2_output(struct remctl *r) { - int status, flags; - gss_buffer_desc token; - OM_uint32 data, major, minor; + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 data, minor; char *p; int type; @@ -252,34 +293,12 @@ internal_v2_output(struct remctl *r) return r->output; /* Otherwise, we have to read the token from the server. */ - status = token_recv_priv(r->fd, r->context, &flags, &token, - TOKEN_MAX_LENGTH, &major, &minor); - if (status != TOKEN_OK) { - internal_token_error(r, "receiving token", status, major, minor); - if (status == TOKEN_FAIL_EOF) { - socket_close(r->fd); - r->fd = INVALID_SOCKET; - } + if (!internal_v2_read_token(r, &token)) return NULL; - } - if (flags != (TOKEN_DATA | TOKEN_PROTOCOL)) { - internal_set_error(r, "unexpected token from server"); - goto fail; - } - if (token.length < 2) { - internal_set_error(r, "malformed result token from server"); - goto fail; - } - /* Extract the message protocol and type. */ + /* Now, what we do depends on the message type. */ p = token.value; - if (p[0] != 2) { - internal_set_error(r, "unexpected protocol %d from server", p[0]); - goto fail; - } type = p[1]; - - /* Now, what we do depends on the message type. */ switch (type) { case MESSAGE_OUTPUT: if (token.length < 2 + 5) { @@ -332,3 +351,44 @@ fail: gss_release_buffer(&minor, &token); return NULL; } + + +/* + * Send a NOOP command to the server using protocol v3 and read the response. + * Returns true on success, false on failure. + */ +bool +internal_noop(struct remctl *r) +{ + gss_buffer_desc token; + char buffer[2] = { 3, MESSAGE_NOOP }; + OM_uint32 major, minor; + int status; + char *p; + + /* Send the NOOP token. */ + token.length = 1 + 1; + token.value = buffer; + status = token_send_priv(r->fd, r->context, TOKEN_DATA | TOKEN_PROTOCOL, + &token, &major, &minor); + if (status != TOKEN_OK) { + internal_token_error(r, "sending NOOP token", status, major, minor); + return false; + } + + /* Read the resulting NOOP token. */ + token.length = 0; + token.value = GSS_C_NO_BUFFER; + if (!internal_v2_read_token(r, &token)) + return false; + p = token.value; + if (p[1] != MESSAGE_NOOP) { + internal_set_error(r, "unexpected message type %d from server", p[1]); + gss_release_buffer(&minor, &token); + return false; + } + gss_release_buffer(&minor, &token); + + /* Everything looks good. */ + return true; +} diff --git a/client/internal.h b/client/internal.h index 3c02734..9251777 100644 --- a/client/internal.h +++ b/client/internal.h @@ -67,6 +67,9 @@ struct remctl_output *internal_v1_output(struct remctl *); bool internal_v2_commandv(struct remctl *, const struct iovec *command, size_t count); +/* Send a protocol v3 NOOP command. */ +bool internal_noop(struct remctl *); + /* Send a protocol v2 QUIT command. */ bool internal_v2_quit(struct remctl *); diff --git a/client/libremctl.map b/client/libremctl.map index bba3513..287de0f 100644 --- a/client/libremctl.map +++ b/client/libremctl.map @@ -6,6 +6,7 @@ REMCTL_1.0 { remctl_commandv; remctl_error; remctl_new; + remctl_noop; remctl_open; remctl_output; remctl_result_free; diff --git a/client/libremctl.sym b/client/libremctl.sym index 5fa1740..091e39c 100644 --- a/client/libremctl.sym +++ b/client/libremctl.sym @@ -4,6 +4,7 @@ remctl_command remctl_commandv remctl_error remctl_new +remctl_noop remctl_open remctl_output remctl_result_free diff --git a/client/remctl.h b/client/remctl.h index b91ecee..56f4ec5 100644 --- a/client/remctl.h +++ b/client/remctl.h @@ -155,6 +155,18 @@ int remctl_command(struct remctl *, const char **command); int remctl_commandv(struct remctl *, const struct iovec *, size_t count); /* + * Send a NOOP message to the server and read the NOOP reply. This is + * normally used to keep a connection alive (through a firewall with timeouts, + * for example) while awaiting subsequent commands. Returns true on success + * and false on failure. On failure, use remctl_error to get the error. + * + * This is a protocol version 3 message and requires a server that supports + * it, so the caller should be prepared to handle an error return and fall + * back on reopening the connection when necessary. + */ +int remctl_noop(struct remctl *); + +/* * Retrieve output from the remote server. Each call to this function on the * same connection invalidates the previous returned remctl_output struct, so * copy any data that should be persistant before calling this function again. |