mod_tmcd.c 7.14 KB
Newer Older
1 2
/*
 * Copyright (c) 2008-2009 University of Utah and the Flux Group.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 
 * {{{EMULAB-LICENSE
 * 
 * This file is part of the Emulab network testbed software.
 * 
 * This file is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This file is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 * License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * }}}
22 23
 */

Ryan Jackson's avatar
Ryan Jackson committed
24 25
#include <arpa/inet.h>

26 27 28 29 30 31 32 33 34 35 36
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_main.h"
#include "http_protocol.h"

#include "libtmcd.h"

module MODULE_VAR_EXPORT tmcd_module;

Ryan Jackson's avatar
Ryan Jackson committed
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
static char **make_argv(char *args, int *argc, char separator)
{
	char **argv = NULL;
	char *p, *token;
	int count;
	int i;

	count = 1;
	p = args;
	while (*p) {
		if (*p == separator) {
			count++;
		}

		p++;
	}

	argv = malloc(sizeof(char *) * count);
	if (argv == NULL)
		goto err;
	memset(argv, 0, sizeof(char *) * count);

	p = args;
	token = args;
	i = 0;
	while (*p) {
		if (*p == separator) {
			*p = '\0';
			argv[i] = token;
			token = p + 1;
			i++;
		}

		p++;
	}
	argv[i] = token;

	*argc = count;
err:
	return argv;
}

