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 = uc_fn_arg(0); 119 ssize_t len; 120 char *p; 121 122 if (ucv_type(val) == UC_STRING) { 123 len = write(STDOUT_FILENO, ucv_string_get(val), ucv_string_length(val)); 124 } 125 else if (val != NULL) { 126 p = ucv_to_string(vm, val); 127 len = p ? write(STDOUT_FILENO, p, strlen(p)) : 0; 128 free(p); 129 } 130 else { 131 len = 0; 132 } 133 134 return ucv_int64_new(len); 135 } 136 137 static uc_value_t * 138 uh_ucode_strconvert(uc_vm_t *vm, size_t nargs, int (*convert)(char *, int, const char *, int)) 139 { 140 uc_value_t *val = uc_fn_arg(0); 141 static char out_buf[4096]; 142 int out_len; 143 char *p; 144 145 if (ucv_type(val) == UC_STRING) { 146 out_len = convert(out_buf, sizeof(out_buf), 147 ucv_string_get(val), ucv_string_length(val)); 148 } 149 else if (val != NULL) { 150 p = ucv_to_string(vm, val); 151 out_len = p ? convert(out_buf, sizeof(out_buf), p, strlen(p)) : 0; 152 free(p); 153 } 154 else { 155 out_len = 0; 156 } 157 158 if (out_len < 0) { 159 const char *error; 160 161 if (out_len == -1) 162 error = "buffer overflow"; 163 else 164 error = "malformed string"; 165 166 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, 167 "%s on URL conversion\n", error); 168 169 return NULL; 170 } 171 172 return ucv_string_new_length(out_buf, out_len); 173 } 174 175 static uc_value_t * 176 uh_ucode_urldecode(uc_vm_t *vm, size_t nargs) 177 { 178 return uh_ucode_strconvert(vm, nargs, ops->urldecode); 179 } 180 181 static uc_value_t * 182 uh_ucode_urlencode(uc_vm_t *vm, size_t nargs) 183 { 184 return uh_ucode_strconvert(vm, nargs, ops->urlencode); 185 } 186 187 static uc_parse_config_t config = { 188 .strict_declarations = false, 189 .lstrip_blocks = true, 190 .trim_blocks = true 191 }; 192 193 static void 194 uh_ucode_exception(uc_vm_t *vm, uc_exception_t *ex) 195 { 196 uc_value_t *ctx; 197 198 printf("Status: 500 Internal Server Error\r\n\r\n" 199 "Exception while executing ucode program %s:\n", 200 current_prefix->handler); 201 202 switch (ex->type) { 203 case EXCEPTION_SYNTAX: printf("Syntax error"); break; 204 case EXCEPTION_RUNTIME: printf("Runtime error"); break; 205 case EXCEPTION_TYPE: printf("Type error"); break; 206 case EXCEPTION_REFERENCE: printf("Reference error"); break; 207 default: printf("Error"); 208 } 209 210 printf(": %s\n", ex->message); 211 212 ctx = ucv_object_get(ucv_array_get(ex->stacktrace, 0), "context", NULL); 213 214 if (ctx) 215 printf("%s\n", ucv_string_get(ctx)); 216 } 217 218 static void 219 uh_ucode_state_init(struct ucode_prefix *ucode) 220 { 221 char *syntax_error = NULL; 222 uc_vm_t *vm = &ucode->ctx; 223 uc_program_t *handler; 224 uc_vm_status_t status; 225 uc_source_t *src; 226 uc_value_t *v; 227 int exitcode; 228 229 uc_vm_init(vm, &config); 230 uc_stdlib_load(uc_vm_scope_get(vm)); 231 232 /* build uhttpd api table */ 233 v = ucv_object_new(vm); 234 235 ucv_object_add(v, "send", ucv_cfunction_new("send", uh_ucode_send)); 236 ucv_object_add(v, "sendc", ucv_get(ucv_object_get(v, "send", NULL))); 237 ucv_object_add(v, "recv", ucv_cfunction_new("recv", uh_ucode_recv)); 238 ucv_object_add(v, "urldecode", ucv_cfunction_new("urldecode", uh_ucode_urldecode)); 239 ucv_object_add(v, "urlencode", ucv_cfunction_new("urlencode", uh_ucode_urlencode)); 240 ucv_object_add(v, "docroot", ucv_string_new(conf.docroot)); 241 242 ucv_object_add(uc_vm_scope_get(vm), "uhttpd", v); 243 244 src = uc_source_new_file(ucode->handler); 245 246 if (!src) { 247 fprintf(stderr, "Error: Unable to open ucode handler: %s\n", 248 strerror(errno)); 249 250 exit(1); 251 } 252 253 handler = uc_compile(&config, src, &syntax_error); 254 255 uc_source_put(src); 256 257 if (!handler) { 258 fprintf(stderr, "Error: Unable to compile ucode handler: %s\n", 259 syntax_error); 260 261 exit(1); 262 } 263 264 free(syntax_error); 265 266 vm->output = fopen("/dev/null", "w"); 267 268 if (!vm->output) { 269 fprintf(stderr, "Error: Unable to open /dev/null for writing: %s\n", 270 strerror(errno)); 271 272 exit(1); 273 } 274 275 status = uc_vm_execute(vm, handler, &v); 276 exitcode = (int)ucv_int64_get(v); 277 278 uc_program_put(handler); 279 ucv_put(v); 280 281 switch (status) { 282 case STATUS_OK: 283 break; 284 285 case STATUS_EXIT: 286 fprintf(stderr, "Error: The ucode handler invoked exit(%d)\n", exitcode); 287 exit(exitcode ? exitcode : 1); 288 289 case ERROR_COMPILE: 290 fprintf(stderr, "Error: Compilation error while executing ucode handler\n"); 291 exit(1); 292 293 case ERROR_RUNTIME: 294 fprintf(stderr, "Error: Runtime error while executing ucode handler\n"); 295 exit(2); 296 } 297 298 v = ucv_object_get(uc_vm_scope_get(vm), UH_UCODE_CB, NULL); 299 300 if (!ucv_is_callable(v)) { 301 fprintf(stderr, "Error: The ucode handler declares no " UH_UCODE_CB "() callback.\n"); 302 exit(1); 303 } 304 305 uc_vm_exception_handler_set(vm, uh_ucode_exception); 306 307 ucv_gc(vm); 308 309 fclose(vm->output); 310 311 vm->output = stdout; 312 } 313 314 static void 315 ucode_main(struct client *cl, struct path_info *pi, char *url) 316 { 317 uc_vm_t *vm = ¤t_prefix->ctx; 318 uc_value_t *req, *hdr, *res; 319 int path_len, prefix_len; 320 struct blob_attr *cur; 321 struct env_var *var; 322 char *str; 323 int rem; 324 325 /* new env table for this request */ 326 req = ucv_object_new(vm); 327 328 prefix_len = strlen(pi->name); 329 path_len = strlen(url); 330 str = strchr(url, '?'); 331 332 if (str) { 333 if (*(str + 1)) 334 pi->query = str + 1; 335 336 path_len = str - url; 337 } 338 339 if (prefix_len > 0 && pi->name[prefix_len - 1] == '/') 340 prefix_len--; 341 342 if (path_len > prefix_len) { 343 ucv_object_add(req, "PATH_INFO", 344 ucv_string_new_length(url + prefix_len, path_len - prefix_len)); 345 } 346 347 for (var = ops->get_process_vars(cl, pi); var->name; var++) { 348 if (!var->value) 349 continue; 350 351 ucv_object_add(req, var->name, ucv_string_new(var->value)); 352 } 353 354 ucv_object_add(req, "HTTP_VERSION", 355 ucv_double_new(0.9 + (cl->request.version / 10.0))); 356 357 hdr = ucv_object_new(vm); 358 359 blob_for_each_attr(cur, cl->hdr.head, rem) 360 ucv_object_add(hdr, blobmsg_name(cur), ucv_string_new(blobmsg_data(cur))); 361 362 ucv_object_add(req, "headers", hdr); 363 364 res = uc_vm_invoke(vm, UH_UCODE_CB, 1, req); 365 366 ucv_put(req); 367 ucv_put(res); 368 369 exit(0); 370 } 371 372 static void 373 ucode_handle_request(struct client *cl, char *url, struct path_info *pi) 374 { 375 struct ucode_prefix *p; 376 static struct path_info _pi; 377 378 list_for_each_entry(p, &conf.ucode_prefix, list) { 379 if (!ops->path_match(p->prefix, url)) 380 continue; 381 382 pi = &_pi; 383 pi->name = p->prefix; 384 pi->phys = p->handler; 385 386 current_prefix = p; 387 388 if (!ops->create_process(cl, pi, url, ucode_main)) { 389 ops->client_error(cl, 500, "Internal Server Error", 390 "Failed to create CGI process: %s", 391 strerror(errno)); 392 } 393 394 return; 395 } 396 397 ops->client_error(cl, 500, "Internal Server Error", 398 "Failed to lookup matching handler"); 399 } 400 401 static bool 402 check_ucode_url(const char *url) 403 { 404 struct ucode_prefix *p; 405 406 list_for_each_entry(p, &conf.ucode_prefix, list) 407 if (ops->path_match(p->prefix, url)) 408 return true; 409 410 return false; 411 } 412 413 static struct dispatch_handler ucode_dispatch = { 414 .script = true, 415 .check_url = check_ucode_url, 416 .handle_request = ucode_handle_request, 417 }; 418 419 static int 420 ucode_plugin_init(const struct uhttpd_ops *o, struct config *c) 421 { 422 struct ucode_prefix *p; 423 424 ops = o; 425 _conf = c; 426 427 list_for_each_entry(p, &conf.ucode_prefix, list) 428 uh_ucode_state_init(p); 429 430 ops->dispatch_add(&ucode_dispatch); 431 return 0; 432 } 433 434 struct uhttpd_plugin uhttpd_plugin = { 435 .init = ucode_plugin_init, 436 }; 437
This page was automatically generated by LXR 0.3.1. • OpenWrt