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_search_path_init(&config.module_search_path); 235 uc_vm_init(vm, &config); 236 uc_stdlib_load(uc_vm_scope_get(vm)); 237 238 /* build uhttpd api table */ 239 v = ucv_object_new(vm); 240 241 ucv_object_add(v, "send", ucv_cfunction_new("send", uh_ucode_send)); 242 ucv_object_add(v, "sendc", ucv_get(ucv_object_get(v, "send", NULL))); 243 ucv_object_add(v, "recv", ucv_cfunction_new("recv", uh_ucode_recv)); 244 ucv_object_add(v, "urldecode", ucv_cfunction_new("urldecode", uh_ucode_urldecode)); 245 ucv_object_add(v, "urlencode", ucv_cfunction_new("urlencode", uh_ucode_urlencode)); 246 ucv_object_add(v, "docroot", ucv_string_new(conf.docroot)); 247 248 ucv_object_add(uc_vm_scope_get(vm), "uhttpd", v); 249 250 src = uc_source_new_file(ucode->handler); 251 252 if (!src) { 253 fprintf(stderr, "Error: Unable to open ucode handler: %s\n", 254 strerror(errno)); 255 256 exit(1); 257 } 258 259 handler = uc_compile(&config, src, &syntax_error); 260 261 uc_source_put(src); 262 263 if (!handler) { 264 fprintf(stderr, "Error: Unable to compile ucode handler: %s\n", 265 syntax_error); 266 267 exit(1); 268 } 269 270 free(syntax_error); 271 272 vm->output = fopen("/dev/null", "w"); 273 274 if (!vm->output) { 275 fprintf(stderr, "Error: Unable to open /dev/null for writing: %s\n", 276 strerror(errno)); 277 278 exit(1); 279 } 280 281 status = uc_vm_execute(vm, handler, &v); 282 exitcode = (int)ucv_int64_get(v); 283 284 uc_program_put(handler); 285 ucv_put(v); 286 287 switch (status) { 288 case STATUS_OK: 289 break; 290 291 case STATUS_EXIT: 292 fprintf(stderr, "Error: The ucode handler invoked exit(%d)\n", exitcode); 293 exit(exitcode ? exitcode : 1); 294 295 case ERROR_COMPILE: 296 fprintf(stderr, "Error: Compilation error while executing ucode handler\n"); 297 exit(1); 298 299 case ERROR_RUNTIME: 300 fprintf(stderr, "Error: Runtime error while executing ucode handler\n"); 301 exit(2); 302 } 303 304 v = ucv_object_get(uc_vm_scope_get(vm), UH_UCODE_CB, NULL); 305 306 if (!ucv_is_callable(v)) { 307 fprintf(stderr, "Error: The ucode handler declares no " UH_UCODE_CB "() callback.\n"); 308 exit(1); 309 } 310 311 uc_vm_exception_handler_set(vm, uh_ucode_exception); 312 313 ucv_gc(vm); 314 315 fclose(vm->output); 316 317 vm->output = stdout; 318 } 319 320 static void 321 ucode_main(struct client *cl, struct path_info *pi, char *url) 322 { 323 uc_vm_t *vm = ¤t_prefix->ctx; 324 uc_value_t *req, *hdr, *res; 325 int path_len, prefix_len; 326 struct blob_attr *cur; 327 struct env_var *var; 328 char *str; 329 int rem; 330 331 /* new env table for this request */ 332 req = ucv_object_new(vm); 333 334 prefix_len = strlen(pi->name); 335 path_len = strlen(url); 336 str = strchr(url, '?'); 337 338 if (str) { 339 if (*(str + 1)) 340 pi->query = str + 1; 341 342 path_len = str - url; 343 } 344 345 if (prefix_len > 0 && pi->name[prefix_len - 1] == '/') 346 prefix_len--; 347 348 if (path_len > prefix_len) { 349 ucv_object_add(req, "PATH_INFO", 350 ucv_string_new_length(url + prefix_len, path_len - prefix_len)); 351 } 352 353 for (var = ops->get_process_vars(cl, pi); var->name; var++) { 354 if (!var->value) 355 continue; 356 357 ucv_object_add(req, var->name, ucv_string_new(var->value)); 358 } 359 360 ucv_object_add(req, "HTTP_VERSION", 361 ucv_double_new(0.9 + (cl->request.version / 10.0))); 362 363 hdr = ucv_object_new(vm); 364 365 blob_for_each_attr(cur, cl->hdr.head, rem) 366 ucv_object_add(hdr, blobmsg_name(cur), ucv_string_new(blobmsg_data(cur))); 367 368 ucv_object_add(req, "headers", hdr); 369 370 res = uc_vm_invoke(vm, UH_UCODE_CB, 1, req); 371 372 ucv_put(req); 373 ucv_put(res); 374 375 exit(0); 376 } 377 378 static void 379 ucode_handle_request(struct client *cl, char *url, struct path_info *pi) 380 { 381 struct ucode_prefix *p; 382 static struct path_info _pi; 383 384 list_for_each_entry(p, &conf.ucode_prefix, list) { 385 if (!ops->path_match(p->prefix, url)) 386 continue; 387 388 pi = &_pi; 389 pi->name = p->prefix; 390 pi->phys = p->handler; 391 392 current_prefix = p; 393 394 if (!ops->create_process(cl, pi, url, ucode_main)) { 395 ops->client_error(cl, 500, "Internal Server Error", 396 "Failed to create CGI process: %s", 397 strerror(errno)); 398 } 399 400 return; 401 } 402 403 ops->client_error(cl, 500, "Internal Server Error", 404 "Failed to lookup matching handler"); 405 } 406 407 static bool 408 check_ucode_url(const char *url) 409 { 410 struct ucode_prefix *p; 411 412 list_for_each_entry(p, &conf.ucode_prefix, list) 413 if (ops->path_match(p->prefix, url)) 414 return true; 415 416 return false; 417 } 418 419 static struct dispatch_handler ucode_dispatch = { 420 .script = true, 421 .check_url = check_ucode_url, 422 .handle_request = ucode_handle_request, 423 }; 424 425 static int 426 ucode_plugin_init(const struct uhttpd_ops *o, struct config *c) 427 { 428 struct ucode_prefix *p; 429 430 ops = o; 431 _conf = c; 432 433 list_for_each_entry(p, &conf.ucode_prefix, list) 434 uh_ucode_state_init(p); 435 436 ops->dispatch_add(&ucode_dispatch); 437 return 0; 438 } 439 440 struct uhttpd_plugin uhttpd_plugin = { 441 .init = ucode_plugin_init, 442 }; 443
This page was automatically generated by LXR 0.3.1. • OpenWrt