summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorRuss Allbery <rra@stanford.edu>2011-10-31 15:18:46 -0700
committerRuss Allbery <rra@stanford.edu>2011-10-31 15:34:57 -0700
commit360c60dd29ef9ece3bb2d8f20015e5063b6f95d3 (patch)
treecfe9b00bd2b9352f5b16733f877bfb3d3e1f322d /client
parentab300c2b6be6ab5492dd6bf938c9d35f61f936a9 (diff)
Add remctl_noop to the remctl client library API
Diffstat (limited to 'client')
-rw-r--r--client/api.c57
-rw-r--r--client/client-v2.c116
-rw-r--r--client/internal.h3
-rw-r--r--client/libremctl.map1
-rw-r--r--client/libremctl.sym1
-rw-r--r--client/remctl.h12
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.