1 /* 2 * uhttpd - Tiny single-threaded httpd 3 * 4 * Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org> 5 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org> 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <libubox/blobmsg.h> 21 #include <ucode/compiler.h> 22 #include <ucode/lib.h> 23 #include <ucode/vm.h> 24 #include <stdio.h> 25 #include <poll.h> 26 27 #include "uhttpd.h" 28 #include "plugin.h" 29 30 #define UH_UCODE_CB "handle_request" 31 32 static const struct uhttpd_ops *ops; 33 static struct config *_conf; 34 #define conf (*_conf) 35 36 static struct ucode_prefix *current_prefix; 37 38 static uc_value_t * 39 uh_ucode_recv(uc_vm_t *vm, size_t nargs) 40 { 41 static struct pollfd pfd = { .fd = STDIN_FILENO, .events = POLLIN }; 42 int data_len = 0, len = BUFSIZ, rlen, r; 43 uc_value_t *v = uc_fn_arg(0); 44 uc_stringbuf_t *buf; 45 46 if (ucv_type(v) == UC_INTEGER) { 47 len = ucv_int64_get(v); 48 } 49 else if (v != NULL) { 50 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Argument not an integer"); 51 52 return NULL; 53 } 54 55 buf = ucv_stringbuf_new(); 56 57 while (len > 0) { 58 rlen = (len < BUFSIZ) ? len : BUFSIZ; 59 60 if (printbuf_memset(buf, -1, 0, rlen)) { 61 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Out of memory"); 62 printbuf_free(buf); 63 64 return NULL; 65 } 66 67 buf->bpos -= rlen; 68 r = read(STDIN_FILENO, buf->buf + buf->bpos, rlen); 69 70 if (r < 0) { 71 if (errno == EWOULDBLOCK || errno == EAGAIN) { 72 pfd.revents = 0; 73 poll(&pfd, 1, 1000); 74 75 if (pfd.revents & POLLIN) 76 continue; 77 } 78 79 if (errno == EINTR) 80 continue; 81 82 if (!data_len) 83 data_len = -1; 84 85 break; 86 } 87 88 buf->bpos += r; 89 data_len += r; 90 len -= r; 91 92 if (r != rlen) 93 break; 94 } 95 96 if (data_len > 0) { 97 /* add final guard \0 but do not count it */ 98 if (printbuf_memset(buf, -1, 0, 1)) { 99 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Out of memory"); 100 printbuf_free(buf); 101 102 return NULL; 103 } 104 105 buf->bpos--; 106 107 return ucv_stringbuf_finish(buf); 108 } 109 110 printbuf_free(buf); 111 112 return NULL; 113 } 114 115 static uc_value_t * 116 uh_ucode_send(uc_vm_t *vm, size_t nargs) 117 { 118 uc_value_t *val; 119 size_t arridx; 120 ssize_t len = 0; 121 char *p; 122 123 for (arridx = 0; arridx < nargs; arridx++) { 124 val = uc_fn_arg(arridx); 125 126 if (ucv_type(val) == UC_STRING) { 127 len += write(STDOUT_FILENO, ucv_string_get(val), ucv_string_length(val)); 128 } 129 else if (val != NULL) { 130 p = ucv_to_string(vm, val); 131 len += p ? write(STDOUT_FILENO, p, strlen(p)) : 0; 132 free(p); 133 } 134 } 135 136 return ucv_int64_new(len); 137 } 138 139 static uc_value_t * 140 uh_ucode_strconvert(uc_vm_t *vm, size_t nargs, int (*convert)(char *, int, const char *, int)) 141 { 142 uc_value_t *val = uc_fn_arg(0); 143 static char out_buf[4096]; 144 int out_len; 145 char *p; 146 147 if (ucv_type(val) == UC_STRING) { 148 out_len = convert(out_buf, sizeof(out_buf), 149 ucv_string_get(val), ucv_string_length(val)); 150 } 151 else if (val != NULL) { 152 p = ucv_to_string(vm, val); 153 out_len = p ? convert(out_buf, sizeof(out_buf), p, strlen(p)) : 0; 154 free(p); 155 } 156 else { 157 out_len = 0; 158 } 159 160 if (out_len < 0) { 161 const char *error; 162 163 if (out_len == -1) 164 error = "buffer overflow"; 165 else 166 error = "malformed string"; 167 168 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, 169 "%s on URL conversion\n", error); 170 171 return NULL; 172 } 173 174 return ucv_string_new_length(out_buf, out_len); 175 } 176 177 static uc_value_t * 178 uh_ucode_urldecode(uc_vm_t *vm, size_t nargs) 179 { 180 return uh_ucode_strconvert(vm, nargs, ops->urldecode); 181 } 182 183 static uc_value_t * 184 uh_ucode_urlencode(uc_vm_t *vm, size_t nargs) 185 { 186 return uh_ucode_strconvert(vm, nargs, ops->urlencode); 187 } 188 189 static uc_parse_config_t config = { 190 .strict_declarations = false, 191 .lstrip_blocks = true, 192 .trim_blocks = true 193 }; 194 195 static void 196 uh_ucode_exception(uc_vm_t *vm, uc_exception_t *ex) 197 { 198 uc_value_t *ctx; 199 200 if (ex->type == EXCEPTION_EXIT) 201 return; 202 203 printf("Status: 500 Internal Server Error\r\n\r\n" 204 "Exception while executing ucode program %s:\n", 205 current_prefix->handler); 206 207 switch (ex->type) { 208 case EXCEPTION_SYNTAX: printf("Syntax error"); break; 209 case EXCEPTION_RUNTIME: printf("Runtime error"); break; 210 case EXCEPTION_TYPE: printf("Type error"); break; 211 case EXCEPTION_REFERENCE: printf("Reference error"); break; 212 default: printf("Error"); 213 } 214 215 printf(": %s\n", ex->message); 216 217 ctx = ucv_object_get(ucv_array_get(ex->stacktrace, 0), "context", NULL); 218 219 if (ctx) 220 printf("%s\n", ucv_string_get(ctx)); 221 } 222 223 static void 224 uh_ucode_state_init(struct ucode_prefix *ucode) 225 { 226 char *syntax_error = NULL; 227 uc_vm_t *vm = &ucode->ctx; 228 uc_program_t *handler; 229 uc_vm_status_t status; 230 uc_source_t *src; 231 uc_value_t *v; 232 int exitcode; 233 234 uc_vm_init(vm, &config); 235 uc_stdlib_load(uc_vm_scope_get(vm)); 236 237 /* build uhttpd api table */ 238 v = ucv_object_new(vm); 239 240 ucv_object_add(v, "send", ucv_cfunction_new("send", uh_ucode_send)); 241 ucv_object_add(v, "sendc", ucv_get(ucv_object_get(v, "send", NULL))); 242 ucv_object_add(v, "recv", ucv_cfunction_new("recv", uh_ucode_recv)); 243 ucv_object_add(v, "urldecode", ucv_cfunction_new("urldecode", uh_ucode_urldecode)); 244 ucv_object_add(v, "urlencode", ucv_cfunction_new("urlencode", uh_ucode_urlencode)); 245 ucv_object_add(v, "docroot", ucv_string_new(conf.docroot)); 246 247 ucv_object_add(uc_vm_scope_get(vm), "uhttpd", v); 248 249 src = uc_source_new_file(ucode->handler); 250 251 if (!src) { 252 fprintf(stderr, "Error: Unable to open ucode handler: %s\n", 253 strerror(errno)); 254 255 exit(1); 256 } 257 258 handler = uc_compile(&config, src, &syntax_error); 259 260 uc_source_put(src); 261 262 if (!handler) { 263 fprintf(stderr, "Error: Unable to compile ucode handler: %s\n", 264 syntax_error); 265 266 exit(1); 267 } 268 269 free(syntax_error); 270 271 vm->output = fopen("/dev/null", "w"); 272 273 if (!vm->output) { 274 fprintf(stderr, "Error: Unable to open /dev/null for writing: %s\n", 275 strerror(errno)); 276 277 exit(1); 278 } 279 280 status = uc_vm_execute(vm, handler, &v); 281 exitcode = (int)ucv_int64_get(v); 282 283 uc_program_put(handler); 284 ucv_put(v); 285 286 switch (status) { 287 case STATUS_OK: 288 break; 289 290 case STATUS_EXIT: 291 fprintf(stderr, "Error: The ucode handler invoked exit(%d)\n", exitcode); 292 exit(exitcode ? exitcode : 1); 293 294 case ERROR_COMPILE: 295 fprintf(stderr, "Error: Compilation error while executing ucode handler\n"); 296 exit(1); 297 298 case ERROR_RUNTIME: 299 fprintf(stderr, "Error: Runtime error while executing ucode handler\n"); 300 exit(2); 301 } 302 303 v = ucv_object_get(uc_vm_scope_get(vm), UH_UCODE_CB, NULL); 304 305 if (!ucv_is_callable(v)) { 306 fprintf(stderr, "Error: The ucode handler declares no " UH_UCODE_CB "() callback.\n"); 307 exit(1); 308 } 309 310 uc_vm_exception_handler_set(vm, uh_ucode_exception); 311 312 ucv_gc(vm); 313 314 fclose(vm->output); 315 316 vm->output = stdout; 317 } 318 319 static void 320 ucode_main(struct client *cl, struct path_info *pi, char *url) 321 { 322 uc_vm_t *vm = ¤t_prefix->ctx; 323 uc_value_t *req, *hdr, *res; 324 int path_len, prefix_len; 325 struct blob_attr *cur; 326 struct env_var *var; 327 char *str; 328 int rem; 329 330 /* new env table for this request */ 331 req = ucv_object_new(vm); 332 333 prefix_len = strlen(pi->name); 334 path_len = strlen(url); 335 str = strchr(url, '?'); 336 337 if (str) { 338 if (*(str + 1)) 339 pi->query = str + 1; 340 341 path_len = str - url; 342 } 343 344 if (prefix_len > 0 && pi->name[prefix_len - 1] == '/') 345 prefix_len--; 346 347 if (path_len > prefix_len) { 348 ucv_object_add(req, "PATH_INFO", 349 ucv_string_new_length(url + prefix_len, path_len - prefix_len)); 350 } 351 352 for (var = ops->get_process_vars(cl, pi); var->name; var++) { 353 if (!var->value) 354 continue; 355 356 ucv_object_add(req, var->name, ucv_string_new(var->value)); 357 } 358 359 ucv_object_add(req, "HTTP_VERSION", 360 ucv_double_new(0.9 + (cl->request.version / 10.0))); 361 362 hdr = ucv_object_new(vm); 363 364 blob_for_each_attr(cur, cl->hdr.head, rem) 365 ucv_object_add(hdr, blobmsg_name(cur), ucv_string_new(blobmsg_data(cur))); 366 367 ucv_object_add(req, "headers", hdr); 368 369 res = uc_vm_invoke(vm, UH_UCODE_CB, 1, req); 370 371 ucv_put(req); 372 ucv_put(res); 373 374 exit(0); 375 } 376 377 static void 378 ucode_handle_request(struct client *cl, char *url, struct path_info *pi) 379 { 380 struct ucode_prefix *p; 381 static struct path_info _pi; 382 383 list_for_each_entry(p, &conf.ucode_prefix, list) { 384 if (!ops->path_match(p->prefix, url)) 385 continue; 386 387 pi = &_pi; 388 pi->name = p->prefix; 389 pi->phys = p->handler; 390 391 current_prefix = p; 392 393 if (!ops->create_process(cl, pi, url, ucode_main)) { 394 ops->client_error(cl, 500, "Internal Server Error", 395 "Failed to create CGI process: %s", 396 strerror(errno)); 397 } 398 399 return; 400 } 401 402 ops->client_error(cl, 500, "Internal Server Error", 403 "Failed to lookup matching handler"); 404 } 405 406 static bool 407 check_ucode_url(const char *url) 408 { 409 struct ucode_prefix *p; 410 411 list_for_each_entry(p, &conf.ucode_prefix, list) 412 if (ops->path_match(p->prefix, url)) 413 return true; 414 415 return false; 416 } 417 418 static struct dispatch_handler ucode_dispatch = { 419 .script = true, 420 .check_url = check_ucode_url, 421 .handle_request = ucode_handle_request, 422 }; 423 424 static int 425 ucode_plugin_init(const struct uhttpd_ops *o, struct config *c) 426 { 427 struct ucode_prefix *p; 428 429 ops = o; 430 _conf = c; 431 432 uc_search_path_init(&config.module_search_path); 433 434 list_for_each_entry(p, &conf.ucode_prefix, list) 435 uh_ucode_state_init(p); 436 437 ops->dispatch_add(&ucode_dispatch); 438 return 0; 439 } 440 441 struct uhttpd_plugin uhttpd_plugin = { 442 .init = ucode_plugin_init, 443 }; 444
This page was automatically generated by LXR 0.3.1. • OpenWrt