source: web/old/remctl-2.14/server/server-v2.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: 10.2 KB
Line 
1/*
2 * Protocol v2, server implementation.
3 *
4 * This is the server implementation of the new v2 protocol.
5 *
6 * Written by Russ Allbery <rra@stanford.edu>
7 * Based on work by Anton Ushakov
8 * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
9 *     Board of Trustees, Leland Stanford Jr. University
10 *
11 * See LICENSE for licensing terms.
12 */
13
14#include <config.h>
15#include <portable/system.h>
16#include <portable/gssapi.h>
17#include <portable/socket.h>
18#include <portable/uio.h>
19
20#include <server/internal.h>
21#include <util/util.h>
22
23
24/*
25 * Given the client struct and the stream number the data is from, send a
26 * protocol v2 output token to the client containing the data stored in the
27 * buffer in the client struct.  Returns true on success, false on failure
28 * (and logs a message on failure).
29 */
30bool
31server_v2_send_output(struct client *client, int stream)
32{
33    gss_buffer_desc token;
34    char *p;
35    OM_uint32 tmp, major, minor;
36    int status;
37
38    /* Allocate room for the total message. */
39    token.length = 1 + 1 + 1 + 4 + client->outlen;
40    token.value = xmalloc(token.length);
41
42    /*
43     * Fill in the header (version, type, stream, length, and data) and then
44     * the data.
45     */
46    p = token.value;
47    *p = 2;
48    p++;
49    *p = MESSAGE_OUTPUT;
50    p++;
51    *p = stream;
52    p++;
53    tmp = htonl(client->outlen);
54    memcpy(p, &tmp, 4);
55    p += 4;
56    memcpy(p, client->output, client->outlen);
57
58    /* Send the token. */
59    status = token_send_priv(client->fd, client->context,
60                 TOKEN_DATA | TOKEN_PROTOCOL, &token, &major, &minor);
61    if (status != TOKEN_OK) {
62        warn_token("sending output token", status, major, minor);
63        free(token.value);
64        client->fatal = true;
65        return false;
66    }
67    free(token.value);
68    return true;
69}
70
71
72/*
73 * Given the client struct and the exit status, send a protocol v2 status
74 * token to the client.  Returns true on success, false on failure (and logs a
75 * message on failure).
76 */
77bool
78server_v2_send_status(struct client *client, int exit_status)
79{
80    gss_buffer_desc token;
81    char buffer[1 + 1 + 1];
82    OM_uint32 major, minor;
83    int status;
84
85    /* Build the status token. */
86    token.length = 1 + 1 + 1;
87    token.value = &buffer;
88    buffer[0] = 2;
89    buffer[1] = MESSAGE_STATUS;
90    buffer[2] = exit_status;
91
92    /* Send the token. */
93    status = token_send_priv(client->fd, client->context,
94                 TOKEN_DATA | TOKEN_PROTOCOL, &token, &major, &minor);
95    if (status != TOKEN_OK) {
96        warn_token("sending status token", status, major, minor);
97        client->fatal = true;
98        return false;
99    }
100    return true;
101}
102
103
104/*
105 * Given the client struct, an error code, and an error message, send a
106 * protocol v2 error token to the client.  Returns true on success, false on
107 * failure (and logs a message on failure).
108 */
109bool
110server_v2_send_error(struct client *client, enum error_codes code,
111                     const char *message)
112{
113    gss_buffer_desc token;
114    char *p;
115    OM_uint32 tmp, major, minor;
116    int status;
117
118    /* Build the error token. */
119    token.length = 1 + 1 + 4 + 4 + strlen(message);
120    token.value = xmalloc(token.length);
121    p = token.value;
122    *p = 2;
123    p++;
124    *p = MESSAGE_ERROR;
125    p++;
126    tmp = htonl(code);
127    memcpy(p, &tmp, 4);
128    p += 4;
129    tmp = htonl(strlen(message));
130    memcpy(p, &tmp, 4);
131    p += 4;
132    memcpy(p, message, strlen(message));
133
134    /* Send the token. */
135    status = token_send_priv(client->fd, client->context,
136                 TOKEN_DATA | TOKEN_PROTOCOL, &token, &major, &minor);
137    if (status != TOKEN_OK) {
138        warn_token("sending error token", status, major, minor);
139        free(token.value);
140        client->fatal = true;
141        return false;
142    }
143    free(token.value);
144    return true;
145}
146
147
148/*
149 * Given the client struct, send a protocol v2 version token to the client.
150 * This is the response to a higher version number than we understand.
151 * Returns true on success, false on failure (and logs a message on failure).
152 */
153static bool
154server_v2_send_version(struct client *client)
155{
156    gss_buffer_desc token;
157    char buffer[1 + 1 + 1];
158    OM_uint32 major, minor;
159    int status;
160
161    /* Build the version token. */
162    token.length = 1 + 1 + 1;
163    token.value = &buffer;
164    buffer[0] = 2;
165    buffer[1] = MESSAGE_VERSION;
166    buffer[2] = 2;
167
168    /* Send the token. */
169    status = token_send_priv(client->fd, client->context,
170                 TOKEN_DATA | TOKEN_PROTOCOL, &token, &major, &minor);
171    if (status != TOKEN_OK) {
172        warn_token("sending version token", status, major, minor);
173        client->fatal = true;
174        return false;
175    }
176    return true;
177}
178
179
180/*
181 * Receive a new token from the client, handling reporting of errors.  Takes
182 * the client struct and a pointer to storage for the token.  Returns TOKEN_OK
183 * on success, TOKEN_FAIL_EOF if the other end has gone away, and a different
184 * error code on a recoverable error.
185 */
186static int
187server_v2_read_token(struct client *client, gss_buffer_t token)
188{
189    OM_uint32 major, minor;
190    int status, flags;
191   
192    status = token_recv_priv(client->fd, client->context, &flags, token,
193                             TOKEN_MAX_LENGTH, &major, &minor);
194    if (status != TOKEN_OK) {
195        warn_token("receiving command token", status, major, minor);
196        if (status != TOKEN_FAIL_EOF)
197            if (!server_send_error(client, ERROR_BAD_TOKEN, "Invalid token"))
198                return TOKEN_FAIL_EOF;
199    }
200    return status;
201}
202
203
204/*
205 * Handles a single token from the client, responding or running a command as
206 * appropriate.  Returns true if we should continue, false if an error
207 * occurred or QUIT was received and we should stop processing tokens.
208 */
209static bool
210server_v2_handle_token(struct client *client, struct config *config,
211                       gss_buffer_t token)
212{
213    char *p;
214    size_t length, total;
215    struct iovec **argv = NULL;
216    char *buffer = NULL;
217    int status;
218    OM_uint32 minor;
219    bool result = false;
220    bool allocated = false;
221    bool continued = false;
222
223    /*
224     * Loop on tokens until we have a complete command, allowing for continued
225     * commands.  We're going to accumulate the full command in buffer until
226     * we've seen all of it.  If the command isn't continued, we can use the
227     * token as the buffer.
228     */
229    total = 0;
230    do {
231        p = token->value;
232        if (p[0] != 2) {
233            result = server_v2_send_version(client);
234            goto fail;
235        } else if (p[1] == MESSAGE_QUIT) {
236            debug("quit received, closing connection");
237            result = false;
238            goto fail;
239        } else if (p[1] != MESSAGE_COMMAND) {
240            warn("unknown message type %d from client", (int) p[1]);
241            result = server_send_error(client, ERROR_UNKNOWN_MESSAGE,
242                                       "Unknown message");
243            goto fail;
244        }
245        p += 2;
246        client->keepalive = p[0] ? true : false;
247
248        /* Check the data size. */
249        if (token->length > TOKEN_MAX_DATA) {
250            warn("command data length %lu exceeds 64KB",
251                 (unsigned long) token->length);
252            result = server_send_error(client, ERROR_TOOMUCH_DATA,
253                                       "Too much data");
254            goto fail;
255        }
256
257        /* Make sure the continuation is sane. */
258        if ((p[1] == 1 && continued) || (p[1] > 1 && !continued) || p[1] > 3) {
259            warn("bad continue status %d", (int) p[1]);
260            result = server_send_error(client, ERROR_BAD_COMMAND,
261                                       "Invalid command token");
262            goto fail;
263        }
264        continued = (p[1] == 1 || p[1] == 2);
265
266        /*
267         * Read the token data.  If the command is continued *or* if buffer is
268         * non-NULL (meaning the command was previously continued), we copy
269         * the data into the buffer.
270         */
271        p += 2;
272        length = token->length - (p - (char *) token->value);
273        if (continued || buffer != NULL) {
274            if (buffer == NULL)
275                buffer = xmalloc(length);
276            else
277                buffer = xrealloc(buffer, total + length);
278            allocated = true;
279            memcpy(buffer + total, p, length);
280            total += length;
281        }
282
283        /*
284         * If the command was continued, we have to read the next token.
285         * Otherwise, if buffer is NULL (no continuation), we just use this
286         * token as the complete buffer.
287         */
288        if (continued) {
289            gss_release_buffer(&minor, token);
290            status = server_v2_read_token(client, token);
291            if (status == TOKEN_FAIL_EOF)
292                result = false;
293            else if (status != TOKEN_OK)
294                result = true;
295            if (status != TOKEN_OK)
296                goto fail;
297        } else if (buffer == NULL) {
298            buffer = p;
299            total = length;
300        }
301    } while (continued);
302
303    /*
304     * Okay, we now have a complete command that was possibly spread over
305     * multiple tokens.  Now we can parse it.
306     */
307    argv = server_parse_command(client, buffer, total);
308    if (allocated)
309        free(buffer);
310    if (argv == NULL)
311        return !client->fatal;
312
313    /* We have a command.  Now do the heavy lifting. */
314    server_run_command(client, config, argv);
315    server_free_command(argv);
316    return !client->fatal;
317
318fail:
319    if (allocated)
320        free(buffer);
321    return result;
322}
323
324
325/*
326 * Takes the client struct and the server configuration and handles client
327 * requests.  Reads messages from the client, checking commands against the
328 * ACLs and executing them when appropriate, until the connection is
329 * terminated.
330 */
331void
332server_v2_handle_commands(struct client *client, struct config *config)
333{
334    gss_buffer_desc token;
335    OM_uint32 minor;
336    int status;
337
338    /* Loop receiving messages until we're finished. */
339    do {
340        status = server_v2_read_token(client, &token);
341        if (status == TOKEN_FAIL_EOF)
342            break;
343        else if (status != TOKEN_OK)
344            continue;
345        if (!server_v2_handle_token(client, config, &token)) {
346            gss_release_buffer(&minor, &token);
347            break;
348        }
349        gss_release_buffer(&minor, &token);
350    } while (client->keepalive);
351}
Note: See TracBrowser for help on using the repository browser.