79 80 81 82 83
static int handle_request(request_rec *r)
{
	tmcdreq_t tmcdreq, *reqp = &tmcdreq;
	tmcdresp_t *response = NULL;
	char *command;
Ryan Jackson's avatar
Ryan Jackson committed
84 85 86 87 88 89 90 91 92 93 94
	struct in_addr local_addr;
	struct in_addr remote_addr;
	struct sockaddr_in redir_client;
	int tmcd_status;
	int status = OK;
	char *status_line = NULL;
	char *args = NULL;
	char *function_args = NULL;
	char *p;
	char **argv = NULL;
	int argc, i;
95

96 97 98 99
	reqp->istcp = 1;
	reqp->isssl = 1; /* FIXME */

	if (strcmp(r->handler, "tmcd")) {
Ryan Jackson's avatar
Ryan Jackson committed
100
		status = DECLINED;
101 102
		goto err;
	}
103 104 105

#if 0
	r->allowed |= (AP_METHOD_BIT << M_GET);
106
	if (r->method_number != M_GET) {
Ryan Jackson's avatar
Ryan Jackson committed
107
		status = DECLINED;
108 109
		goto err;
	}
110 111 112 113
#endif

	memset(reqp, 0, sizeof(*reqp));

Ryan Jackson's avatar
Ryan Jackson committed
114 115
	local_addr = r->connection->local_addr.sin_addr;
	remote_addr = r->connection->remote_addr.sin_addr;
116

Ryan Jackson's avatar
Ryan Jackson committed
117 118
	reqp->version = 1; /* FIXME need sane default */
	tmcd_init(reqp, &local_addr, NULL);
119

Ryan Jackson's avatar
Ryan Jackson committed
120 121 122 123 124 125 126 127
	command = r->path_info;
	while (*command && *command == '/') {
		command++;
	}
	if (command[0] == '\0') {
		status = HTTP_BAD_REQUEST;
		goto err;
	}
128 129

	if (r->args) {
Ryan Jackson's avatar
Ryan Jackson committed
130 131 132 133 134
		args = malloc(strlen(r->args) + 1);
		if (args == NULL) {
			status = HTTP_INTERNAL_SERVER_ERROR;
			goto err;
		}
135

Ryan Jackson's avatar
Ryan Jackson committed
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
		strcpy(args, r->args);
		argv = make_argv(args, &argc, '&');
		if (argv == NULL) {
			status = HTTP_INTERNAL_SERVER_ERROR;
			goto err;
		}

		for (i = 0; i < argc; i++) {
			/* Unescape the arguments */
			p = args;
			while (*p) {
				if (*p == '+')
					*p = ' ';
				p++;
			}

			status = ap_unescape_url(args);
			if (status != OK) {
				goto err;
			}

			if (strncasecmp(argv[i], "version=", 8) == 0) {
				long version;
				char *end;
				version = strtol(argv[i] + 8, &end, 10);
				if (*end != '\0' || *(argv[i] + 8) == '\0') {
					status = HTTP_BAD_REQUEST;
					status_line = "Invalid Version";
					goto err;
				}

				reqp->version = version;
			} else if (strncasecmp(argv[i], "redirect=", 9) == 0) {
				if (inet_pton(AF_INET, argv[i] + 9,
				              &redir_client.sin_addr) <= 0) {
					status = HTTP_BAD_REQUEST;
					status_line = "Invalid IP Address";
					goto err;
				}
				/* FIXME info message */

				if (remote_addr.s_addr != local_addr.s_addr) {
					status = HTTP_FORBIDDEN;
					status_line = "Redirection Not Allowed";
					goto err;
				}

				remote_addr =
				    redir_client.sin_addr;

			} else if (strncasecmp(argv[i], "vnodeid=", 8) == 0) {
				if (strlen(argv[i] + 8) >=
				           sizeof(reqp->vnodeid)) {
					status = HTTP_BAD_REQUEST;
					status_line =
					    "Virtual Node ID Too Long";
					goto err;
				}
				reqp->isvnode = 1;
				strcpy(reqp->vnodeid, argv[i] + 8);
			} else if (strncasecmp(argv[i], "args=", 5) == 0) {
				function_args = argv[i] + 5;
			}
		}

	}
202 203

	/* FIXME handle wanodekey */
Ryan Jackson's avatar
Ryan Jackson committed
204
	if ((tmcd_status = iptonodeid(reqp, remote_addr, NULL))) {
205
		if (reqp->isvnode) {
Ryan Jackson's avatar
Ryan Jackson committed
206
			status_line = "Invalid Virtual Node";
207 208
		}
		else {
Ryan Jackson's avatar
Ryan Jackson committed
209
			status_line = "Invalid Node";
210
		}
Ryan Jackson's avatar
Ryan Jackson committed
211
		status = HTTP_NOT_FOUND;
212
		goto err;
213 214
	}

Ryan Jackson's avatar
Ryan Jackson committed
215 216 217 218 219 220 221 222 223
	if (reqp->tmcd_redirect[0]) {
		/* FIXME what if https should be used? */
		/* FIXME do I need to specify the args should be passed too? */
		char *uri = ap_psprintf(r->pool, "http://%s%s?%s", reqp->tmcd_redirect,
		                        r->uri, r->args);
		ap_table_setn(r->headers_out, "Location", uri);
		status = HTTP_MOVED_TEMPORARILY;
		goto done;
	}
224

Ryan Jackson's avatar
Ryan Jackson committed
225 226 227 228
	tmcd_status = tmcd_handle_request(reqp, &response, command,
	                                  function_args);

	if (tmcd_status == TMCD_STATUS_OK) {
229 230
		r->content_type = response->type;
		ap_set_content_length(r, response->length);
231 232 233
		/* FIXME doctype */
		ap_soft_timeout("tmcd response call trace", r);
		ap_send_http_header(r);
234
		ap_rprintf(r, "%s", response->data);
235
		ap_kill_timeout(r);
Ryan Jackson's avatar
Ryan Jackson committed
236
		status = OK;
237
		goto done;
Ryan Jackson's avatar
Ryan Jackson committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
	} else {
		switch(tmcd_status) {
			case TMCD_STATUS_UNKNOWN_COMMAND:
				status = HTTP_NOT_FOUND;
				status_line = "Unknown Command";
				break;
			case TMCD_STATUS_REQUIRES_ENCRYPTION:
				status = HTTP_FORBIDDEN;
				status_line = "SSL Required";
				break;
			case TMCD_STATUS_NODE_NOT_ALLOCATED:
				status = HTTP_FORBIDDEN;
				status_line = "Node Not Allocated";
				break;
			case TMCD_STATUS_COMMAND_FAILED:
				status = HTTP_INTERNAL_SERVER_ERROR;
				if (response && response->data) {
					status_line = response->data;
				}
				break;
			case TMCD_STATUS_MALLOC_FAILED:
				status = HTTP_INTERNAL_SERVER_ERROR;
				break;
		}

263 264 265 266
		goto err;
	}
err:
done:
Ryan Jackson's avatar
Ryan Jackson committed
267 268 269 270 271 272
	if (argv)
		free(argv);

	if (args)
		free(args);

273 274
	if (response)
		tmcd_free_response(response);
275

Ryan Jackson's avatar
Ryan Jackson committed
276 277 278 279 280
	if (status_line) {
		r->status_line = ap_psprintf(r->pool, "%3.3u %s", status,
		                             status_line);
	}
	return status;
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
}

static const handler_rec tmcd_handlers[] =
{
	{"tmcd", handle_request},
	{NULL}
};

module MODULE_VAR_EXPORT tmcd_module = {
	STANDARD_MODULE_STUFF,
	NULL,               /* module initializer */
	NULL,  /* per-directory config creator */
	NULL,   /* dir config merger */
	NULL,       /* server config creator */
	NULL,        /* server config merger */
	NULL,               /* command table */
	tmcd_handlers,           /* [9] list of handlers */
	NULL,  /* [2] filename-to-URI translation */
	NULL,      /* [5] check/validate user_id */
	NULL,       /* [6] check user_id is valid *here* */
	NULL,     /* [4] check access by host address */
	NULL,       /* [7] MIME type checker/setter */
	NULL,        /* [8] fixups */
	NULL,             /* [10] logger */
#if MODULE_MAGIC_NUMBER >= 19970103
	NULL,      /* [3] header parser */
#endif
#if MODULE_MAGIC_NUMBER >= 19970719
	NULL,         /* process initializer */
#endif
#if MODULE_MAGIC_NUMBER >= 19970728
	NULL,         /* process exit/cleanup */
#endif
#if MODULE_MAGIC_NUMBER >= 19970902
	NULL  /* [1] post read_request handling */
#endif
};