2 * Copyright (C) 2009 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* This code parses and executes the commands sent by guests. It
20 * is therefore particularly security sensitive. The protocol is
21 * documented in hostinfo-protocol(5).
35 #include "hostinfod.h"
37 #define PROTOCOL_VERSION "1.0"
40 apr_hash_t *commands = NULL;
42 static const char *string_of_arg_type (enum arg_type);
43 static void init_commands_hash (void);
44 static int not_printable (const char *str);
47 execute_command (const struct timespec *now,
48 struct guest_description *hval, const char *command)
53 apr_array_header_t args;
57 debug ("%s: %s", hval->name, command);
59 /* Create a new pool for allocation during the lifetime of the
60 * request/response. **NB** the hval->reply field is allocated
61 * from this pool, which is why it gets nulled below.
64 apr_pool_destroy (hval->rpool);
67 hval->reply_size = hval->reply_posn = 0;
68 apr_pool_create (&hval->rpool, hval->pool);
70 /* Split up the command. Commands have a very narrowly-defined
71 * format, and we reject any malformed commands with a 400 error.
73 * NB. A lot of the code below assumes 7 bit, printable ASCII,
74 * and this is only safe because of the call to 'not_printable'
77 len = strlen (command);
78 if (len < 1 || len > 4094 || not_printable (command)) {
79 warning ("%s: command too short, too long or contained unprintable chars",
81 send_error (hval, 400);
85 /* Command is alphanumeric string, non-zero length. */
87 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
88 "abcdefghijklmnopqrstuvwxyz"
91 warning ("%s: no command part in request", hval->name);
92 send_error (hval, 400);
96 cmd = apr_pstrmemdup (hval->rpool, command, i);
97 for (j = 0; j < i; ++j)
98 cmd[j] = tolower (cmd[j]);
100 args = apr_array_make (hval->rpool, 0, sizeof (struct arg));
102 while (command[i]) { /* command[i] is the space before the next arg */
103 if (command[i] != ' ') {
104 warning ("%s: there must be a single space between command and each argument",
106 send_error (hval, 400);
113 switch (command[i]) {
115 warning ("%s: trailing space after command", hval->name);
116 send_error (hval, 400);
119 case '"': /* string literal */
120 arg.type = arg_type_string;
124 case '-': /* integer literal */
136 arg.type = arg_type_int;
140 case 't': /* boolean */
144 arg.type = arg_type_bool;
145 if (strncasecmp (&command[i], "true", 4) == 0) {
148 } else if (strncasecmp (&command[i], "false", 5) == 0) {
157 warning ("%s: unknown or malformed argument starting at position %d ('%c')",
158 hval->name, i, command[i]);
159 send_error (hval, 400);
163 APR_ARRAY_PUSH (args, struct arg) = *arg;
167 /* Debug output what the guest sent / what we decoded. */
168 debug ("%s: command '%s' with %d args", hval->name, cmd, args->nelts);
169 for (i = 0; i < args->nelts; ++i) {
170 struct arg arg = APR_ARRAY_IDX (args, i, struct arg);
175 debug ("%s: arg %d : %s = %ld",
176 hval->name, i, string_of_arg_type (arg.type), arg.u.i);
178 case arg_type_string:
179 debug ("%s: arg %d : %s = %s",
180 hval->name, i, string_of_arg_type (arg.type), arg.u.str);
186 /* Know about this command? */
187 fn = apr_hash_get (commands, cmd, APR_HASH_KEY_STRING);
189 send_error (hval, 404);
193 /* Before dispatching the command, check the command is enabled
194 * and guest is not calling it too frequently.
196 error ("XXXXXXX enabled check not implemented XXXXXXX");
197 error ("XXXXXXX frequency check not implemented XXXXXXX");
199 /* Dispatch the command. */
200 fn (hval, cmd, args);
203 /* All commands must consist only of printable 7 bit ASCII.
204 * NB. Don't use isprint(3).
207 not_printable (const char *str)
211 while ((c = *(unsigned char *)str)) {
212 if (c < 32 || c > 126)
219 /* For single line replies. */
221 send_reply (struct guest_description *hval, int code, const char *fs, ...)
225 /* All success codes must be 2xx. */
226 assert (code >= 200 && code < 300);
232 send_error (struct guest_description *hval, int code)
236 /* All errors must be 4xx or 5xx. */
237 assert (code >= 400 && code < 600);
239 /* NB: If you add a code, update COMMON STATUS CODES section
240 * in hostinfo-protocol.pod.
243 case 400: msg = "Bad request"; break;
244 case 401: msg = "Command disabled"; break;
245 case 404: msg = "Command not found"; break;
246 case 406: msg = "Too frequent"; break;
247 case 500: msg = "Internal server error"; break;
248 default: msg = "Unknown error"; break;
251 /* Construct the reply. */
253 apr_psprintf (hval->rpool, PROTOCOL_VERSION " %03d %s" CRLF,
255 hval->reply_size = strlen (hval->reply);
256 hval->reply_posn = 0;
257 hval->state = guest_state_reply;
259 /* Penalty is always increased on errors, to ensure the guest
260 * cannot flood us with invalid commands.