/* * Locking note: Must be called with skt_mutex held! */ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) { struct socket_data *s_data = s->resource_data; unsigned int probe_mask = MEM_PROBE_LOW; int ret = 0; if (!probe_mem) return 0; mutex_lock(&rsrc_mutex); if (s->features & SS_CAP_PAGE_REGS) probe_mask = MEM_PROBE_HIGH; if (probe_mask & ~s_data->rsrc_mem_probe) { if (s->state & SOCKET_PRESENT) ret = validate_mem(s, probe_mask); if (!ret) s_data->rsrc_mem_probe |= probe_mask; } mutex_unlock(&rsrc_mutex); return ret; }
inline static void http1_free(http1_protocol_s* http) { http_request_clear(&http->request); validate_mem(); if (((void*)http) >= http1_pool.memory && ((void*)http) <= (http1_pool.memory + HTTP1_POOL_MEMORY_SIZE)) { pool_push(http); } else free(http); }
static int access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, void *arg) { if (unlikely (write)) { /* ANDROID support update. */ #ifdef UNW_LOCAL_ONLY if (map_local_is_writable (addr, sizeof(unw_word_t))) { #endif Debug (16, "mem[%016lx] <- %lx\n", addr, *val); *(unw_word_t *) addr = *val; #ifdef UNW_LOCAL_ONLY } else { Debug (16, "Unwritable memory mem[%016lx] <- %lx\n", addr, *val); return -1; } #endif /* End of ANDROID update. */ } else { /* validate address */ const struct cursor *c = (const struct cursor *)arg; if (likely (c != NULL) && unlikely (c->validate) && unlikely (validate_mem (addr))) return -1; /* ANDROID support update. */ #ifdef UNW_LOCAL_ONLY if (map_local_is_readable (addr, sizeof(unw_word_t))) { #endif *val = *(unw_word_t *) addr; Debug (16, "mem[%016lx] -> %lx\n", addr, *val); #ifdef UNW_LOCAL_ONLY } else { Debug (16, "Unreadable memory mem[%016lx] -> XXX\n", addr); return -1; } #endif /* End of ANDROID update. */ } return 0; }
static int access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, void *arg) { if (write) { Debug (16, "mem[%x] <- %x\n", addr, *val); *(unw_word_t *) addr = *val; } else { /* validate address */ const struct cursor *c = (const struct cursor *)arg; if (c && c->validate && validate_mem(addr)) return -1; *val = *(unw_word_t *) addr; Debug (16, "mem[%x] -> %x\n", addr, *val); } return 0; }
static int access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, void *arg) { if (unlikely (write)) { Debug (16, "mem[%016lx] <- %lx\n", addr, *val); *(unw_word_t *) addr = *val; } else { /* validate address */ const struct cursor *c = (const struct cursor *)arg; if (likely (c != NULL) && unlikely (c->validate) && unlikely (validate_mem (addr))) return -1; *val = *(unw_word_t *) addr; Debug (16, "mem[%016lx] -> %lx\n", addr, *val); } return 0; }
/* * Locking note: Must be called with skt_sem held! */ static void pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) { struct socket_data *s_data = s->resource_data; if (probe_mem) { unsigned int probe_mask; down(&rsrc_sem); probe_mask = MEM_PROBE_LOW; if (s->features & SS_CAP_PAGE_REGS) probe_mask = MEM_PROBE_HIGH; if (probe_mask & ~s_data->rsrc_mem_probe) { s_data->rsrc_mem_probe |= probe_mask; if (s->state & SOCKET_PRESENT) validate_mem(s, probe_mask); } up(&rsrc_sem); } }
protocol_s* http1_alloc(intptr_t fd, http_settings_s* settings) { validate_mem(); // HTTP/1.1 should send a busy response // if there aren't enough available file descriptors. if (sock_max_capacity() - sock_uuid2fd(fd) <= HTTP_BUSY_UNLESS_HAS_FDS) goto is_busy; // get an http object from the pool http1_protocol_s* http = pool_pop(); // of malloc one if (http == NULL) http = malloc(HTTP1_PROTOCOL_SIZE); // review allocation if (http == NULL) return NULL; // we shouldn't update the `http` protocol as a struct, as this will waste // time as the whole buffer will be zeroed out when there is no need. // setup parsing state http->buffer_pos = 0; // setup protocol callbacks http->protocol = (protocol_s){ .service = HTTP1, .on_data = (void (*)(intptr_t, protocol_s*))http1_on_data, .on_close = (void (*)(protocol_s*))http1_free, }; // setup request data http->request = (http_request_s){ .metadata.max_headers = HTTP1_MAX_HEADER_COUNT, .metadata.fd = fd, .metadata.owner = http, }; // update settings http->settings = settings; http->on_request = settings->on_request; // set the timeout server_set_timeout(fd, settings->timeout); return (protocol_s*)http; is_busy: if (settings->public_folder && settings->public_folder_length) { size_t p_len = settings->public_folder_length; struct stat file_data = {}; char fname[p_len + 8 + 1]; memcpy(fname, settings->public_folder, p_len); if (settings->public_folder[p_len - 1] == '/' || settings->public_folder[p_len - 1] == '\\') p_len--; memcpy(fname + p_len, "/503.html", 9); p_len += 9; if (stat(fname, &file_data)) goto busy_no_file; // check that we have a file and not something else if (!S_ISREG(file_data.st_mode) && !S_ISLNK(file_data.st_mode)) goto busy_no_file; int file = open(fname, O_RDONLY); if (file == -1) goto busy_no_file; sock_packet_s* packet; packet = sock_checkout_packet(); memcpy(packet->buffer, "HTTP/1.1 503 Service Unavailable\r\n" "Content-Type: text/html\r\n" "Connection: close\r\n" "Content-Length: ", 94); p_len = 94 + http_ul2a(packet->buffer + 94, file_data.st_size); memcpy(packet->buffer + p_len, "\r\n\r\n", 4); p_len += 4; if (BUFFER_PACKET_SIZE - p_len > file_data.st_size) { if (read(file, packet->buffer + p_len, file_data.st_size) < 0) { close(file); sock_free_packet(packet); goto busy_no_file; } close(file); packet->length = p_len + file_data.st_size; sock_send_packet(fd, packet); } else { packet->length = p_len; sock_send_packet(fd, packet); sock_sendfile(fd, file, 0, file_data.st_size); } return NULL; } busy_no_file: sock_write(fd, "HTTP/1.1 503 Service Unavailable\r\nContent-Length: " "13\r\n\r\nServer Busy.", 68); return NULL; } /* ***************************************************************************** HTTP/1.1 protocol bare-bones implementation */ #define HTTP_BODY_CHUNK_SIZE 3072 // 4096 /* parse and call callback */ static void http1_on_data(intptr_t uuid, http1_protocol_s* protocol) { ssize_t len = 0; ssize_t result; char buff[HTTP_BODY_CHUNK_SIZE]; char* buffer; http_request_s* request = &protocol->request; for (;;) { // handle requests with no file data if (request->body_file <= 0) { // request headers parsing if (len == 0) { buffer = protocol->buffer; // make sure headers don't overflow len = sock_read(uuid, buffer + protocol->buffer_pos, HTTP1_MAX_HEADER_SIZE - protocol->buffer_pos); // update buffer read position. protocol->buffer_pos += len; } if (len <= 0) { return; } // parse headers result = http1_parse_request_headers(buffer, protocol->buffer_pos, request); // review result if (result >= 0) { // headers comeplete // mark buffer position, for HTTP pipelining protocol->buffer_pos = result; // are we done? if (request->content_length == 0 || request->body_str) { goto handle_request; } if (request->content_length > protocol->settings->max_body_size) { goto body_to_big; } // initialize or submit body data result = http1_parse_request_body(buffer + result, len - result, request); if (result >= 0) { protocol->buffer_pos += result; goto handle_request; } else if (result == -1) // parser error goto parser_error; goto parse_body; } else if (result == -1) // parser error goto parser_error; // assume incomplete (result == -2), even if wrong, we're right. len = 0; continue; } if (request->body_file > 0) { parse_body: buffer = buff; // request body parsing len = sock_read(uuid, buffer, HTTP_BODY_CHUNK_SIZE); if (len <= 0) return; result = http1_parse_request_body(buffer, len, request); if (result >= 0) { // set buffer pos for piplining support protocol->buffer_pos = result; goto handle_request; } else if (result == -1) // parser error goto parser_error; if (len < HTTP_BODY_CHUNK_SIZE) // pause parser for more data return; goto parse_body; } continue; handle_request: // review required headers / data if (request->host == NULL) goto bad_request; http_settings_s* settings = protocol->settings; // call request callback if (protocol && settings && (protocol->settings->public_folder == NULL || http_response_sendfile2(NULL, request, settings->public_folder, settings->public_folder_length, request->path, request->path_len, settings->log_static))) { protocol->on_request(request); } // clear request state http_request_clear(request); // rotate buffer for HTTP pipelining if (result >= len) { len = 0; } else { memmove(protocol->buffer, buffer + protocol->buffer_pos, len - result); len -= result; } // restart buffer position protocol->buffer_pos = 0; buffer = protocol->buffer; } // no routes lead here. fprintf(stderr, "I am lost on a deserted island, no code can reach me here :-)\n"); return; // How did we get here? parser_error: if (request->headers_count == request->metadata.max_headers) goto too_big; bad_request: /* handle generally bad requests */ { http_response_s response = http_response_init(request); response.status = 400; http_response_write_body(&response, "Bad Request", 11); http_response_finish(&response); sock_close(uuid); protocol->buffer_pos = 0; return; } too_big: /* handle oversized headers */ { http_response_s response = http_response_init(request); response.status = 431; http_response_write_body(&response, "Request Header Fields Too Large", 31); http_response_finish(&response); sock_close(uuid); protocol->buffer_pos = 0; return; body_to_big: /* handle oversized body */ { http_response_s response = http_response_init(request); response.status = 413; http_response_write_body(&response, "Payload Too Large", 17); http_response_finish(&response); sock_close(uuid); protocol->buffer_pos = 0; return; } } } /* ***************************************************************************** HTTP/1.1 listenning API implementation */ #undef http1_listen static void http1_on_init(http_settings_s* settings) { if (settings->timeout == 0) settings->timeout = 5; if (settings->max_body_size == 0) settings->max_body_size = HTTP_DEFAULT_BODY_LIMIT; if (settings->public_folder) { settings->public_folder_length = strlen(settings->public_folder); if (settings->public_folder[0] == '~' && settings->public_folder[1] == '/' && getenv("HOME")) { char* home = getenv("HOME"); size_t home_len = strlen(home); char* tmp = malloc(settings->public_folder_length + home_len + 1); memcpy(tmp, home, home_len); if (home[home_len - 1] == '/') --home_len; memcpy(tmp + home_len, settings->public_folder + 1, settings->public_folder_length); // copy also the NULL settings->public_folder = tmp; settings->private_metaflags |= 1; settings->public_folder_length = strlen(settings->public_folder); } } } static void http1_on_finish(http_settings_s* settings) { if (settings->private_metaflags & 1) free((void*)settings->public_folder); if (settings->private_metaflags & 2) free(settings); } int http1_listen(const char* port, const char* address, http_settings_s settings) { if (settings.on_request == NULL) { fprintf( stderr, "ERROR: http1_listen requires the .on_request parameter to be set\n"); exit(11); } http_settings_s* settings_copy = malloc(sizeof(*settings_copy)); *settings_copy = settings; settings_copy->private_metaflags = 2; return server_listen(.port = port, .address = address, .on_start = (void*)http1_on_init, .on_finish = (void*)http1_on_finish, .on_open = (void*)http1_alloc, .udata = settings_copy); }