source: web/old/remctl-2.14/client/client-v1.c @ f6f3e91

web
Last change on this file since f6f3e91 was f6f3e91, checked in by Jessica B. Hamrick <jhamrick@…>, 15 years ago

Preserve directory hierarchy (not sure what happened to it)

  • Property mode set to 100644
File size: 5.5 KB
Line 
1/*
2 * Protocol v1, client implementation.
3 *
4 * This is the client implementation of the old v1 protocol, which doesn't
5 * support streaming, keep-alive, or many of the other features of the current
6 * protocol.  We shoehorn this protocol into the same API as the new protocol
7 * so that clients don't have to care, but some functions (like continued
8 * commands) will return unimplemented errors.
9 *
10 * Written by Russ Allbery <rra@stanford.edu>
11 * Based on work by Anton Ushakov
12 * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008
13 *     Board of Trustees, Leland Stanford Jr. University
14 *
15 * See LICENSE for licensing terms.
16 */
17
18#include <config.h>
19#include <portable/system.h>
20#include <portable/gssapi.h>
21#include <portable/socket.h>
22#include <portable/uio.h>
23
24#include <errno.h>
25
26#include <client/internal.h>
27#include <client/remctl.h>
28#include <util/util.h>
29
30
31/*
32 * Send a command to the server using protocol v1.  Returns true on success,
33 * false on failure.
34 */
35bool
36internal_v1_commandv(struct remctl *r, const struct iovec *command,
37                     size_t count)
38{
39    gss_buffer_desc token;
40    size_t i;
41    char *p;
42    OM_uint32 data, major, minor;
43    int status;
44
45    /* Allocate room for the total message: argc, {<length><arg>}+. */
46    token.length = 4;
47    for (i = 0; i < count; i++)
48        token.length += 4 + command[i].iov_len;
49    token.value = malloc(token.length);
50    if (token.value == NULL) {
51        internal_set_error(r, "cannot allocate memory: %s", strerror(errno));
52        return false;
53    }
54
55    /* First, the argument count.  Then, each argument. */
56    p = token.value;
57    data = htonl(count);
58    memcpy(p, &data, 4);
59    p += 4;
60    for (i = 0; i < count; i++) {
61        data = htonl(command[i].iov_len);
62        memcpy(p, &data, 4);
63        p += 4;
64        memcpy(p, command[i].iov_base, command[i].iov_len);
65        p += command[i].iov_len;
66    }
67
68    /* Send the result. */
69    status = token_send_priv(r->fd, r->context, TOKEN_DATA | TOKEN_SEND_MIC,
70                             &token, &major, &minor);
71    if (status != TOKEN_OK) {
72        internal_token_error(r, "sending token", status, major, minor);
73        free(token.value);
74        return false;
75    }
76    free(token.value);
77    r->ready = true;
78    return true;
79}
80
81
82/*
83 * Retrieve the output from the server using protocol version one and return
84 * it.  This function will actually be called twice, once to retrieve the
85 * output data and once to retrieve the exit status.  The old protocol
86 * returned those together in one message, so we have to buffer the exit
87 * status and return it on the second call.  Returns a remctl output struct on
88 * success and NULL on failure.
89 */
90struct remctl_output *
91internal_v1_output(struct remctl *r)
92{
93    int status, flags;
94    gss_buffer_desc token;
95    OM_uint32 data, major, minor, length;
96    char *p;
97
98    /*
99     * First, see if we already had an output struct.  If so, this is the
100     * second call and we should just return the exit status.
101     */
102    if (r->output != NULL && !r->ready) {
103        if (r->output->type == REMCTL_OUT_STATUS)
104            r->output->type = REMCTL_OUT_DONE;
105        else {
106            internal_output_wipe(r->output);
107            r->output->type = REMCTL_OUT_STATUS;
108        }
109        r->output->status = r->status;
110        return r->output;
111    }
112
113    /* Otherwise, we have to read the token from the server. */
114    status = token_recv_priv(r->fd, r->context, &flags, &token,
115                             TOKEN_MAX_LENGTH, &major, &minor);
116    if (status != TOKEN_OK) {
117        internal_token_error(r, "receiving token", status, major, minor);
118        if (status == TOKEN_FAIL_EOF) {
119            socket_close(r->fd);
120            r->fd = -1;
121        }
122        return NULL;
123    }
124    if (flags != TOKEN_DATA) {
125        internal_set_error(r, "unexpected token from server");
126        gss_release_buffer(&minor, &token);
127        return NULL;
128    }
129
130    /* Extract the return code, message length, and data. */
131    if (token.length < 8) {
132        internal_set_error(r, "malformed result token from server");
133        gss_release_buffer(&minor, &token);
134        return NULL;
135    }
136    p = token.value;
137    memcpy(&data, p, 4);
138    r->status = ntohl(data);
139    p += 4;
140    memcpy(&data, p, 4);
141    length = ntohl(data);
142    p += 4;
143    if (length != token.length - 8) {
144        internal_set_error(r, "malformed result token from server");
145        gss_release_buffer(&minor, &token);
146        return NULL;
147    }
148
149    /*
150     * Allocate the new output token.  We make another copy of the data,
151     * unfortunately, so that we don't have to keep the token around to free
152     * later.
153     */
154    r->output = malloc(sizeof(struct remctl_output));
155    if (r->output == NULL) {
156        internal_set_error(r, "cannot allocate memory: %s", strerror(errno));
157        gss_release_buffer(&minor, &token);
158        return NULL;
159    }
160    r->output->type = REMCTL_OUT_OUTPUT;
161    r->output->data = malloc(length);
162    if (r->output->data == NULL) {
163        internal_set_error(r, "cannot allocate memory: %s", strerror(errno));
164        gss_release_buffer(&minor, &token);
165        return NULL;
166    }
167    memcpy(r->output->data, p, length);
168    r->output->length = length;
169    gss_release_buffer(&minor, &token);
170
171    /*
172     * We always claim everything was stdout since we have no way of knowing
173     * better with protocol version one.
174     */
175    r->output->stream = 1;
176
177    /*
178     * We can only do one round with protocol version one, so close the
179     * connection now.
180     */
181    socket_close(r->fd);
182    r->fd = -1;
183    r->ready = false;
184    return r->output;
185}
Note: See TracBrowser for help on using the repository browser.